3. Algorithmes de recherche dans les systèmes à agents
3.3. Le problème de la satisfaction des contraintes (PSC)

3.3.2. Retour en arrière asynchrone (RAA)

Pour présenter cette méthode on va considérer une classe de PSC où toutes les contraintes sont binaires ( Pk(xi,xj) ). Un tel problème se peut représenter par un graphe où les variables constituent des nœuds xi  et les contraintes Pk(xi,xj) sont des arcs orientés entre les nœuds xi et xj. Le graphe abstrait qui représente ce type de PSC est indépendant du réseau physique de communication entre les agents.

Exemple  Soit trois variables x1, x2, x3 qui peuvent prendre des valeurs dans des domaines booléens et les contraintes

P1(x1,x3) = x1  Ú   x3
P2(x2,x3) = x2 Ú  ~ x3

Chaque variable est assignée à un seul agent (voir figure 3.3.1), donc un nœud va représenter aussi un seul agent.  La valeur choisie par un agent xi  pour la variable propre doit être validée par les nœuds évaluateurs xj qui vérifient la satisfaction des contraintes Pk(xi,xj). Dans le cas où deux agents xi et xj feraient partie d`une même contrainte Pk(xi,xj),  ils seront liés par un arc (xi,xj) qui sera orienté de l`agent qui envoie la valeur de sa variable propre, vers l`agent qui va évaluer la contrainte.

Figure 3.3.1. Le graphe pour un problème à 3 agents avec 2 contraintes

Le fonctionnement de ce réseau des contraintes est basé sur l`échange de messages entre les agents. Chaque agent donne une valeur pour sa variable propre et émet ensuite un message contenant cette valeur vers tous les agents auxquels il est lié par des arcs ayant comme origine son nœud. Ensuite il attend des messages et répond aux messages reçus. Il existe deux types principaux de messages:

  • “correcte_?” – envoyé par un agent qui demande à l`évaluateur de la contrainte de vérifier si la valeur choisie pour sa variable propre est correcte,
  • ”incorrect”– la réponse de l`agent évaluateur à l`agent qui a transmis une valeur qui ne respecte pas la contrainte vérifiée par l`évaluateur.

Chaque agent i dispose d`une mémoire propre dans laquelle on garde les informations suivantes :

  • la valeur courante de la variable propre x;
  • la liste état_des_voisins - contient les valeurs des variables correspondant aux agents voisins (représentés par les nœuds origines des arcs qui arrivent dans le nœud courant). Les éléments de cette liste sont des couples (xj, dj), avec dj la valeur propre de l`agent xj ; on a un couple (xj, dj) pour chaque arc (xj, xi), où xi désigne le nœud courant ;
  • la liste des valeurs refusées (valeurs_refusées) - contient les valeurs de sa variable propre indiquées comme erronées par les nœuds évaluateurs de contraintes, avec qui le nœud courant est lié ; cette liste est actualisée à la réception d`un message de type incorrect; les éléments de la liste sont aussi des couples (xj, dj), où xj fait partie d`un arc (xi , xj), où xi désigne le nœud courant ;
  • les adresses des nœuds voisins qui lui communiquent des valeurs pour être évaluées (ces adresses sont nécessaires pour pouvoir leur répondre)
  • les adresses des nœuds évaluateurs de contraintes auxquels il est lié à son tour (pour leur communiquer la valeur courante de la variable propre et leur demander leur "avis").

La liste état_des_voisins est actualisée à l`arrivée des messages de type “correcte_?”, mais à cause des délais dans la transmission des messages, elle ne représente pas toujours une image synchrone de l`état respectif des agents.

à l`arrivée d`un message de type “correcte_?” l`agent actualise la liste état_des_voisins et vérifie si la valeur reçue et sa valeur courante de la variable propre satisfont toutes les contraintes que l`agent connaît.

Chaque agent va considérer que la valeur assignée à sa variable propre est consistante avec toutes les valeurs qui se trouvent dans la liste état_des_voisins quand les deux conditions suivantes seront vérifiées :

  1. toutes les contraintes évaluées par l`agent et qui utilisent des valeurs qui se trouvent dans la liste état_des_voisins et sa valeur courante sont satisfaites
  2. toutes les valeurs de la liste valeurs _refusées sont incompatibles avec toutes les valeurs qui se trouvent dans la liste état_des_voisins et avec la valeur courante.

Si la valeur courante n`est pas compatible avec état_des_voisins, l`agent va essayer de la changer avec une autre valeur, qui à ce moment là est consistante. Cette nouvelle valeur sera envoyée pour évaluation aux nœuds évaluateurs auxquels le nœud courant est lié.

Eviter le traitement en cycle infini

Si dans le graphe des contraintes il existe un circuit x1, x2, ... , xk,, xalors il est possible qu`un changement de la valeur de x1 provoque une modification de la valeur de x2  et ensuite de x3 etc, ce qui peut se répercuter par un nouveau changement de valeur de la variable x1 , suite au circuit des nœuds évaluateurs x1, x2, ... , xk,, x1.

Pour éviter ce cycle, on peut assigner des priorités aux agents. Par exemple, pour les valeurs des priorités on peut prendre en considération dans l`ordre lexicographique, des symboles des variables propres : si la variable propre de l`agent A a un symbole qui précède en ordre lexicographique le symbole de la variable propre de l`agent B, alors la priorité de l`agent A est plus grande que celle l`agent B.

Dans le cas de l`algorithme RAA les priorités sont fixes et chaque contrainte Pk(xi,xj) sera représentée par un arc qui part du nœud le plus prioritaire vers le moins prioritaire.

Exemple.  Soit un problème à 3 variables (agents) et 2 contraintes (liens) modélisé par le graphe de la figure 3.3.1. On remarque dans cette figure que

  • la contrainte P1(x1,x3) = x1  Ú   x3 est représentée par un arc orienté du nœud x1 vers le nœud x3
  • la contrainte P2(x2,x3) = x2 Ú  ~ x3 sera représentée par l`arc orienté (x2 , x3)

Suite à ce système des priorités, tous les messages " correcte_?" sont toujours transmis par le nœud le plus prioritaire vers le nœud le moins prioritaire (le nœud évaluateur) et les messages incorrects sont transmis vers le nœud le moins prioritaire existant dans la liste valeurs_refusées. En appliquant ce procédé, il n'est pas possible d'avoir un circuit des nœuds évaluateurs x1, x2, ... , xk,, x1 (le lien (xk,, x1) ne peut pas exister dans le graphe).

Question. Considérez 4 variables : a, b, c1, b2. et les contraintes P1(a,b), P2(b2,b), P3(c1,b2), P4(c1,a). Quels sont les liens représentant ces contraintes dans le graphe des agents ?

Cliquez ici pour voir la réponse

Eviter les inconsistances dues au traitement asynchrone

Il est possible qu'un agent reçoive un message de type incorrect pour une valeur communiquée antérieurement au nœud évaluateur, valeur qui entre temps a été corrigée suite à l'évaluation des autres contraintes. Le message incorrect arrivé pourrait conduire à un nouveau changement de valeur du nœud courant, ce qui peut engendrer des inconsistances. Pour éviter ceci, on va préciser aussi dans le cadre des messages de type incorrect la valeur erronée (le contexte) qui a déclenché l'envoi du message. Ainsi, l'agent qui reçoit un message de type incorrect va vérifier si la valeur transmise dans le message est compatible avec la valeur courante et la liste état_des_voisins et seulement dans un tel cas, l'agent va modifier sa valeur.

Remarque. La liste des valeurs refusées réceptionnées par les nœuds évaluateurs des contraintes se constitue en fait comme une liste des contraintes supplémentaires, ce qui évite la répétition des mêmes erreurs.

Le traitement des messages peut être décrit par une série de procédures associées à certains événements, des procédures représentées par le pseudo code suivant.

Procédure Réception_correcte_?_ (xj, dj) est

// La procédure est appelée à l`arrivée d`un message
// de type “correcte_?” émis par le nœud xj qui transmet ainsi
// sa nouvelle valeur dj au nœud courant (xi) qui a le rôle 
// d`évaluateur de contrainte
//

Insert (état_des_voisins, (xj, dj) )
Vérifie_état_des_voisins()

Fin

Procédure Réception_incorrect_ (xj , E) est

// La procédure est appelée à l`arrivée d`un message
// de type incorrect émis par le nœud xj qui a un rôle
// d`évaluateur de contrainte pour le nœud courant (xi)
// E = {(xk, dk)/ dk est la valeur incorrecte pour le nœud xk}
//

     pour *) chacune (xk, dk) dans E faire
     
Insert ( valeurs_refusées, (xk, dk) )
     si *) le nœud (xk) n`est pas lié au nœud courant (xi) alors
          
*) demande au nœud (xk) d`établir un nouveau lien (xk, xi)
          Insert (état_des_voisins, (xk, dk) )
     fin si
fin pour

     vieille_valeur = valeur_courante
     Vérifie_état_des_voisins()

     si vieille _valeur = valeur_courante alors
          
Envoyer (“correcte_?”, xj, (xi , valeur_courante))
     fin si
Fin

 

Procédure Vérifie_état_des_voisins() est

     si *)la valeur_courante n`est pas consistante avec état_des_voisins alors
          
si *)aucune valeur de Di n`est consistante avec état_des_voisins alors
          
     RetourEnArrière()
          sinon
               
d = Choix_valeur_consistante (Di , état_des_voisins)
               valeur_courante = d
               pour *) tous les nœuds évaluateurs xj Î (xi, xj) faire
                    
Envoyer (“correcte_?”, xj, (xi , valeur_courante))
               fin pour
     
     fin si
     
fin si
Fin

Procédure RetourEnArrière() est

     F = { A / A un sous-ensemble inconsistant de la liste  état_des_voisins }

     si F = f alors
          *)transmet aux autres agents qu`il n`existe pas de solution
          *) termine l`algorithme
     fin si
     pour
*) chacune A dans F faire
          
*) sélecte (xj, dj), où (xj) a la plus petite priorité dans A
          Envoyer (incorrect, xj, (xi , A))
          Effacer (état_des_voisins, (xj, dj))
     fin pour
     
Vérifie_état_des_voisins()
Fin

Exemple  Soit trois variables a, b, c booléennes (les domaines représentés par l`ensemble {0,1} ) et les contraintes : R1= a Ú c  et  R2= b Ú ~c

Figure 3.3.2. Agents, variables, domaines et contraintes

Le graphe des contraintes est présenté dans la figure 3.3.2. On va supposer que l`ordre chronologique des événements et celui décrit plus loin, mais ce système à 3 agents pourra bien avoir un autre ordre des événements, suite au parallélisme des traitements et aux délais de communication.

1.      les nœuds a et b

Figure 3.3.3. Les agents a et b envoient leurs essais des valeurs
pour leurs variables propres à l`agent l`évaluateur

          envoient vers le nœud c  les messages  (“correcte_?”, c, (a ,0)) et respectivement  (“correcte_?”, c, (b ,0))

2.      dans le nœud c

  • arrive le message (correcte_?, c, (a, 0)) transmis par le nœud a;
  • on insère dans la propre liste état_des_voisins l`élément (a,0), mais on ne peut pas déterminer une valeur courante consistante, parce qu`on ne peut pas évaluer R2, la valeur de la variable b ne se trouvant pas dans la liste état_des_voisins
  • arrive le message (correcte_?, c, (b,0)) envoyé par le nœud b;
    • après sa réception, on insère (b,0) dans la liste état_des_voisins  qui devient {(a,0), (b,0)};
    • aucune valeur de c ne peut satisfaire les deux contraintes ; on exécute donc la procédure RetourEnArrière() qui copie les éléments de la liste état_des_voisins dans l`ensemble F
    • envoie un message de type incorrect (figure 3.3.4)  vers le nœud le moins prioritaire de tous les nœuds qui sont en F, par l`appelle Envoyer (incorrect, b, (c , {(a,0), (b,0)}))

     

Figure 3.3.4. Le nœud évaluateur transmet les valeurs inacceptables

3. le nœud b

  • reçoit le message de type incorrect transmis par c et insère les éléments (a,0), (b,0) dans sa propre liste valeurs_refusées;
    • parce que le nœud a n'est pas lié à b, le nœud b émet vers a une demande d'établir un nouveau lien entre a et b (figure 3.3.5.a) et insère dans sa propre liste état_des_voisins l'élément (a ,0)
    • exécute Vérifie_état_des_voisins(); la valeur courante (0) n`est pas consistante parce qu'elle se trouve dans la liste des valeurs refusées ; on fixe la valeur courante à 1 et puis on envoie le message  (“correcte_?”, c, (b ,1)) (figure 3.3.5.c)

(a)

(b)

(c)

Figure 3.3.5. Actions effectuées par l'agent b après la réception
du message 'incorrecte' transmis par l'agent c

4. le nœud a

  • établit le lien demandé par le nœud b et transmet vers b le message ("correcte_?", b, (a ,0))

5. le nœud b

  • reçoit ce message
  • parce qu'on constate que la liste état_des_voisins est compatible avec la liste valeurs_refusées ,(l'élément (a ,0) se trouve dans les deux listes) exécute Envoyer (incorrect,a,(b,{(a,0)}) (figure 3.3.5.b)

6. le nœud a

  • reçoit le message (incorrect,(b,{(a,0)}) transmis par b
    • modifie sa valeur à 1 et communique cette modification en envoyant des messages de type correcte_? vers les nœuds successeurs b et c (figure 3.3.6)

7. le nœud c

    • reçoit le message ("correcte_?", c, (b ,1)) transmis par le nœud b, actualise sa liste état_des_voisins à {(a,0),(b,1)} et trouve c=1 comme une valeur consistante


Figure 3.3.6. L'agent a communique sa nouvelle valeur courante

8. les nœuds c et b

  • en chaque nœud arrive un message de type "correcte_?" transmis par le nœud a qui communique ainsi sa nouvelle valeur aux nœuds c et b,
  • ce message sert seulement à la mise à jour des listes état_des_voisins de chaque nœud

À ce stade aucun message n'est plus transmis et l'algorithme se termine.

<< Section précédente Table de matières Section suivante >>

Politechnica University of Bucharest - 2002