Calcul de résolvant en logique des propositions Un outil pour la

publicité
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
Téléchargement