Changes between Initial Version and Version 1 of 2011CaoTme7


Ignore:
Timestamp:
Apr 7, 2011, 11:33:18 PM (14 years ago)
Author:
jpc
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • 2011CaoTme7

    v1 v1  
     1{{{
     2#!html
     3<h1>TME 6 : Représentation des fonctions Boolèennes : ROBDD</h1>
     4}}}
     5[[PageOutline]]
     6
     7= Objectif =
     8
     9L'objectif  principal de  ce  TME est  de  vous familiariser  avec  la représentation  des
     10fonctions  Booléennes sous forme  de ROBDD.   Les ROBDD  (Reduced Ordered  Binary Decision
     11Diagram)  sont utilisés  pour représenter  de façon  compacte une  ou  plusieurs fonctions
     12Booléennes,  partageant le  même support  (c’est à  dire dépendant  d’un même  ensemble de
     13variables Booléennes).   Pour un ordre donné  des variables constituant  le support, cette
     14représentation est  canonique : a chaque  fonction Booléenne est associé  un unique graphe
     15orienté  acyclique  (DAG).   Le  graphe  étant  acyclique, chaque  noeud  BDD  définit  un
     16sous-graphe dont  il est  la racine.  Par  conséquent, chaque  noeud BDD correspond  à une
     17fonction  Booléenne  particulière.  On  utilise  le  fait  que  les  variables  Booléennes
     18constituant le support sont ordonnées pour identifier ces variables par leur index.
     19
     20Des squelettes  de démarrage vous sont fournis  pour un certain nombre  de fichiers, ainsi
     21qu'une librairie {{{ebm}}} ({{{libebm.a}}} précompilée.
     22
     23[[Image(BDD-1.png)]]
     24
     25
     26= A) Structures de données et fonctions de base =
     27
     28La structure  C++ permettant de  représenter un  noeud du graphe  ROBDD est définie  de la
     29façon suivante :
     30
     31{{{
     32#!cpp
     33class Bdd {
     34  public:
     35    class Key {
     36      private:
     37               unsigned int _index;
     38               unsigned int _highIdent;
     39               unsigned int _lowIdent;
     40      public:
     41        inline              Key       ( unsigned int index, unsigned int high, unsigned int low );
     42        friend bool         operator< ( const Key& lhs, const Key& rhs );
     43    };
     44  private:
     45    static unsigned int       _maxIdent;
     46    static unsigned int       _maxStamp;
     47    static std::map<Key,Bdd*> _bdds;
     48  private:
     49           unsigned int       _ident;    // Unicity identifier.
     50           unsigned int       _index;    // Decomposition variable index.
     51           Bdd*               _high;     // Low cofactor pointer.
     52           Bdd*               _low;      // High cofactor pointer.
     53           unsigned           _stamp;    // Used to flag already reached nodes in the DAG
     54                                         // recursive walktrhough.
     55  public:                     
     56    static Bdd*               ConstHigh;
     57    static Bdd*               ConstLow;
     58  private:                   
     59                              Bdd          ( unsigned index, Bdd* high, Bdd* low ); 
     60                             ~Bdd          (); 
     61  public :                                 
     62    static Bdd*               create       ( unsigned index, Bdd* high, Bdd* low );
     63    static Bdd*               apply        ( OperatorType, Bdd*, Bdd* );
     64    static Bdd*               Not          ( Bdd* );
     65    static Bdd*               And          ( Bdd*, Bdd* );
     66    static Bdd*               And          ( Bdd*, Bdd*, Bdd* );
     67    static Bdd*               And          ( Bdd*, Bdd*, Bdd*, Bdd* );
     68    static Bdd*               Xor          ( Bdd*, Bdd* );
     69    static Bdd*               Xor          ( Bdd*, Bdd*, Bdd* );
     70    static Bdd*               Xor          ( Bdd*, Bdd*, Bdd*, Bdd* );
     71    static Bdd*               Or           ( Bdd*, Bdd* );
     72    static Bdd*               Or           ( Bdd*, Bdd*, Bdd* );
     73    static Bdd*               Or           ( Bdd*, Bdd*, Bdd*, Bdd* );
     74  public:                                 
     75    inline unsigned           getIdent     ();
     76    inline unsigned           getIndex     ();
     77    inline Bdd*               getHigh      ();
     78    inline Bdd*               getLow       ();
     79           Bdd*               neg          ();
     80           unsigned int       depth        ();
     81           Bdd*               satisfy      ();
     82           void               display      ( std::ostream& );
     83           ValueType          eval         ();
     84           Ebm*               toEbm        ();
     85    static Bdd*               fromEbm      ( Ebm* );
     86  private:                   
     87    inline Key                _getKey      ();
     88           void               _display     ( std::ostream& );
     89    friend std::ostream&      operator<<   ( std::ostream&, const Bdd* );
     90};
     91}}}
     92
     93 * Le constructeur  {{{Bdd::Bdd()}}}, alloue un nouveau  noeud BDD et  l'ajoute dans le
     94   dictionnaire des noeuds BDD.
     95
     96 * La méthode '''Bdd::create()'''  gère un dictionnaire de tous les  noeuds BDD déjà créés
     97   et fournit  la garantie  qu’il n’existera  jamais deux noeuds  BDD possédant  les mêmes
     98   valeurs  pour  les  attributs  {{{_index}}},  {{{_high}}}  et  {{{_low}}}.  La  méthode
     99   Bdd::create() recherche le noeud BDD  possédant les valeurs définies par les arguments,
     100   et renvoie un pointeur  sur le noeud BDD correspondant. Si ce  noeud n’existe pas, elle
     101   le crée en appelant le constructeur de BDD.
     102
     103 * La méthode '''Bdd::apply()'''  prend pour arguments un entier  définissant un opérateur
     104   Booléen (les  valeurs possibles  sont OR,  AND, et XOR),  deux pointeurs  {{{bdd1}}} et
     105   {{{bdd2}}} sur deux noeuds BDD représentant  deux fonctions Booléennes F1 et F2 (notons
     106   que les  deux fonctions F1  et F2  ne dépendent pas  forcément de toutes  les variables
     107   Booléennes définies dans le support global). La méthode {{{Bdd::apply()}}} construit le
     108   graphe ROBDD représentant la fonction {{{F =  F1 oper F2}}}, et renvoie un pointeur sur
     109   le noeud ROBDD racine de ce graphe.
     110
     111 * La  méthode '''Bdd::neg()'''  renvoie  un pointeur  sur  un noeud  BDD représentant  la
     112   négation du noeud courant {{{NOT(bdd)}}}.
     113
     114 * La fonction '''Bdd::display()''' affichera sur la sortie standard le graphe de noeuds
     115   BDD ayant le noeud courant pour racine. L'affichage sera récursif et indenté:
     116{{{
     117Bdd( [10] idx:4 high:8 low:7 ) - x1
     118  High of [10] x1
     119    [...]
     120  Low of [10] x1
     121    [...]
     122}}}
     123   Ou {{{[X]}}},  {{{high:Y}}} et {{{low:Z}}}  sont respectivement les  identificateurs du
     124   neud courant (X),  du BDD high cofacteur  (Y) et du BDD low  cofacteur (Z). {{{idx:I}}}
     125   est  l'index de  la variable  suivant laquelle  on décompose  et {{{x1}}}  son  nom. Un
     126   exemple complet donnera:
     127{{{
     128Bdd( [10] idx:4 high:8 low:7 ) - x1
     129  High of [10] x1
     130    Bdd( [8] idx:3 high:1 low:2 ) - x2
     131      High of [8] x2
     132        Bdd( One )
     133      Low of [8] x2
     134        Bdd( [2] idx:2 high:1 low:0 ) - x3
     135          High of [2] x3
     136            Bdd( One )
     137          Low of [2] x3
     138            Bdd( Zero )
     139  Low of [10] x1
     140    Bdd( [7] idx:3 high:2 low:0 ) - x2
     141      High of [7] x2
     142        Bdd( [2] idx:2 high:1 low:0 ), already displayed.
     143      Low of [7] x2
     144        Bdd( Zero )
     145
     146}}}
     147
     148Pour  la modélisation  des variables,  vous réutiliserez  l'objet {{{EbmVar}}}  vu  au TME
     149précédent.  Pour ne  pas bloquer  sur des  erreurs que  contiendrais  éventuellement votre
     150implantation,  des ''headers'' {{{Ebm.h}}},  {{{EbmVar.h}}}, {{{EbmExpr.h}}}  ainsi qu'une
     151librairie compilée {{{libebm.a}}} vous sont fournis
     152
     153
     154= B) Techniques de Programmation =
     155
     156== B.1) Constructeur pré-conditionné ==
     157
     158Avant de créer un nouveau BDD, il est nécessaire de vérifier s'il n'existe pas déja.  Cela
     159ne peut pas être fait ''dans'' le constructeur puisque si nous sommes dans cette fonction,
     160c'est que l'objet est ''déjà'' en cours de construction.
     161
     162Pour résoudre ce problème on adopte l'architecture suivante:
     163
     164 * Le constructeur  est rendu  ''privé''. Ce  qui inderdit son  utilisation, sauf  par des
     165   fonctions membre de la classe.
     166
     167 * On ajoute  une fonction  membre statique  {{{Bdd::create()}}} qui va  jouer le  rôle du
     168   constructeur.  Comme elle  est  membre  de la  classe,  elle a  le  droit d'appeler  le
     169   constructeur. Et comme elle  est {{{static}}} elle n'est pas attachée à  un objet de la
     170   classe. Dans cette fonction on peut donc effectuer les vérifications (est-ce que le BDD
     171   existe déjà?) puis créer ou non un nouvel objet.
     172
     173
     174== B.2) Dictionnaire de BDD ==
     175
     176
     177Comme  il a  été montré  en cours,  une expression  booléenne sous  la forme  canonique de
     178Shannon est associée à un et un seul ROBDD. Celui-çi étant entièrement caractérisé par son
     179triplet {{{(_index,_high,_low)}}}.  Lorsque la méthode {{{Bdd::create()}}}  est appelée il
     180faut qu'elle puisse déterminer rapidement si un ROBDD existe ou non.
     181
     182Le conteneur  approprié pour ce type  d'opération est la  {{{stl::map<>}}}. La {{{map<>}}}
     183associe  une valeur  (le  pointeur sur  le  ROBDD) à  une  clé (le  triplet).  Elle à  les
     184propriétés suivantes:
     185
     186 * Une clé d'une valeur donnée sera  présente en un unique exemplaire dans la {{{map<>}}},
     187   et sera associée à une unique valeur.
     188
     189 * On peut retrouver très rapidement une valeur en fonction d'une clé.
     190
     191 * N'importe quel  objet (classe) peut  servir de clé  ''sous réserve'' de  disposer d'une
     192   fonction  d'ordre.    C'est  à  dire   de  disposer  d'une  surcharge   de  l'operateur
     193   ''strictement inférieur''.
     194
     195La  définition  de  la classe  {{{Bdd}}}  comporte  donc  une classe  imbriquée  {{{Key}}}
     196représentant  le triplet  et  proposant une  fonction  d'ordre. La  relation d'ordre  sera
     197simplement l'ordre lexicographique du triplet. Soit:
     198
     199{{{
     200#!cpp
     201if ( lhs->_index != rhs->_index ) return lhs->_index < rhs->_index;
     202
     203if ( lhs->_highIndex != rhs->_highIndex )
     204  return lhs->_highIndex < rhs->_highIndex;
     205
     206return lhs->_lowIndex < rhs->_lowIndex;
     207}}}
     208
     209
     210= C)  Représentation Graphique =
     211
     212Soit la fonction Booléenne F(a,b,c,d,e), définie par l’expression suivante
     213
     214E0 = (a’ . (b + c + d’) . e) + c               
     215
     216La notation a’ signifie NOT(a).
     217
     218E0 peut se re-écrire sous forme préfixée de la façon suivante :
     219
     220E0 = OR( AND (NOT (a) OR (b c NOT (d) e)) c)
     221
     222'''C.1''' En  utilisant la décomposition  de Shannon, représentez graphiquement  le graphe
     223ROBDD associé  à la  fonction F(a,b,c,d,e) pour  l’ordre a >  b > c  > d  > e ,  puis pour
     224l’ordre e > d > c > b > a.
     225
     226'''C.2''' Précisez,  pour chaque noeud  du ROBDD ainsi  construit, quelle est  la fonction
     227Booléenne représentée par ce noeud.
     228
     229
     230= D) Méthodes Bdd::neg() et Bdd::apply() =
     231
     232Les  méthodes {{{Bdd::apply()}}}  et {{{bdd::neg()}}}  ont été  présentées en  cours. Vous
     233trouverez le code de ces fonctions dans le fichier {{{Bdd.cpp}}}.
     234
     235'''D.1''' Soient F1 et  F2 deux fonctions Booléennes, et les fonctions  F1H, F1L, F2H, F2L
     236définies par la décomposition de Shannon suivant la variable x :
     237
     238 * F1 = x . F1H + x’ . F1L
     239 * F2 = x . F2H + x’ . F2L
     240
     241La relation de recurrence (F1 op F2) = x . (F1H op F2H) + x’. (F1L op F2L) a été démontrée
     242en cours dans le cas des opérateurs OR et AND. Démontrez que cette relation est vraie dans
     243le cas  d’un opérateur XOR.  Analysez le  cas général, ainsi  que les cas  particuliers où
     244l’une des deux fonctions F1 ou F2 est égale à une des deux fonctions constantes 0 ou 1.
     245
     246'''D.2'''  Soit la fonction  F, définie  par l’expression  E1 =  a .  (b +  c) Représentez
     247graphiquement le  ROBDD représentant la  fonction F,  pour l’ordre des  variables a >  b >
     248c. On  appelle p0, p1,  p2, p3,  p4 les pointeurs  sur les 5  noeuds BDD contenus  dans ce
     249graphe :
     250
     251 * p0 représente la fonction F0 = 0
     252 * p1 représente la fonction F1 = 1
     253 * p2 représente la fonction F2 = c
     254 * p3 représente la fonction F3 = b + c
     255 * p4 représente la fonction F4 = a . (b + c)
     256
     257Représentez  graphiquement l’arbre  d’appels des  fonctions lorsqu’on  appelle  la méthode
     258bdd::neg() sur  l'objet pointé par p4.  Quel  est le nombre maximum  d’appels de fonctions
     259empilés sur  la pile d’exécution? Combien  de noeuds BDD  vont être créés par  la fonction
     260create_bdd(), si on suppose que la structure de données ne contient initialement que les 5
     261noeuds pointés par p0, p1, p2, p3, et p4 au moment de l’appel de la méthode bdd::neg()?
     262
     263
     264= E) Méthode Bdd::fromEbm() =
     265
     266On  cherche à  écrire la  méthode {{{Bdd::fromEbm()}}},  qui construit  automatiquement le
     267graphe ROBDD  représentant une fonction Booléenne  F, à partir  d'une Expression Booléenne
     268multiniveaux particulière de cette fonction  F.  Cette méthode prend comme unique argument
     269un  pointeur sur  la racine  d’une {{{Ebm}}},  et  renvoie un  pointeur sur  le noeud  BDD
     270représentant la  fonction.  Puisque {{{Ebm}}} ne  contiennent que des  opérateurs OR, AND,
     271XOR et NOT,  et qu’on dispose des fonctions {{{Bdd::apply()}}}  et {{{bdd::neg()}}, il est
     272possible de construire le graphe ROBDD en utilisant uniquement ces deux fonctions.
     273
     274'''E.1''' Décrire en français, l’algorithme  de cette méthode {{{Bdd::fromEbm()}}} dans le
     275cas particulier où tous  les opérandes AND, OR ou XOR présents  dans l’arbre ABL n’ont que
     276deux opérandes. On traite successivement les trois cas suivants:
     277
     278 * Le pointeur p désigne une variable.
     279
     280 * Le pointeur p  désigne une expression Booléenne multi-niveaux,  dont l'opérateur racine
     281   est l'opérateur NOT.
     282
     283 * Le pointeur p  désigne une expression Booléenne multi-niveaux,  dont l'opérateur racine
     284   est l'un des trois opérateurs OR, AND ou XOR.
     285
     286'''E.2''' Ecrire en langage C++ la méthode {{{Bdd::fromEbm()}}} dans le cas particulier de
     287la question précédente.   Pour valider cette fonction, on écrira  un programme main(), qui
     288effectue successivement les opérations suivantes :
     289
     290 * Construction   de  l'{{{Ebm}}}   représentant   l’expression  E1,   avec  la   fonction
     291   {{{Ebm::parse()}}}.
     292
     293 * Affichage de  cette expression Booléenne,  avec la fonction  {{{Ebm::display()}}}, pour
     294   vérifier que l’{{{Ebm}}} est correctement construite.
     295
     296 * Construction  du   graphe  ROBDD   représentant  la  fonction   F,  avec   la  fonction
     297   Bdd::fromEbm().
     298
     299 * Affichage du graphe ROBDD ainsi construit, en utilisant la fonction Bdd::display().
     300
     301'''E.3''' Comment faut-il  modifier la fonction {{{Bdd::fromEbm()}}}, pour  traiter le cas
     302général, où les opérateurs OR, AND et XOR peuvent avoir une arité quelconque?  Modifier le
     303code de la fonction, et validez  ces modifications sur l’exemple de l’expression Booléenne
     304E0, ou sur des expressions encore plus complexes.
     305
     306
     307= F) Méthode Bdd::satisfy() =
     308
     309Soit  une  fonction   Booléenne  quelconque  F(x1,  x2,  x3,  ...   xn),  dépendant  de  n
     310variables. Soit  la fonction Booléenne  G, définie comme  un produit (opérateur  AND) d’un
     311nombre queconque de  variables xi (directes ou complémentées). On dit  que G “satisfait” F
     312si on  a la relation  G => F. (Autrement  dit, si G  = 1, alors  F = 1). Remarquez  que la
     313condition G = 1 impose la valeur de  toutes les variables appartenant au support de G (les
     314variables directes  doivent prendre  la valeur 1,  et les variables  complémentées doivent
     315prendre la valeur 0). Notez que, pour  une fonction F donnée, il existe plusieurs fonction
     316G qui satisfont F...
     317
     318Exemple : F = (a . b) + c
     319
     320On peut avoir {{{G = c}}} ou {{{G = a . b}}} ou encore {{{G = a . b . c’}}}.
     321
     322Dans  la  suite  de  cet  exercice,  on cherche  à  construire  automatiquement  le  ROBDD
     323représentant  une fonction G  satisfaisant une  fronction F  quelconque.  Comme  il existe
     324plusieurs solutions, on choisira la systématiquement la solution qui minimise le nombre de
     325variable  {{{xi}}} dans  le support  de G.   La méthode  Bdd::satisfy() appliqué  au noeud
     326représentant F retourne un pointeur sur le noeud représentant G.
     327
     328'''F.1''' La décomposition  de Shannon de la  fonction F suivant la variable  x d’index le
     329plus élévé  définit les cofacteurs  FH et FL  : F  = x .  FH + x’  .  FL Pour  alléger les
     330notations,   on  note   sat(F)   la   fonction  Booléenne   construite   par  la   méthode
     331{{{Bdd::satisfy()}}}  pour la  fonction F.   Donner la  relation de  récurrence  entre les
     332fonctions sat(F), sat  (FH), et sat(FL).  On étudiera successivement le  cas général où ni
     333FH et FL ne sont constantes, et les quatre cas particuliers où l’une des deux fonctions FH
     334ou FL sont égales à 0 ou 1.
     335
     336'''F.2'''  Ecrire,  en  langage  C++,  le  code de  la  méthode  {{{Bdd::satisfy()}}},  et
     337appliquez-la sur la fonction Booléenne F définie dans la partie, en modifiant le programme
     338main de la fonction précédente.
     339
     340
     341= G) Méthode Bdd::toEbm() =
     342
     343On souhaite pour  finir écrire la méthode Bdd::toEbm(),  qui construit automatiquement une
     344expression  Booléenne  multi-niveaux,  à  partir  d'un  ROBDD  représentant  une  fonction
     345Booléenne.
     346
     347Il existe évidemment  un grand nombre d’expressions Booléennes  équivalentes pour une même
     348fonction  Booléenne.  Dans  un  premier  temps,  nous  nous  contenterons  d’utiliser  des
     349opérateurs OR,  et AND à  deux opérandes,  ainsi que l’opérateur  NOT.  Soit un  noeud BDD
     350représentant une fonction Booléenne F. Soient x la variable associée à ce noeud BDD, FH et
     351FL les fonctions Booléennes associées aux noeuds BDD pointés par les pointeurs HIGH et LOW
     352(cofacteurs de Shannon).
     353
     354'''G.1'''  Quelle expression Booléenne  dépendant de  x, FH  et FL  peut-on associer  à la
     355fonction F dans le  cas général ou FH et FL sont quelconques  ? (aucune des deux fonctions
     356FH ou FL n’est égale à 0 ou à 1)
     357
     358'''G.2''' Comment  cette expression Booléenne  se simplifie-t-elle lorsque l’une  des deux
     359fonctions FH ou FL est égale à 0 ou à 1. Etudier un par un les 4 cas particuliers.
     360
     361'''G.3''' En utilisant les résultats des questions précédentes, proposez un algorithme.