Calcul de résolvant en logique des propositions Un outil pour la validation de système Eric Delvalet, Philippe Chatalic et Mourad Ykhlef L.R.I, U.A. C.N.R.S Bâtiment 490, Université Paris-Sud, 91405, Orsay Cedex Rapport Numéro 2 du contrat EDF-LRI (référence : I211J7468) Contents 1 Introduction 2 2 Rappels de logique propositionnelle 2.1 Définitions de base . . . . . . . . . . . . . . . . . . . . . . 2.1.1 Sémantique . . . . . . . . . . . . . . . . . . . . . . 2.1.2 Forme normale conjonctive . . . . . . . . . . . . . 2.2 Calcul de satisfiabilité et algorithme de Davis et Putnam . 2.2.1 Méthode des tables de vérité . . . . . . . . . . . . 2.2.2 Arbre sémantique . . . . . . . . . . . . . . . . . . . 2.2.3 Algorithme de Quine . . . . . . . . . . . . . . . . . 2.2.4 Algorithme de Davis et Putnam . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 4 5 5 5 6 6 6 3 Résolvant, définition et méthode de calcul 3.1 Définition . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Application à la modélisation de systèmes complexes 3.3 Méthode de calcul du résolvant . . . . . . . . . . . . 3.4 Illustration sur un exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 8 8 8 9 . . . . . 11 11 11 12 12 13 . . . . . 14 14 16 16 17 17 . . . . . . . . 4 Mise en œuvre de l’algorithme de calcul du résolvant 4.1 Structure de l’algorithme de calcul du résolvant . . . . . 4.2 Structure des données manipulées par l’algorithme . . . 4.2.1 Représentation de l’ensemble des variables . . . . 4.2.2 Représentation des ensembles des clauses . . . . 4.2.3 Représentation d’une clause . . . . . . . . . . . . 5 Codage 5.1 Représentation de l’ensemble des variables 5.2 Représentation des variables . . . . . . . . 5.3 Représentation des ensembles des clauses 5.3.1 Représentation des clauses . . . . 5.3.2 Les procédures de calcul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Résultats 18 7 Conclusions et perspectives 18 1 1 Introduction Cadre du travail : modélisation et validation L’étude et la conception d’un système physique complexe nécessite la construction d’un modèle de ce système, dans un certain formalisme, fournissant les moyens de l’étudier. La construction d’un modèle ne peut être séparée de sa validation. Une technique de validation peut consister à expliciter les contraintes sur certaines données du modèle et à les confronter avec les propriétés physiques que l’on tente de représenter à l’aide de ce modèle. Les méthodes de calcul mises en œuvre pour extraire de telles contraintes dépendent du langage retenu pour la modélisation. Leur complexité est souvent fonction de la richessse du langage utilisé. La logique des propositions comme langage de modélisation Le cadre général de cette étude utilise la logique classique (logique des propositions et logique des prédicats) comme langage de modélisation. Ces logiques décrivent des objets et des propriétés sur ces objets. Dans ce rapport, seule la logique des propositions est utilisée. Contrairement à la logique des prédicats, elle ne permet pas d’exprimer des propriétés génériques sur des objets. Toutefois, il est souvent possible de traduire dans un cadre propositionnel des modélisations exprimées dans la logique des prédicats, notamment lorsque les domaines d’interprétation des variables du modèle correspondent à des ensembles finis, auquel cas, il suffit de dupliquer les propriétés génériques pour tous les objets des domaines concernés. Bien que d’un pouvoir d’expression plus faible, la logique propositionnelle peut s’avérer très intéressante comme langage de modélisation en raison de l’existence de méthodes de calcul performantes pour ce langage. Ainsi, parmi toutes les méthodes visant à démontrer la satisfiabilité d’une formule booléenne, c’est un algorithme relativement simple qui semble à l’heure actuelle donner les meilleurs résultats : l’algorithme de Davis et Putnam[1]. Cette étude s’appuie sur une adaptation de cet algorithme, permettant de calculer ce que l’on appelle le résolvant d’une formule booléenne (modélisant un système) par rapport à un sous-ensemble des variables propositionnelles du système. Ce résolvant, lui même une formule en langage propositionnel, exprime les propriétés des données qu’il contient. Objet de l’étude Les études conduites au centre de recherche EDF-DER ont permis de mettre en évidence l’intérêt de l’algorithme de calcul du résolvant pour extraire les contraintes liant un sous-ensemble des variables utilisées dans la caractérisaton d’un modèle. Cependant, cette méthode n’a jusqu’à présent été utilisée que sur papier, ce qui limitait son application à des exemples de petite taille. Le présent travail consiste à mettre en œuvre un algorithme de calcul de résolvant afin de pouvoir l’utiliser sur des exemples plus conséquents. L’objectif à terme étant de pouvoir traiter des modèles de taille importante (des applications sur des exemples correspondant à plusieurs milliers de clauses sont envisagées) ce travail propose quelques premiers élément de réflexion sur 2 les structures de données à mettre en place et les choix d’implémentation permettant d’obtenir un algorithme de calcul de résolvant efficace. Dans ce rapport nous présentons une première implémentation mettant en œuvre ces structures proposées dans le langage PROLOG (langage logique). Une autre implémentation est actuellement en cours de developpement en CAML (langage fonctionnel) au terme de laquelle nous essayerons d’analyser si un type de langage est plus adapté que l’autre à ce genre de calcul. Plan du rapport Nous ferons d’abord quelques rappels sur la terminologie de base de la logique propositionnelle, ainsi que sur certaines méthodes de calcul de satisfiabilité, dont la méthode de Davis et Putnam. Puis, après avoir formalisé la notion de résolvant, nous décrirons son utilisation dans le domaine de la modélisation ainsi qu’une méthode permettant de le calculer. Nous présenterons ensuite les structures de données nécessaires à la programmation de l’algorithme de calcul d’un résolvant. Enfin nous donnerons quelques détails sur le codage de ce programme. 2 Rappels de logique propositionnelle Dans ce chapitre, nous rappelons la terminologie et quelques notions de base de la logique propositionnelle. Puis nous présentons des méthodes sémantiques de calcul de la satisfiabilité de formules logiques, parmi lesquelles l’algorithme de Davis et Putnam. 2.1 Définitions de base On appelle atomes, variables propositionnelles ou encore symboles propositionnels des énoncés qui gardent leur identité tout au long d’un calcul propositionnel. Ces énoncés sont représentés par les éléments d’un ensemble dénombrable de mots définis sur l’alphabet {a, b, ..., z, 0, 1, ..., 9} et noté Vp . Les connecteurs propositionnels sont les symboles suivants : ∧ la conjonction, ∨ la disjonction , ¬ la négation, → l’implication et ↔ l’équivalence. Définition 2.1 (Formules) L’ensemble des formules bien formées est défini inductivement de la façon suivante : • Les atomes sont des formules; • Si A et B sont des formules alors (A ↔ B), (A → B), (A ∧ B), (A ∨ B), (¬A) et (¬B) sont des formules; Toute formule est obtenue par l’application des règles précédentes un nombre fini de fois. L’ensemble des formules bien formées forme le langage de la logique propositionnelle. Dans la suite, nous dénoterons les formules, par des mots commençant par des lettres majuscules (A, B, Form, ...). En l’absence de parenthésage, les règles de priorité usuelles des connecteurs seront utilisées pour désambiguer les expressions. Définition 2.2 (Littéral) Un littéral est un atome (littéral positif ) ou la négation d’un atome (littéral négatif ). 3 Définition 2.3 (Clause) On appelle clause une disjonction de littéraux (positifs ou négatifs), soit une formule de la forme : (p1 ∨ p2 ∨ ... ∨ pn ) ∨ (¬q1 ∨ ¬q2 ∨ ... ∨ ¬qm ) où les pi et qj sont des symboles propositionnels. 2.1.1 Sémantique La méthode sémantique d’évaluation des formules est une formalisation de la notion intuitive de vérité d’une phrase, en fonction des propositions qui la composent. En calcul propositionnel, ce résultat est obtenu en donnant à chaque atome la valeur de vérité Vrai ou Faux. A chaque connecteur logique est associée une table de vérité qui, en fonction de la valeur de vérité des arguments du connecteur, donne la valeur de vérité de la formule constituée. A Vrai Faux Vrai Faux B Vrai Vrai Faux Faux ¬B Faux Faux Vrai Vrai A↔B Vrai Faux Faux Vrai A→B Vrai Vrai Faux Vrai A∧B Vrai Faux Faux Faux A∨B Vrai Vrai Vrai Faux Table 1: Table de vérité des connecteurs logiques La valeur de vérité d’une formule est ainsi entièrement déterminée par la valeur de chacun de ses arguments. Définition 2.4 (Interprétation) Une interprétation est une application I de l’ensemble des variables propositionnelles Vp dans {V rai, F aux}. Une application I d’un sous ensemble Vp dans {V rai, F aux} est appelée interprétation partielle de V Etant donné une formule A dont tous les symboles propositionnels sont dans Vp et une intertrétation I, l’interprétation [A]I de A dans I est la valeur de vérité calculée récursivement à partir des valeurs de vérité affectées par I à chaque variable propositionnelle et des fonctions booléennes associées à chaque connecteur logique (voir tableau 1). Définition 2.5 (modèle) Une interprétation I d’un ensemble de variables Vp est un modèle d’une formule F si et seulement si [F ]I = V rai. On dit I satisfait F . Une formule vraie dans au moins une interprétation est dite satisfiable. Elle est dite insatisfiable dans le cas contraire. Une formule vraie dans toute interprétation est dite valide. Les formules valides du calcul propositionnel sont appellées des tautologies. De plus, une formule F est valide si et seulement si ¬F est insatisfiable. Inversement, une formule F est insatisfiable si et seulement si ¬F est valide. Définition 2.6 (Conséquence sémantique) Soit A et B deux formules. Si B est vraie dans toute interprétation I satisfaisant A, on dit que B est une conséquence sémantique de A (i.e., ∀I, si [A]I = V rai alors [B]I = V rai). On note A |= B. 4 En particulier, une formule insatisfiable a pour conséquence sémantique toute formule. A est dite équivalente sémantiquement à B, noté A ≡ B, si A |= B et B |= A. Conséquence et équivalence sémantiques jouissent des propriétés suivantes : A |= B si et seulement si A → B est une tautologie. A ≡ B si et seulement si A ↔ B est une tautologie. Ainsi, on peut démontrer que A |= B simplement en démontrant la validité de (A → B), ou de manière équivalente, l’insatisfiabilité de (B ∧ ¬A) 2.1.2 Forme normale conjonctive Toute formule propositionnelle sur un ensemble de variables propositionnelles Vp peut s’écrire sous la forme d’une conjonction de clauses sur Vp qui lui est logiquement équivalente. La formule est alors dite sous forme normale conjonctive, ou forme clausale. Dans la suite, nous assimilerons généralement une forme normale conjonctive C1 ∧ C2 ∧ ... ∧ Cn à l’ensemble de ces clauses {C1 , C2 , ..., Cn }). De même, une clause (p1 ∨ p2 ∨ ... ∨ pn ) ∨ (¬q1 ∨ ¬q2 ∨ ... ∨ ¬qm ) sera assimilée à l’ensemble des littéraux {p1 , p2 , ..., pn , ¬q1 , ¬q2 , ..., ¬qm }. Etant donné une clause C = {p1 , p2 , ..., pn , ¬q1 , ¬q2 , ..., ¬qm } et une interprétation I, la clause C est interprétée à Vrai si et seulement si l’un au moins des symboles propositionnels pi , i ∈ [1..n] est interprété à Vrai dans I, ou si l’un des symboles propositionnels qi , i ∈ [1..m] est interprété à Faux dans I. Par extension, on appelle clause vide la clause correspondant à un ensemble vide de littéraux. Cette clause est assimilée à la contradication et est toujours interprétée à Faux. Une forme normale conjonctive C1 ∧ C2 ∧ ... ∧ Cn s’interprète à Vrai dans une interprétation I si et seulement si toutes les clauses Ci , i = 1..n s’interprètent à Vrai. Elle s’interprête à Faux dès que l’une des clauses s’interprète à Faux. Par extension, la forme normale conjonctive vide correspondant à un ensemble vide de clauses est considérée comme valide car aucune interprétation ne permet de l’interpréter à Faux. Une clause contenant deux littéraux opposés p et ¬p est nécessairement valide ; on peut la retirer d’une forme normale sans changer le sens de celle-ci. Une clause Ci est dite subsumée par Cj si Ci contient la clause Cj . Lorsque Ci et Cj sont éléments d’une même forme normale et que Ci est dite subsumée par Cj , la clause Ci peut-être supprimée de la forme normale sans que le sens de celle-ci ne soit changé. 2.2 Calcul de satisfiabilité et algorithme de Davis et Putnam Dans les modélisations s’appuyant sur un formalisme logique, la résolution d’un problème se ramène généralement à résoudre un problème de satisfiabilité ou de validité d’une formule booléenne. Par exemple, tester si une formule F est conséquence logique d’une formule G, revient à montrer que la formule G ∧ ¬F est une formule insatisfiable. Dans la suite nous rappelons quelques techniques pouvant être employées pour résoudre ce problème 2.2.1 Méthode des tables de vérité La méthode dite des tables de vérité consiste à construire une table de vérité avec toutes les variables d’une formule F et de calculer la valeur de vérité de F pour chacune de ses interprétations. Pour n variables, le nombre d’interprétations à envisager est alors de 2n . Cette croissance exponentielle rend inutilisable cette méthode pour des formules contenant un grand nombre de variables. 5 2.2.2 Arbre sémantique Un arbre sémantique associé à un ensemble V de variables {v1 , ..., vn } de Vp est un arbre binaire dont les nœuds sont étiquettés par des variables de V , et dont les deux arcs issus de chaque nœud correspondent aux deux valeurs Vrai ou Faux que peut prendre la variable. Un arbre sémantique est dit complet si chaque branche (c’est à dire la séquence des arcs issus de la racine et aboutissant aux feuilles) contient une et une seule fois chaque variable de Vp . L’ensemble des branches d’un arbre sémantique complet définit toutes les interprétations des variables de Vp , et chaque chemin de la racine jusqu’à un nœud non terminal définit une interprétation partielle. Pour déterminer la validité d’une formule F , on construit un arbre sémantique complet associé aux variables VF de F et on réalise une évaluation de la formule à chacune des feuilles. On peut aussi trouver tous les modèles de F , qui sont représentés par les chemins menant à des feuilles où F s’évalue à Vrai. Notons que pour n variables, un arbre sémantique complet comprend 2n feuilles, cette méthode n’apporte donc pas d’amélioration significative par rapport à la méthode des tables de vérité. 2.2.3 Algorithme de Quine L’algorithme de Quine [3] est une amélioration de la méthode des arbres sémantiques dans laquelle, lors de la construction de l’arbre, on réalise à chaque nœud de l’arbre binaire une évaluation partielle de la formule. Cette évaluation permet de simplifier la formule, en tenant compte des assignations de variables déjà réalisées. Si une évaluation partielle permet de conclure directement, on ne poursuit pas la construction de l’arbre au delà de ce nœud. Par suite, le nombre de feuilles d’un arbre sémantique construit selon la méthode de Quine peut dans certains cas être nettement inférieur à ce que l’on obtiendrait en construisant systématiquement des arbres sémantiques complets. 2.2.4 Algorithme de Davis et Putnam L’ algorithme dit de Davis et Putnam est considéré à ce jour comme l’une des méthodes les plus efficaces parmi celles permettant de résoudre le problème de la satisfiabilité (ou l’insatisfiabilité) d’une formule booléenne. Il peut être vu comme un ¡¡raffinement¿¿ de la méthode de Quine, s’appliquant à des formules mises au préalable sous forme normale conjonctive (assimilée à un ensemble de clauses). Le fait de travailler sur des clauses, permet de préciser les règles de simplification mises en œuvre au niveau de chaque nœud de l’arbre sémantique, lors de l’évaluation d’une nouvelle variable propositionnelle. Détaillons le principe de cet algorithme. Soit une formule F sous forme normale conjonctive ¡¡pure¿¿ (i.e. dont on a éliminé les clauses correspondant à des tautologies ou les clauses subsumées par d’autres clauses) ; on applique sur F le calcul suivant : 1. Si F = ∅ alors F est satisfiable et dans ce cas, toute interprétation correspondant à un prolongement de l’interprétation partielle construite jusqu’à présent constitue un modèle de l’ensemble initial. Sinon : si F contient la clause vide (2) alors F est insatisfiable Dans les deux cas précédents, l’algorithme s’arrête à ce stade, sinon : 6 2. Choisir une variable p apparaissant dans l’une des clauses de F . 3. Calculer les deux ensembles de clauses suivants : • Fp l’ensemble des clauses de F privé des clauses contenant p et privé de toutes les occurences de ¬p (cas où p est vrai) • F¬p l’ensemble des clauses de F privé des clauses contenant ¬p et privé de toutes les occurences de p (cas où p est faux) 4. F est insatisfiable si et seulement si Fp et F¬p le sont. On justifie le calcul de Fp par le fait que si p est vrai, l’évaluation partielle de Fp permet d’éliminer le littéral ¬p des clauses. D’autre part, toutes les clauses contenant p sont évaluées à Vrai et peuvent donc être supprimées. On applique une opération symétrique pour calculer F¬p . Deux heuristiques simples de choix de la variable p peuvent être utilisées pour élaguer le plus tôt possible certaines branches de l’arbre réduisant ainsi la durée totale du calcul. 1. Si F contient une clause contenant un seul littéral p (respectivement ¬p), alors sélectionner p. En effet, cette clause se réduit alors à la clause vide lorsque p est faux et donc F¬p est insatisfiable, (respectivement lorsque p est vrai et Fp insatisfiable) interrompant le calcul pour ce nœud. 2. Si des clauses de F contiennent le littéral p (ou le littéral ¬p) et aucune ne contient ¬p (respectivement p) alors choisir comme variable p (respectivement ¬p). Par suite des simplification successives, il est possible dans certains cas de produire des clauses qui subsument d’autres clauses déjà présentes dans l’ensemble courant. On peut dans ce cas appliquer une règle de simplification (règle de subsomption) afin de retirer les clauses subsumées, ce qui contribue à réduire la taille des formules manipulées. 3 Résolvant, définition et méthode de calcul Dans cette partie nous abordons la notion de résolvant d’un ensemble de clauses par rapport à un ensemble de variables, nous montrons son utilité dans le contexte de problèmes de validation de modèles et nous présentons une méthode permettant de calculer de tels résolvants. Etant donnée une formule F définie à partir d’un ensemble de variables propositionnelles VF et une variable p ∈ VF , on peut considérer les formules Fp et F¬p correspondant aux simplifications de F obtenues lorsque l’on interprête la variable p à Vrai ou à Faux. Dans ce cas, si I est un modèle de F nécessairement I est soit un modèle de Fp soit un modèle F¬p . Par conséquent, I est un modèle de Fp ∨ F¬p . Ceci étant vrai pour tout modèle I de F, cette formule est donc une conséquence logique de F . Cette formule ne comporte que des variables de F \ {p}. En réitérant ce processus, il est possible de produire une formule Fi qui ne contient que des variables d’un sous ensemble Vi ∈ VF et qui est telle que F |= Fi . Cette formule correspond à la notion de résolvant de F relativement à Vi [2]. 7 3.1 Définition Un résolvant RVi d’une formule F relativement à une partie Vi de VF est une formule définie sur les seules variables de Vi et dont les modèles (sur Vi ) correspondent aux projections sur Vi des modèles (VF ) de F . Illustrons cette définition par un exemple. Soit F la formule a ∧ (¬b ∨ ¬c). La projection sur {a, b} des modèles de F nous donne soit (a = V rai; b = V rai) soit (a = V rai; b = F aux). Le résolvant R{a,b} de F relativement à {a, b} correspond donc à la formule a. De cette définition, découlent les propriétés suivantes : • L’ensemble des modèles du résolvant (exprimés sur VF ) contient au moins tous les modèles de F . • Tout résolvant de F est conséquence sémantique de F , puisque tous les modèles de F sont des modèles du résolvant. 3.2 Application à la modélisation de systèmes complexes La notion de résolvant d’une formule permet de caractériser des contraintes devant être nécessairement vérifiées par un sous-ensemble des variables de cette formule. Si la formule considérée correspond au modèle d’un système physique complexe, la notion de résolvant peut être utile pour essayer d’extraire à partir de ce modèle, certaines contraintes liant directement certains paramètres du système. Par exemple, cette technique peut être utile pour extraire les contraintes liant les données observables. Si F est la formule modélisant le système et Vi l’ensemble des variables correspondant aux données observables, il suffit de calculer le résolvant RVi de F relativement à Vi . Si un modèle admet des données indépendantes entre elles, celles-ci ne doivent être assujetties à aucune contrainte. Le calcul du résolvant par rapport à ces données doit alors conduire à un résolvant vide. Si tel n’est pas le cas, c’est que le modèle est en défaut. Si au contraire, on sait les données interdépendantes, le résolvant doit permettre d’expliciter les liaisons directes entre ces données. De telles contraintes peuvent alors être confrontées aux connaissances d’un expert du domaine. 3.3 Méthode de calcul du résolvant Une méthode possible pour calculer le résolvant d’une formule par rapport à un ensemble de variables Vi est d’appliquer partiellement l’algorithme de Davis et Putnam en choisissant de n’éliminer que les variables qui figurent dans l’ensemble de variables de VF \ Vi . Le résolvant est alors la disjonction de toutes les formules partiellement évaluées obtenues aux feuilles de l’arbre. Cette disjonction de formule peut alors être remise sous une forme normale conjonctive. C’est une variante de cette méthode, décrite dans [2], que nous reprenons ici. Au lieu d’explorer tout l’arbre pour calculer ensuite une disjonction, on calcule en fait un nouveau résolvant partiel à chaque étape en determinant la disjonction des deux sous formules créées. Après avoir choisi une variable p, comme pour l’algorithme décrit au chapitre 2, on procède au calcul des deux formules Np et N¬p à partir de la formule courante N . Seulement, au lieu de réappliquer l’algorithme de Davis et Putnam à chacune des deux formules, on l’applique à la formule N 0 = Np ∨ N¬p préalablement remise sous forme normale conjonctive. 8 Pour expliquer comment remettre simplement N 0 sous forme normale conjonctive, nous avons besoin de la définition d’une clause résolvante ( à ne pas confondre avec le résolvant). Nous définissons une résolvante entre deux clauses comme suit. Soient deux clauses de la forme : • C1 = p ∨ C10 • C2 = ¬p ∨ C20 On appelle clause résolvante associée aux clauses C1 et C2 relativement à la variable p la clause : C1,2 = C10 ∨ C20 Les répétitions éventuelles de littéraux dans C1,2 sont à supprimer. Cette clause est aussi appelée déduction par coupure relativement à la variable p. Si N ne contient pas de tautologies, les littéraux p et ¬p figurent dans des clauses distinctes. Notons Ci pour 1 < i < m les clauses qui contiennent le littéral p et Cj0 pour 1 < j < n celles contenant le littéral ¬p. On calcule alors directement N 0 à partir de N en remplaçant dans N toutes les clauses Ci et Cj par les résolvantes Ci,j relativement à p calculées sur tous les couples de clauses (Ci , Cj ) tel que 1 < i < m, 1 < j < n. On a alors pour N 0 une forme normale équivalente à Np ∨ N¬p . On peut vérifier ce résultat en ¡¡factorisant¿¿ dans Np ∨ N¬p les clauses ne contenant pas d’occurences de p, puis en appliquant la distributivité de la conjonction sur la disjonction entre l’ensemble des clauses Ci privées de p et l’ensemble des clauses Cj privé de ¬p. On trouve alors l’expression N 0 . 3.4 Illustration sur un exemple Prenons l’exemple d’un circuit électrique élémentaire. Nous avons deux tableaux sources A et B qui alimentent un tableau secondaire C. Ils sont protégés par les disjoncteurs DA et DB. Les variables propositionnelles qui caractérisent l’état physique de ce système sont: • la présence de tension sur les tableaux A, B, C (que l’on notera T A, T B, T C) qui sont les données observables; • l’état fermé des disjoncteurs DA et DB (que l’on notera DAF et DBF ). On a donc VF = {T A, T B, T C, DAF, DBF }. Si les mesures dont on veut exprimer les contraintes sont les trois tensions, on a : Vi = {T A, T B, T C} Notre modèle exprimera simplement la contrainte selon laquelle la présence de tension sur le tableau C équivaut à l’une des configurations symétriques ¡¡présence de tension sur A et disjoncteur DA fermé¿¿ ou ¡¡présence de tension sur B et disjoncteur DB fermé¿¿. Soit la formule F: ((T A ∧ DAF ) ∨ (T B ∧ DBF )) ↔ T C une fois mise sous forme clausale, cette formule nous donne les six clauses suivantes : 1. ¬T A ∨ ¬DAF ∨ T C 2. ¬T B ∨ ¬DBF ∨ T C 9 Tableau A Tableau B Disjoncteur DA Disjoncteur DB Tableau C Figure 1: Schéma d’alimentation électrique 3. ¬T C ∨ T A ∨ T B 4. ¬T C ∨ T A ∨ DBF 5. ¬T C ∨ DAF ∨ T B 6. ¬T C ∨ DAF ∨ DBF Calculons le résolvant de F relativement à Vi par coupure sur l’ensemble des variables de VF \Vi = {DAF, DBF } On choisi d’abord la variable DAF : • entre (1) et (5) la résolvante est une tautologie que l’on élimine; • entre (1) et (6) la résolvante est une tautologie que l’on élimine; • on élimine ensuite les clauses (1), (5) et (6). Puis on procède à l’élimination de DBF : • entre (2) et (4) la résolvante est une tautologie que l’on élimine; • on élimine ensuite les clauses (2) et (4). Il reste finalement la seule clause (3) : ¬T C ∨ T A ∨ T B que l’on peut mettre sous la forme T C → T A ∨ T B et qui est le résolvant RVi de F relativement à Vi . RVi exprime la contrainte que doivent vérifier les trois données T A, T B et T C: la présence de tension sur A ou B constitue une condition nécessaire de la présence de tension sur C. 10 4 Mise en œuvre de l’algorithme de calcul du résolvant La mise en œuvre de l’algorithme de calcul du résolvant nécessite de choisir une représentation des données de l’algorithme. Le choix d’une structure de données plutôt qu’une autre se justifie par la rapidité des accès aux données qu’offre cette structure, le temps requis pour la maintenir à jour et ceci, indépendamment du langage d’implémentation. Afin de pouvoir tester différentes possibilités nous avons essayé d’identifier les différents types de données en faisant abstraction de la façon dont ils étaient mis en œuvre. Ceci confère à l’ensemble une grande modularité qui facilite les comparaisons selon les choix réalisés. Dans un premier temps nous allons préciser la structure de l’algorithme de calcul de résolvant, ensuite nous détaillons les structures de données mises en œuvre. 4.1 Structure de l’algorithme de calcul du résolvant L’algorithme calcule le résolvant R d’une formule F relativement à un ensemble de variables Vi . Si VF représente l’ensemble des variables de F , l’ensemble des variables à éliminer est V = VF \ Vi . L’algorithme est conçu comme une fonction récursive res(V, F ) qui calcule le résolvant d’un ensemble de clause courant F pour un ensemble de variables à éliminer V . Chaque appel récursif correspond à l’élimination d’une variable de V . Lorsque l’ensemble des variables à éliminer est vide, la formule définie par l’ensemble F est le résolvant recherché. La schéma général de l’algorithme est le suivant : res(V, F ) : 1. Si V est vide, retourner F comme résultat. Sinon, choisir une variable v dans l’ensemble V des variables à retirer. 2. Déterminer l’ensemble CP os(v, F ) des clauses qui contiennent v et l’ensemble CN eg(v, F ) de celles qui contiennent ¬v. 3. Calculer l’ensemble N ewC(v, F ) de toutes les clauses résolvantes obtenues par coupure sur v, à partir des clauses de CP os(v, F ) et de CN eg(v, F ). 4. Eliminer de N ewC(v, F ) les clauses correspondant à des tautologies. 5. Renvoyer : res(V \ {v}, F \ (CP os(v, F ) ∪ CN eg(v, F )) ∪ N ewC(v, F )). Dans la version actuelle, le choix de la variable de coupure se fait de manière arbitraire. Nous étudierons dans une phase ultérieure l’impact de la prise en compte de critères heuristiques pour guider ce choix. 4.2 Structure des données manipulées par l’algorithme L’algorithme gère deux types de données : • l’ensemble des variables restant à éliminer V ; 11 • l’ensemble des clauses du résolvant courant. Afin d’identifier les structures de données adéquates pour modéliser ces ensembles, il est nécessaire de prendre en compte le type d’opérations effectuées sur ces ensembles. Les principales opérations effectuées sont : • Etant donné une variable, accéder aux ensembles de clauses contenant cette variable (posivement ou négativement) • Etant donné deux ensembles de clauses contenant respectement une variable positivement et négativement, calculer l’ensemble des clauses que l’on peut obtenir par coupure sur cette variable. • mettre à jour des ensembles de clauses associées aux autres variables apparaissant dans les clauses impliquées dans les opérations de coupure. 4.2.1 Représentation de l’ensemble des variables L’algorithme nécessite d’éliminer les clauses qui contiennent la variable de coupure v. Pour ce faire, il faut déterminer l’ensemble CP os(v, F ) (resp. CN eg(v, F )) des clauses de F où v apparait positivement (resp. négativement). Une première approche peut consister à parcourir lors de chaque étape l’ensemble F en sélectionnant les clauses contenant la variable v. Cependant, si F est constitué d’ un grand nombre de clauses, ce calcul peut s’avérer très couteux, car il est fonction de la taille de F , alors qu’un grand nombre de clauses de F risquent de ne pas contenir la variable v. Une autre méthode peut consister à associer en permanence à chaque variable v de V les deux ensembles de clauses CP os(v, F ) et CN eg(v, F ). Dans ce cas, l’accès à ces ensembles est immédiat. En contrepartie, lors de chaque étape d’élimination, il faudra mettre à jour ces ensembles CPos et CNeg, pour chaque variable apparaissant dans une clause impliquée dans une opération de coupure. Ceci nécessite donc une représentation de l’ensemble des variables permettant un accès rapide à chaque variable. Une structure permettant un accès indexé semble adaptée. Dans la suite nous supposerons que l’ensemble des variables est représenté par un tableau. Dans la représentation des clauses, les variables de V seront représentées par leur indice dans le tableau. Connaissant l’indice d’une variable, l’accès à sa définition se fait alors dans un temps constant. 4.2.2 Représentation des ensembles des clauses Le calcul des clauses obtenues par coupure lors de chaque phase d’élimination nécessite d’effectuer des mises à jour des ensembles CPos et CNeg, ce qui revient à faire des ajouts et des suppressions sur des ensembles de clauses. La suppression d’un élément nécessite tout d’abord de déterminer sa position, puis d’effectuer la suppression proprement dite, ce qui s’accompagne éventuellement d’une réorganisation de l’ensemble des clauses. Dans la version actuelle nous avons fait le choix d’une structure séquentielle pour représenter les ensembles de clauses. Ceci permet d’effectuer les ajouts d’éléments en temps constant. Par contre le coût de la recherche et de la suppression d’un élément est en moyenne 12 proportionnel à la longueur de la liste. Il existe d’autres méthodes plus rapides en moyenne pour chercher un élément dans un ensemble. Par exemple, dans certaines structures comme les différents types d’arbres de recherche, la recherche d’un élément peut se faire en moyenne en ln n accès (au lien de n). Mais si la recherche est plus rapide, les ajouts sont alors plus coûteux. On ne peut pas dire à priori quelles seront, parmi les ajouts ou les retraits, les opérations les plus fréquentes. Si le coût de la mise à jour se révèle prépondérant lorsque la taille des données de l’algorithme augmente, on tentera de réduire celui-ci par une analyse plus fine de la complexité pour ces opérations de mise à jour. Remarquons que le résolvant correspond lui-même à un ensemble de clauses : l’ensemble F en fin de calcul. Mais lors du calcul, on ne manipule les clauses que par l’intermédiaire des ensembles CP os(v, F ) et CN eg(v, F ) des variables concernées. Si l’on inclut les variables de Vi dans le tableau des variables, la définition de F peut être obtenue en parcourant les ensembles CP os(v, F ) et CN eg(v, F ) pour tout v de Vi et V . Si on ne veut déterminer F que pour connaitre le résolvant, il suffit de prendre v ∈ Vi . L’inconvénient de cette méthode est de nécessiter à chaque retrait ou ajout de clauses, les mêmes opérations de mises à jour pour les variables de Vi que pour celles de V , alors qu’aucune opération de coupure n’est exécutée sur des variables de Vi . Une autre méthode consiste à rassembler au sein d’une même structure, l’ensemble des clauses de F . La gestion d’un ensemble des variables de Vi n’est alors plus nécesssaire. Une variante consiste à ne stocker que les clauses de F que l’on sait appartenir au résolvant recherché. Cette structure ne nécessite alors pas de retrait de clauses de l’ensemble, mais elle ne permet pas de connaitre F avant que le calcul est arrivé à son terme. Nous préférons construire l’ensemble F à chaque étape du calcul, en ajoutant les clauses créées par coupure et en retirant les clauses éliminées. On peut ainsi facilement afficher en cours de calcul la formule F qui est elle même un résolvant, obtenant ainsi un résolvant ¡¡partiel¿¿. Cela permet aussi de pouvoir contrôler le calcul lors de la mise au point des différentes implémentations. Les opérations effectuées à chaque étape par l’algorithme sur l’ensemble des clauses, sont alors le retrait des clauses contenant v et l’ajout de nouvelles clauses obtenues par coupure. On adopte une structure de liste doublement chainée, où chaque élément permet d’accéder à l’élément suivant ou précédent de la liste. Comme pour une liste séquentielle, la liste doublement chainée permet d’ajouter de nouvelles clauses en un temps constant. De plus le retrait d’une clause à laquelle on accède par l’intermédiaire d’un ensemble CP os(v, F ) ou CN eg(v, F ), peut lui aussi se faire en temps constant. 4.2.3 Représentation d’une clause Une clause est une disjonction de littéraux assimilée à un ensemble de littéraux. Lors de la création de chaque nouvelle clause par coupure, on ajoute les littéraux de deux clauses CA et CB pour en former une troisième CC . Puis on vérifie que la nouvelle clause CC n’est pas une tautologie. CA et CB n’étant pas des tautologies, La clause CC n’est pas une tautologie si l’intersection de l’ensemble des littéraux positif de CA et de l’ensemble des littéraux négatif de CB est vide et si l’intersection de l’ensemble des littéraux négatifs de CA et de l’ensemble des 13 littéraux positif de CB est vide. La nécessité de contrôler les tautologies lors des opérations de coupure nous conduit à organiser les littéraux d’une clause en deux structures séparées, l’ensemble des littéraux positifs et l’ensemble des littéraux négatifs. Pour déterminer si une clause est une tautologie, il nous faut calculer les intersections de différents ensembles de littéraux. Une méthode pour déterminer l’intersection de deux ensembles consiste à accèder à chacun des éléments d’un de ces ensembles et de les comparer aux éléments de l’ autre ensemble. Soit pour deux ensembles de longueurs n et m, effectuer n × m accès aux éléments et autant de comparaisons entre éléments. Si N V est le nombre moyen de littéraux C par clause, le coût moyen de la vérification qu’une clause n’est pas une tautologie est alors de ³ ´2 l’ordre de N V accès et comparaisons de littéraux. C Une autre méthode consiste à organiser les élément des ensembles sous forme de listes triées. Le calcul de l’intersection peut alors ce faire en n+m accès et comparaisons. Pour déterminer le coût total de la création d’une clause, il faut y ajouter le coût du tri des littéraux de la nouvelle clause On utilise pour créer une nouvelle clause, un algorithme dit de tri fusion, qui consiste à construire, à partir de deux listes triées, une liste triée contenant la réunion des éléments des deux listes d’origines. Une version récursive de cet algorithme appliquée à des listes séquentielles s’exécute en n + m accès et n + m comparaisons, si n et m sont les longueurs des listes d’origine. Si de plus les listes sont triées, alors le coût moyen de la création d’une nouvelle clause n’est alors plus que de l’ordre de N V opérations d’accès et de comparaisons. C 5 Codage Chaque langage de programmation dispose de primitives qui lui sont propres et qui peuvent avoir un impact sur la façon dont les différentes structures de données sont codées. Dans la suite nous détaillons l’implémentation qui a été développée en Prolog (ECLiPsE sous SPARC et Open Prolog sous Mac). Une autre implémention s’appuyant sur le langage CAML est actuellement en cours de développement Il n’existe pas en PROLOG d’affectation destructive permettant de modifier un élément d’une structure. La modification d’un élément doit se faire par instanciation d’une nouvelle variable à une nouvelle structure reconstruite à partir de l’ancienne, à l’élément modifié près. 5.1 Représentation de l’ensemble des variables Ne pouvant réaliser d’affectation destructrive, il n’y a donc pas de structure de tableau en PROLOG standard. Il est donc nécessaire de la simuler. L’intérêt d’une structure de tableau est de pouvoir accéder à la valeur d’un élément, pour la lire ou la modifier, en temps constant. Si on connait la taille du tableau au moment de l’écriture du programme et si cette taille ne dépasse pas un maximum propre à la version du PROLOG utilisé (généralement un nombre relativement petit pour l’usage que l’on veut en faire) un tableau de n élément peut être représenté par un terme ayant n arguments. 14 Dans ce cas la modification d’un élément peut être réalisée par le biais d’une relation du type modif/4, constituée de n clauses de Horn de la forme % modif( Indice, Tableau, Element, NouveauTableau) % change l’element d’indice Indice en Element (exemple pour un tableau % de trois elements) modif(1, tab(_,Ele2,Ele3), Element, tab(Element, Ele2, Ele3) ). modif(2, tab(Ele1,_,Ele3), Element, tab(Ele1, Element, Ele3) ). modif(3, tab(Ele1,Ele2,_), Element, tab(Ele1, Ele2, Element) ). De la même façon, on peut définir un prédicat permettant d’accéder directement à la valeur d’un élément. Cette méthode a l’avantage d’être très rapide en nombre d’appel de prédicat ( un seul ) pour modifier ou lire un élément. Si le Prolog est équipé d’un mécanisme d’indexage des clauses (ce qui est le cas sous ECLiPsE) l’accès aux données se fait en temps quasi constant. mais elle nécessite de connaitre à l’avance la taille du tableau à représenter et elle ne permet pas d’utiliser de prédicats génériques, applicables à des tableaux de tailles différentes. Une autre approche possible consiste à simuler le tableau par une structure arborescente. On peut alors construire un tableau de taille quelconque en donnant à cette structure la profondeur nécessaire. La structure que nous avons choisie représente un tableau par un arbre dont toutes les feuilles sont à la même profondeur. Les données (les éléments du tableau) sont associées aux feuilles de l’arbre. C’est un arbre n–aire, c’est à dire que le nombre de fils par noeud est fixé. Pour un arbre possédant n fils par noeud, chaque nœud est représenté par un terme à n arguments, qui sont soit des éléments du tableau (qui sont alors les feuilles) soit à leur tour des noeuds. Un arbre est défini par une structure à deux paramètres. Le premier étant le noeud racine de l’arbre, le second étant un entier qui désigne la profondeur p de l’arbre. Pour effectuer l’accès ou la modification d’un élément d’indice Index, on cherche de manière récursive, dans quel sous-arbre se trouve la feuille qui représente l’ élément d’indice i. Un arbre ayant comme profondeur p a alors np feuilles. Chaque sous-arbre a alors np−1 feuilles. Si l’indice Index de l’élément recherché a une valeur comprise entre 0 et np−1 − 1, on sait alors qu’il est dans le premier sous arbre. Si Index a une valeur comprise entre np−1 et 2np−1 − 1, il est dans le second sous arbre, etc. Ainsi, pour déterminer dans quel sous-arbre rechercher l’élément, on calcule la partie entière de la division de l’indice i par np−1 . Le résultat entre 0 et n − 1 est le numéro du sous arbre. On répète alors l’opération en considérant le sous arbre choisi comme étant un tableau de profondeur n − 1 dans lequel on cherche l’élément dont l’indice est le reste de la division entière calculé précédemment. Le calcul se termine lorsque la profondeur de l’arbre courant est p = 0, l’élément étant alors l’une des feuilles. Un accès en lecture ou la modification d’un élément d’un tableau de taille Size = np se font alors en ln(Size)/ln(n) appels de prédicats (que l’on note aussi logn (Size)). Pour cette implémentation nous avons utilisé la librairie logarr.pl réalisée par David H. D. Warren et Fernando Pereira. 15 Dans la structure d’arbre implémentée, chaque nœud non terminal a quatre nœuds fils. Les nœud sont représentés par le terme $/4. La fonction de lecture d’un élément est aref/3. Le prédicat aref(Index, array(Array,Size), Item) accède à l’élément d’indice Index du tableau array(Array,Size) et rend le résultat par l’intermédiaire de la variable Item %--------------------------------------------------------aref(Index, array(Array,Size), Item) :check_int(Index), Index < 1<<Size, N is Size-2, Subindex is (Index>>N) /\ 3, array_item(Subindex, N, Index, Array, Item). %--------------------------------------------------------Dans le prédicat aref, Subindex a pour valeur un entier de 0 à 3 qui indique dans lequel des quatre sous-arbres se trouve l’élément recherché. Il est calculé par une opération de décalage logique bit à bit sur l’indice Index. Le prédicat array item opère le calcul récursivement jusqu’à arriver aux feuilles de l’arbre. Son premier argument désigne le numéro du sous-arbre où se poursuit la recherche, le second, est un compteur de la profondeur de l’arbre, qui est representé par le quatrième argument.Le troisième argument est l’index de l’élément que l’on propage tout le long du calcul. Il arrive en fin de calcul lorsque son deuxieme argument a pour valeur 0. Il instancie alors la variable Item avec la feuille correspondant à l’élément rechercher. Le prédicat qui permet la modification d’un élélment du tableau est aset/4. L’appel au prédicat enlarge array permet d’agrandir le tableau si l’index de l’élément modifié n’est pas dans le tableau. Le prédicat update array item effectue la récursion, et retourne comme dernier argument le tableau modifié. Le calcul se fait comme précédemment, sauf que l’on fait appel à chaque étape de la récursion au prédicat update subarray/5 qui retourne un nouvel arbre en réutilisant les sous-arbres non modifiés. 5.2 Représentation des variables Les variables sont représentées par des termes de la forme posneg(LCPos,LCNeg) où LCPos et LCNeg correspondent aux ensembles de clauses où la variable v apparait respectivement positivement et négativement. 5.3 Représentation des ensembles des clauses Les ensembles de clauses associés à chaque variable sont codés par des listes. Notons que chaque clause est représentée de façon unique. Les ensembles CPos et CNeg, comme l’ensemble F , correspondent en fait à des séquences de références vers celles-ci. Dans la version actuelle la mise à jour des ensemble CPos et CNeg consiste à reconstruire les listes CPos et CNeg à chaque étape. En ce qui concerne le résolvant courant, qui correspond aussi à un ensemble de clauses, on peut s’attendre à ce que cette structure soit de taille importante. La reconstruction systématique de cette structure à chaque étape risque dans ce cas d’être très coûteuse. En effet, comme on l’a vu pour le tableau de variables, la difficulté en PROLOG est de pouvoir modifier un élément 16 de l’ensemble. On ne peut utiliser une représentation de l’ensemble par une liste doublement chainée, le retrait d’un élément se faisant par modification des élément suivant et précédent. Toutefois, la seule opération de modification d’un élément de l’ensemble des clauses est son retrait. Les clauses une fois définies ne sont pas modifiées. On peut alors utiliser l’effet de bord qui consiste à instancier une variable pour réaliser un marquage des clauses éliminées. Une variable initialement libre est associée à chaque clause de l’ensemble. Lorsque l’on a choisi la variable à éliminer et que l’on accède aux clauses qui lui sont associées, ces clauses sont marquées comme éliminées en instanciant la variable libre associée à chaque clause. Les mêmes clauses présentes dans l’ensembles des clauses sont donc elles aussi marquées. L’ensemble des clauses ne nécessitant alors pas de retrait, mais seulement des ajouts des nouvelles clauses générées, il est représenté par une liste de clauses. Les ajouts de nouvelles clauses se faisant en tête de liste. Pour pouvoir déterminer la valeur du résolvant en fin de calcul, on parcourt alors la liste des clauses et on ne retient que celles qui ne sont pas marquées. Cette technique de marquage a l’avantage de retirer des clauses par une simple instanciation de variable, sans avoir à les rechercher dans un ensemble. Elle a toutefois l’inconvénient de ne pas libérer la place occupée en mémoire par ces clauses. Si l’on arrive à calculer dans un temps raisonnable le résolvant de formules d’un trop grand nombre de clauses pour la mémoire disponible, il faudra alors choisir une structure pour représenter l’ensemble des clauses qui permettent une réelle élimination des clauses de la mémoire. Si l’on veut conserver une définition de l’ensemble F tout au long du calcul, on représentera F par une structure permettant le retrait d’un élément (tel que le tableau utilisé pour l’ensemble des variables). Mais si l’on veut privilégier la rapidité du calcul au détriment de la possiblité de visualiser F en cours de calcul, on peut utiliser une liste qui ne contient que les clauses qui appartiendront au résolvant. En effet, une clause dont on a déterminé que toutes ses variables sont dans Vi ne peut pas être éliminée par coupure et fait donc partie du résolvant. Ces clauses du résolvant peuvent alors être rangées dans une liste, dans laquelle on n’opérera que des ajouts de clauses. Notons qu’une variable qui est retirée du tableau est simplement remplacée par le symbol $ qui représente une case vide du tableau. 5.3.1 Représentation des clauses Chaque clause est définie par ses deux ensembles de littéraux, positifs et négatifs, ainsi qu’une variable servant au marquage des clauses éliminées. La représentation s’appuie sur des termes de la forme : clause(VPos,VNeg,Flag) dans lesquels VPos et VNeg sont les listes triées de littéraux de la clause respectivement positifs et négatifs, et Flag est une variable restant libre tant que la clause n’est pas éliminée du résolvant courant. 5.3.2 Les procédures de calcul Le premier prédicat appelé pour exécuter le calcul du résolvant est lance/3. Il prend en entrée la formule et la liste de variables à éliminer et retourne le résolvant obtenu par coupure sur les variables d’entrée. 17 Avant de lancer le calcul du résolvant, différentes procédures d’initialisation sont appelées. Le prédicat lance fait appel au prédicat init var qui remplace les variables de la formule par les termes index(i) et symbol(nom) selon que les variables doivent être éliminées ou pas. On effectue cette opération avant la mise sous forme normale, car la formule est supposée contenir alors moins de variables. Après la mise sous forme normale conjonctive de la formule, on construit l’ensemble des clauses. L’initialisation du tableau des variables se fait au même moment. La procédure de calcul de résolvant est une procédure récursive, qui après avoir choisi une variable et l’avoir retirée de l’ensemble des variables, appelle le prédicat qui lance le calcul de toutes les coupures. Pour pouvoir former tous les doublets de clauses possibles, la procédure fait appel à deux procédures correspondant à des boucles imbriquées. 6 Résultats L’implémentation du programme de calcul de résolvant a été réalisée sur deux architectures de de machine (Sun SPARC 5 et Mac). De nombreux tests sur des petites et de grosses bases ont été faits. Aprés avoir analysé les résultats obtenus pour chaque base, il s’avère que le programme termine en temps raisonable pour certaines bases et dépasse les capacités mémoire de la machine pour d’autres exemples. Cela n’est pas surprenant, d’une part parce que le probléme à traiter reste de complexité exponentielle, et d’autre part parce qu’aucune heuristique de choix n’a été mise en œuvre. Nous avons testé notre programme sur l’exemple fourni par la DER-EDF. Cet exemple comporte une base d’une centaine de règles environ sur environ 300 variables. L’élimination des variables dans l’ordre d’apparition dans la base provoque une explosion combinatoire et un dépassement mémoire. Par contre, après un réordonnement judicieux des variables, nous avons obtenu des résulats complets en temps raisonnable (184,98 sec sur machine SPARC Sun ...). Les résultats obtenus concordent bien avec ceux qui nous ont été communiqués. A l’heure actuelle nous testons également le programme sur des exemples générés de façon aléatoire. 7 Conclusions et perspectives De nombreux choix ont été réalisés sur la façon de calculer le résolvant comme sur la facon d’implémenter ce calcul. On a vu qu’il existe une autre méthode de calcul de résolvant que celle proposée, qui est la recombinaison des sous formules de l’arbre au niveau des feuilles après application de l’algorithme de Davis et Putnam, borné à une certaine profondeur, à une formule. L’analyse des résultats nous a confirmé dans la nécessité d’implémenter d’autre types de structures de données plus efficaces que celles utilisées dans le programme actuel, ainsi que la prise en compte de critères heuristiques pour guider le choix de la variable de coupure. References [1] M. Davis and H. Putnam. A computing procedure for quantification theory. JACM, 7:201– 215, 1960. 18 [2] J.F. Héri et J.C. Laleuf. Logique et modélisation - copatibilit é entre données et modèles en logique des propositions. Technical Report HI21/94-013, EDF-DER, 1995. [3] J.-M. Alliot et T. Schiex. Intelligence artificielle et informatique theorique. Cepadues eds, 1993. 19