Algorithmique distribuée Réétiquetages de graphes dynamiques 1 TD1 Installation de JBotSim JBotSim est une bibliothèque java pour développer rapidement des algorithmes distribués et les tester de manière interactive. Commençons par télécharger la dernière version sur jbotsim.sf.net. Il s’agit d’un fichier .jar. Il est fortement recommandé d’utiliser un IDE comme IntelliJ ou Eclipse pour ce TP, avec une préférence pour IntelliJ. Dans l’IDE, créez un nouveau projet et ajoutez le jar aux dépendances comme suit : — IntelliJ : Project structure > Modules (select your module) > Dependencies > "+" > fichier — Eclipse : Project properties > Java Build Path > Librairies > external JAR > fichier Puis créez une classe HelloWorld avec le contenu suivant executez-là pour vérifier que tout fonctionne. Vous devriez voir apparaître une surface grise sur laquelle vous pouvez cliquer pour ajouter des sommets. Si ça ne fonctionne pas appelez-moi. public static void main(String args[]){ Topology tp = new Topology(); new JViewer(tp); } 2 Notions de bases Un exemple d’algorithme de réétiquetage très simple est donné par l’Algorithme 1. Ici les sommets sont initialement non-informés (étiquette N ), à l’exception d’un sommet qui est déjà informé (étiquette I). La règle de réétiquetage se lit comme : Lorsqu’un sommet informé interagit avec un sommet non-informé, il l’informe. Le nouveau sommet informé pourra à son tour informer d’autres sommets par cette même règle. Algorithm 1 Diffusion d’une information dans le réseau Etats initiaux : I pour le sommet sélectionné ; N pour tous les autres. Règle de réétiquetage : I N I I Le premier objectif de ce TD est de créer un ordonnanceur pour ce type d’interaction. Son rôle est de choisir (en l’occurrence, aléatoirement) les arêtes sur lesquelles l’algorithme tente d’appliquer une règle. Ainsi, dans ce modèle, l’algorithme ne contrôle pas l’ordre des interactions. Il fait ce qu’il peut avec les choix de l’ordonnanceur. Durant cette séance, nous allons : 1. Créer un ordonnanceur aléatoire. 2. 3. 4. 5. 3 Implémenter l’algorithme de diffusion présenté à l’Algorithme 1 Implémenter un algorithme de comptage centralisé Implémenter deux algorithmes de comptage décentralisé Implémenter un algorithme de maintien d’arbres couvrants Création de l’ordonnanceur Même si les réétiquetages de graphes représentent des algorithmes distribués, notre implémentation sera, elle, centralisée : l’ordonnanceur choisit une arête au hasard et la passe à l’algorithme pour y exécuter une interaction. Puis il recommence sur une autre arête. Pour commencer nous allons compléter le squelette d’ordonnanceur disponible sur la page du site (Scheduler.java). Il vous sera utile de savoir qu’il existe une méthode getLinks(). Si vous voyez les arêtes clignoter une à la fois, vous pouvez passer à l’exercice suivant. 4 Une classe abstraite pour les algorithmes Récupérez la classe abstraite Algorithm sur la page du cours. Chaque algorithme que vous implémenterez héritera de cette classe. En particulier, elle a trois méthodes abstraites que vous devrez surcharger. Dans la première (setDefaultState()), vous affecterez au sommet donné l’état initial de l’algorithme (par exemple, N ). Observez que cette méthode est appelée à chaque fois qu’un sommet est ajouté à la topologie (onNodeAdded()), ainsi que lorsque vous redémarrez l’exécution (onStart()). Dans la seconde méthode (setDistinguishedState()), vous affecterez au sommet donné l’état distingué de l’algorithme (s’il y en a un, par exemple I). Observez que cette méthode est appelée lorsqu’un sommet est sélectionné par l’utilisateur (onSelection()). Prenez le temps de bien comprendre tous ces enchaînements. 5 Interactions entre ordonnanceur et algorithme 1. Modifiez le constructeur de l’ordonnanceur de sorte à prendre un objet de type Algorithm en second paramètre. L’ordonnanceur devra appeler la troisième méthode abstraite applyRule() de cet algorithme lorsqu’une arête est sélectionnée. 2. Implémentez maintenant l’Algorithme 1 dans une classe Diffusion, en surchargeant les trois méthodes de façon appropriée. Concernant le constructeur, il suffira d’appeler super() avec la topologie en paramètre. Il est recommandé d’utiliser des couleurs à la place des états I et N pour visualiser le résultat plus facilement. Vous pouvez récupérer les sommets extremités d’une arête en utilisant link.endpoint(0) et link.endpoint(1). Attention, une règle peut être applicable dans un sens ou dans l’autre (endpoint 0 à gauche ou endpoint 1 à gauche), il faudra donc tester les deux possibilités si pertinent. 3. Testez votre algorithme en jouant avec la souris et en sélectionnant un sommet distingué (clic du milieu). 6 Algorithmes de comptage On s’intéresse maintenant au problème du comptage, où l’un des sommets doit apprendre combien de participants se trouvent dans le réseau. On considère d’abord un algorithme très limité où Page 2 le sommet compteur est désigné initialement (état distingué (C, 1)). Le compteur ne compte que les sommets avec lesquels il interagit directement (c.f. Algorithme 2). Algorithm 2 Comptage du nombre de sommets (avec compteur sélectionné) Etats initiaux : C,1 pour le sommet compteur (sélectionné à la souris) ; N pour tous les autres. Règle de réétiquetage : C, i N C, i + 1 F → Implémentez cet algorithme. Astuce : utilisez trois couleur : une pour le compteur (C), une pour les sommets non-comptés (N ), une pour les sommets déjà comptés (F ). Vous pouvez aussi bricoler avec setID() et getID() sur le sommet compteur pour encoder l’entier i dans son identifiant (de sorte à ce qu’il s’affiche quand la souris passe dessus). → Quelle propriété doit avoir la topologie pour que l’Algorithme 2 finisse par compter tout le monde ? Vous répondrez pour les deux cas où 1) on peut choisir le compteur et 2) c’est un ennemi qui choisit le compteur (pire cas). On s’intéresse maintenant à un second algorithme où tous les sommets démarrent dans le même état (initialisation uniforme). Algorithm 3 Comptage du nombre de sommets (version uniforme avec compteurs fusionnants) Etats initiaux : 1 pour tous les sommets. Règle de réétiquetage : i 6= 0 j 6= 0 i+j 0 → Quelle propriété doit avoir la topologie pour que l’Algorithme 3 finisse par compter tout le monde ? Vous répondrez pour les deux cas où 1) le compteur qui survit après chaque interaction est celui qui nous arrange et 2) c’est un adversaire qui choisit le compteur qui survit. Algorithm 4 Comptage du nombre de sommets (avec compteurs fusionnants et circulants) Etats initiaux : 1 pour tous les sommets. Règle de fusion : i 6= 0 j 6= 0 Règle de circulation : i 6= 0 i+j 0 0 0 i → Quelle propriété doit avoir la topologie pour que l’Algorithme 4 parvienne à compter tout le monde ? 7 Forêt couvrante pour réseaux dynamiques Nous considérons maintenant l’exemple d’un algorithme capable de « réagir » à la disparition des arêtes. L’algorithme consiste à maintenir une forêt d’arbres couvrants. L’objectif est de rester cohérent (i.e. pas de cycle et une seule racine par arbre) quitte à mettre plus longtemps à converger vers un état optimal (un arbre par composante connexe). Cet algorithme est constitué de trois règles de réétiquetage. Les deux premières sont classiques : 1. une règle de fusion : si deux racines d’arbres différents se retrouvent en vis à vis, l’une des deux est supprimée et les deux arbres sont fusionnés en mettant en gras l’arête correspondante (désactiver le setWidth() de l’ordonnanceur pour ne pas confondre). De plus, le sommet “perdant” mémorise l’autre comme parent (représenté ci-dessous par une orientation). Page 3 2. une règle de circulation, consistant à faire circuler une racine au sein de son propre arbre, en inversant la relation parent/enfant locale à chaque mouvement. La troisième règle est différente. Elle donne le traitement à effectuer immédiatement après qu’une arête a disparu. Le traitement consiste à régénérer une racine si l’arête menait vers son parent. Algorithm 5 Maintien d’une forêt d’arbres couvrants Etats initiaux : R pour tous les sommets. Règle de fusion : R R Règle de circulation : R Règle de réparation : N R N × N N R R La détection des cassures se fera en utilisant l’interface ConnectivityListener. Concrètement, l’algorithme demandera à être notifié lorsque des arêtes apparaissent ou disparaissent en invoquant addConnectivityListener dans setTopology() (à surcharger, donc). Cela vous permettra d’avoir une méthode onLinkRemoved() dans laquelle vous ferez le traitement nécessaire. Ce traitement consistera à tester si l’arête perdue est une arête de l’arbre (p.ex. est-elle épaisse ?) et si oui, régénérer une racine sur celui des deux qui a perdu son parent. Une vidéo du résultat à obtenir est disponible sur YouTube (chaîne JBotSim). Page 4