Algorithme de détection de C3 et C4


Principe général de l'algorithme

Il s'agit d'implémenter l'algorithme vu en cours et disponible sur les notes de cours sur la page de cours. On rappelle qu'il comporte 3 phases. Les deux premières sont des rondes classiques alors que la troisième correspond à l'envoi d'un ensemble d'identifiants.

Ce que vous devez faire

Vous devez créer un projet avec une class Main et une class DetecC34 qui étend la class Node, l'algorithme distribué à proprement parler (voir les deux aperçus de code ci-après). C'est surtout le code de DetectC34 que vous allez devoir éditer. À la fin de l'algorithme, il faudra visualiser en coloriant différemment les sommets lors des différentes sorties possibles, à minima ceux avec leur variable detect=true et ceux avec detect=false. Mais il sera intéressant de distinguer les autres cas: sommets qui sont bleus (variable b=true) ou rouges (b=false) à la fin de la ronde 1, sommet qui détecte un C4, un C3, ou de manière indéterminée un C3 ou un C4. Il faudra bien sûr tester votre algorithme sur plusieurs topologies. Pour ce faire, vous pourrez: (1) commenter les trois lignes du Main après T.setDefaultNodeModel(), (2) éditer un graphe particulier (avec des parties plus ou moins denses), (3) le sauvegarder dans un fichier pour pouvoir le charger avec T.getFileManager().read("...").

Quelques points de vigilance

  • L'algorithme doit fonctionner selon le modèle CONGEST, c'est-à-dire en mode synchrone où chaque message est de taille limité. Typiquement, chaque message devrait comporter au plus un identifiant ou une couleur, mais surtout pas une liste de messages. (Dans le modèle LOCAL, on aurait par contre le droit de le faire.) La troisième phase devra donc être composée de plusieurs rondes (autant de rondes que la taille de l'ensemble à transmettre). Vous pourrez détecter la fin de transmission de l'ensemble complet avec un message de fin spécial ou en communiquant d'abord la longueur de la liste.
  • N'ouliez pas que l'on programme des algorithmes distribués, et donc que pour lire une information détenue d'un voisin v un sommet u doit recevoir un message émis par v. Par exemple, le sommet u n'a pas le droit d'accéder à son voisin v via getNeighbors(), puis d'accéder à v.getID(). Dit autrement, la méthode getNeighbors() est interdite. Pour calculer son degré, un sommet u devra recevoir des messages de tous ces voisins et les compter.
  • Le simulateur, fonctionne par évènements. Pour le modèle LOCAL ou CONGEST, ce sont les évènements onStart() et onClock() qui nous intéressent. Il ne faut pas en utiliser d'autres, notamment OnMessage() qui permet de gérer les algorithmes asynchrones, ce qui n'est pas le cas des modèles LOCAL et CONGEST.
  • Avec le simulateur, l'algorithme ne s'arrête pas tout seul. Cela vient du fait que l'horloge synchrone, et donc l'évènement onClock(), est éternellement appelé. Il convient donc dans cette méthode de prévoir une variable, disons status, permettant d'indiquer de ne plus rien faire lorsque le sommet a terminer l'algorithme. Cette variable peut également servir à gérer les différentes phases de l'algorithme.
  • Pour visualiser les différentes phases et le débuggage, pensez à modifier la couleur des sommets. Il est aussi possible de faire des sorties sur la console avec System.out.println(), mais tous les sommets le feront. Pensez alors à afficher leur ID et le numéro de ronde pour distinguer les sorties. Bien sûr un if (getID() == 18 && getTime() == 2) System.out.println("...") est une autre solution possible. L'ID des sommets est disponible en survolant le sommet avec la souris.
  • La convention d'une ronde JBotSim est différente de celle utilisée dans le cours. Les messages récupérés/reçus lors d'un évènement onClock() sont ceux qui ont été envoyés à la ronde précédente. Ainsi, une ronde standard consiste d'abord par lire les messages émis à la ronde précédente (voir ci-dessous l'aide), de les traiter, et de terminer par les envois de messages (pour la ronde suivante). Il convient aussi d'envoyer le premier message dans onStart() qui est appelé une seule fois au démarrage de l'algorithme sans quoi rien ne se passera.

    Pour le dire autrement, si $S_i$, $R_i$ et $C_i$ représentent les opérations d'envoi ($S_i$), de réception ($R_i$) et de calcul ($C_i$) pour la ronde $i$ de l'algorithme distribué $A$, le cours va représenter la sucession des rondes de $A$ selon le découpage: $S_1/R_1/C_1$ - $S_2/R_2/C_2$ - $S_3/R_3/C_3$ - ... alors qu'en JBotSim, c'est plutôt: $S_1$ dans onStart(), puis le découpage $R_1/C_1/S_2$ - $R_2/C_2/S_3$ - $R_3/C_3/S_4$ - ... à chaque évènement onClock(). Notez bien que c'est la même chose puisque dans les deux cas les opérations seront effectuées dans l'ordre: $S_1/R_1/C_1/S_2/R_2/C_2/S_3/R_3/C_3/...$
  • Votre class Main devrait ressembler à ceci:

    import io.jbotsim.core.Topology;
    import io.jbotsim.ui.JTopology;
    import io.jbotsim.ui.JViewer;
    
    public class Main {
    
        public static void main(String[] args) {
    
            Topology T = new Topology(680,540); // fenêtre de w x h pixels
            T.setDefaultNodeModel(DetectC34.class); // algo des sommets = DetectC34
            String s = T.getFileManager().read("grille-2x7"); // lecture du fichier
            T.getSerializer().importFromString(T,s); // construction du graphe
            T.shuffleNodeIds(); // permutation aléatoire des IDs dans [0,n[
    
            DetectC34.n = T.getNodes().size(); // calcule n pour tous les sommets
    
            JTopology J = new JTopology(T);
            new JViewer(J);  // dessine la topologie
            T.start();       // démarre l'algorithme
            T.pause();       // mode pas-à-pas (clic-droit dans l'interface)
        }
    }
    

    Votre class DetectC34 devrait ressembler à ceci:

    import io.jbotsim.core.Node;
    //import io.jbotsim.core.Color;
    
    public class DetectC34 extends Node {
    
        public static int n; // constante commune à tous les sommets
    
        @Override // au départ
        public void onStart() {
            ;;;
            // ne pas oublier d'envoyer un premier message
        }
    
        @Override // à chaque top horloge
        public void onClock() {
            ;;;
        }
    
    }
    

    Aide rapide sur JBotSim:

    // colorie le sommet courant
    setColor(Color.getColorAt(getID())); // colorie selon l'ID
    setColor(Color.red);  // colorie en rouge
    setColor(Color.bleu); // colorie en bleu
    
    // envoie un message
    send(Node, Message);
    
    // envoie son ID à tous ses voisins
    int x = getID();
    sendAll(new Message(x));
    
    // lecture des messages dans onClock()
    for (Message m : getMailbox()) { // pour chaque msg émis à la ronde précédente
      y = (int) m.getContent();      // le contenu du msg m
      Node v = m.getSender();        // l'émetteur du msg m
      ;;;
      }
    
    // donne le numéro courant de la ronde
    getTime(); // = 0,1,2,...
    
    // liste des voisins (ne devrait pas être utilisée)
    getNeighbors();
    getNeighbors()[0]; // 1er voisin de la liste