1. HISTORIQUE Origine PROLOG : PROgrammation LOGique. 1967 : SIMULA 67 1972 : introduit par Alain COLMERAUER. Ce langage permet une programmation « déclarative ». Repose sur deux courants: la définition des langages de programmation (« W-grammaires ») et la démonstration automatique 1981 : programme Japonais de 5ème Génération. 1989 : Programmation Logique avec Contraintes (Prolog III) 1995 : Extension et introduction des contraintes sur intervalles (Prolog IV)
HISTORIQUE (1) Logiciels Prolog Ø Domaine du « libre » INRIA : GNU-Prolog Sicstus, Eclipse Ø Domaine payant PrologIA : Prolog 4 COSYTEC (Orsay) : CHIP (Constraint Handling in Prolog) ILOG Solver
HISTORIQUE(2) Méthodologie de construction de programme Spécification logique à partir de laquelle on dérive un programme à l’aide de « règles de dérivation ». Aspects neufs: Le langage dans lequel la spécification et le programme sont écrits est le « même ». Les règles de dérivation conservent la «logique » : si le programme s’arrête le résultat est conforme à la spécification.
HISTORIQUE (3) Exemple PLC grand public vc(L, C) : vrai si L est une liste de versements annuels qui rembourse le capital C avec un intérêt de 10 %. Spécification (et aussi programme): vc([], C) <= (C = 0). vc ([V | Lp], C) <= vc(Lp, Cp) et Cp = C – V + (1/10) * C. Une première question, typique d’un banquier typique: V, vc([V, V, V], 1000) ? Réponse: V = 133100/331 ou 402.11480362537765
HISTORIQUE (5) Aspect « déclaratif » Autre question dans le même style (pas pour banquier ordinaire): V, vc([V, 2*V, 3*V], 1000) ? V = 133100/641 ou 207.6443057722309 Ou encore (pas pour banquier du tout): C, vc([200, 400, 300], C) ? C = 982000/1331 ou 737.7911344853494
2.CLAUSES DE HORN, MACHINE PROLOG clause de Horn: P0 <= P1 … Pn avec n 0 Pi : formule atomique (plus petite formule qui peut être égale à vrai ou faux) Pi = p (t1, …, tm) avec m 0 p : prédicat t1, …, tm : termes: - soit une constante : 123, theodore, … soit une variable (ou inconnue) : X,X1,Mav….
CLAUSES DE HORN, MACHINE PROLOG (1) soit un terme complexe : f (t1, …, tp), p 0 f : foncteur (une constante) t1, …, tp : termes Exemple: f (1, g (3, X)) Les variables des clause sont quantifiées universellement Exemple : X, biellecoulée(X) <= plusd huile(X) voyantrouge(X)
CLAUSES DE HORN, MACHINE PROLOG (2) Toute clause de Horn peut se mettre sous la forme: P0 ¬P1 … ¬Pn avec n 0 Démonstration ? Un seul littéral (formule atomique ou formule atomique négativé) est positif (P0) Une clause générale est de la forme: P0 Q1 … Qp ¬P1 … ¬Pn Qui possède éventuellement plusieurs littéraux positifs et qui peut se mettre sous la forme: P0 Q1 … Qp <=P1 … Pn ou encore: P0 <=P1 … Pn ¬ Q1 … ¬ Qp Attention: on ne peut utiliser de clauses générales en Prolog.
Exemple initial type BD Des formules logiques pere (jacques, pierre) <= pere (pierre, isidore) <= et des théorèmes à démontrer: pere (jacques, pierre) et X pere (jacques, X), on peut déduire les règles Prolog suivantes: pere (jacques, pierre). /*regle 1*/ pere (pierre, isidore). /*regle 2*/ et les requêtes Prolog suivantes: pere (jacques, pierre). pere (jacques, X).
Exemple initial type BD (1) pere (jacques, X). fournit un succès: X = pierre pere (X, Y) fournit un succès et deux solutions X = jacques, Y = pierre ; X = pierre, Y = isidore. On trouve ici un exemple de la première de dérivation: Les règles sont consultées par le démonstrateur prolog dans leur ordre d’apparition.
Exemple initial type BD (2) Le théorème à démontrer suivant Z, Y pere (jacques, Z) pere(Z, Y) peut se traduire par la requête: pere (jacques, Z), pere(Z, Y) démontrée par le processus: 1 2 {jacques = jacques, Z = pierre} pere (Z, Y) {jacques = pierre, Z = isidore} Echec 2 1 {Z = jacques, Y = pierre} Echec {Z = pierre, Y = isidore} Succès
Exemple initial type BD (3) On trouve ici un exemple de la deuxième règle de dérivation: Les buts (formules atomiques) sont prouvés de gauche à droite. La clause de Horn X, Y, Z grand_pere (X, Y) <= pere (Z, Y) pere (X, Z) peut se traduire par la règle grand_pere (X, Y) :- pere (Z, Y), pere (X, Z). ou par la règle grand_pere (X, Y) :- pere (X, Z), pere (Z, Y).
Exemple initial type BD (4) Exercice: donner les deux démonstrations différentes suivant ces deux règles de la requête: grand-pere(jacques,Y). Exercice: montrer que la clause de Horn X, Y, Z grand_pere(X, Y) <= pere(Z, Y) pere(X, Z) est équivalente à la formule X, Y grand_pere(X, Y) <= Z ( pere(Z, Y) pere(X, Z))
Machine Prolog -état courant: (W, L-b, R) où W : ensemble des variables de la question initiale L-b : séquence des "buts« (formules atomiques) à prouver. R : système d’équation courant (supposé « satisfiable » plus ou moins « fortement ») -état initial : (W, [Q0], F ) Q0 = requête initiale.
Machine Prolog (1) -état final = (W, [ ], Rf) Rf satisfaisable. Résultat: Système d’équation "le plus petit" extrait de Rf portant sur les variables de W. -règle d’inférence: (W, [B0, B1, …, Bm], R), P0 :– P1, …, Pn ------------------------------------------------------------- (W, [ P1, …, Pn, B1, …, Bm], {B0 = P0} R) R et {B0 = P0} R doivent être statisfaisables
Machine Prolog (2) Exemple: état initial : ({U}, [grand_pere (jacques, U)], F ) Application de la règle d’inférence: ({U}, [grand_pere (jacques, U)], F), grand_pere (X, Y) :- pere (X, Z), pere (Z, Y). ----------------------------------------------------- ({U}, [pere (X1, Z1), pere (Z1, Y1)], {grand_pere (jacques, U) = grand_pere (X1, Y1)} F )
3. Contraintes sur les arbres Équation sur les arbres: satisfaisable si les étiquettes des sommets sont égales et si leurs fils sont égaux deux à deux. On dit qu’il y a « unification » des deux arbres. Exemple: { f (1,Y) = f (X,4) } ou f f = X 4 1 Y a une solution: {1 = X, Y = 4}
Unification f (1,Y) et f (X,4) s’»unifient » en f(1, 4). Exemple: { f (Y, 2, 1) = f (1,Y,Y) } n’a pas de solution. { X = f (1,X) } a pour solution un arbre infini rationnel (voir chap. 7).
Listes - liste vide : constante, soit : nil liste non vide : terme complexe soit: cons(E, L) où E est le premier élément de liste et L la liste restante. Exemple: cons(1, cons(2, cons(3, nil))) Exemple d’unification: {cons(X, cons(2, cons(3, nil))) = cons(1, L)} a pour solution: { X = 1, L = cons(2, cons(3, nil)) }
Construction de programmes 1) Spécification logique 2) Dérivation depuis la spécification d’un programme qui se termine pour la question considérée.
Terminaison et « réversibilité » Définition: concat(X, Y, Z) : vrai si X, Y, Z sont des listes et si Z est la concaténation de X et Y. Spécification: X, Y concat (nil, Y, Z) <= Z = Y. E, L, Y, L1 concat (cons(E,L), Y, cons(E, L1)) <= concat (L, Y, L1). Un programme: concat (nil, Y, Y). concat (cons(E,L), Y, cons(E, L1)) :-concat (L, Y, L1).
Terminaison(1) Est-ce que la démonstration de concat(X, Y, Z) se termine pour les cas suivants ? X Y Z où * signifie connu et ? inconnu * * * * * ? * ? * ? * * * ? ? ? * ? ? ? * ? ? ?
Terminaison(cont. 1) Théorème à retenir « toute suite entière ui, tq i ui 0 et ui > ui+1, est finie » Exemple non trivial : proc pgcd = (ent a, b) ent : cas a = b alors a, a > b alors pgcd (a-b, b), a < b alors pgcd (a, b-a) fcas Avec pré-condition a>0, et b>0 et entiers
Terminaison(cont. 2) Pour montrer que pgcd (a0, b0) termine (a0>0, b0>0): pgcd (a0, b0) -> pgcd (a1, b1) -> pgcd (a2, b2)… Il s’agit de « découvrir » une fonction f entière respectant le théorème précédent et tq: u0 = f (a0, b0), u1 = f (a1, b1), u2= f (a2, b2)… f(a, b) = a-b ne convient pas f(a, b) = | a-b | ne convient pas f(a, b) = a + b et f(a, b) = a * b conviennent.
Terminaison (cont. 3) En programmation logique, il faut prendre soin de montrer que la fonction f est évaluable Soit Z connu comme dans la requête: concat (X, cons(2, nil), cons(1, cons(2, nil))) La fonction f = |Z| convient: |cons(E, L1)) |> |cons(L1)| On montre par réccurence qu’elle est évaluable. Idem si X est connu. Les seuls cas de non terminaison sont ceux où ni la taille de X ni celle de Z ne sont connues. Question: quel est le résultat de la requête suivante ? concat (cons(1, nil), Y, Z)
Notation usuelle de listes Notations de base: [], [E | L] Contractions: [E1 | [E2…[En | L]…]] = [E1, E2, …, En|L] [E1, E2, …|[]] = [E1, E2,…]
Dérivation de programme Définition: inverse(X, Y) : vrai si X est inverse de Y . Spécification: inverse([], []) <= E, L, Y, L1 inverse ([E| L], Y) <= concat (L1, [E], Y) inverse(L, L1). Un programme: inverse1([], []). inverse1 ([E| L], Y) :- concat (L1, [E], Y), inverse1(L, L1).
Dérivation de programme(1) Méthodologie: dérivation d’un deuxième programme possédant la même spécification: inverse2([], []). inverse2([E| L], Y) :- inverse2(L, L1), concat (L1, [E], Y). Mêmes questions concernant la terminaison de démonstrations du type inverse(X, Y) selon que la taille de X ou celle de Y est connue.
Coupure Troisième règle de dérivation. Nécessitée par un besoin d’efficacité i toutes les démonstrations ne sont pas nécessaires. Exemple: membre_et_reste(X, L ,R): vrai si X a une occurrence dans L et si R est L amputée de cette occurence. membre_et_reste(X, [X | L],L). membre_et_reste(X, [E| L], [E | Rp]):- membre_et_reste(X, L, Rp).
Coupure(1) membre_et_reste(1, [2,1,3,1,4,1,5], R). fournit: Si on introduit la coupure membre_et_reste(X, [X | L],L) :- !. le seul résultat est: R = [2,3,1,4,1,5].
Coupure(2) La coupure ! est une formule atomique toujours vraie. De plus, tous les buts en suspens pour prouver le but qui est le sujet de la règle dans laquelle intervient la coupure sont abandonnés. Exemple: :- B1, !, B2. B :- B3. B1:-A1. B1 :- A2. A1. Consigne: règle de dérivation à employer avec précaution pour ne pas « supprimer la complétude », ie supprimer toutes les démonstrations. Emploi « sur »: requête initiale, !.
Coupure(3) B abandon B1, !, B2 A1, !, B2 abandon !, B2 B2
Négation par échec neg_par_echec(b) :- b, !, fail. neg_par_echec(b). « Fausse » négation à n’employer que sur des buts instanciés. homme(pierre). homme(jacques). fort(jacques). homme(X), neg_par_echec(fort(X)) donne un succès. neg_par_echec(fort(X)), homme(X) donne un échec.
4. Contraintes sur les listes Objets: les listes Opérations: X o Y (concaténation) Contraintes: =, #, size(N, X) (N est la taille de X). Exemple: {[U, 2]o Y = [1]o [V, W]} se réduit en: {U = 1, 2 = V, Y = [W]} Deux questions: décidabilité de la satisfaisabilité de telles contraintes et (si décidabilité) complexité (en termes de nb de variables et nb d’équations).
Linéarité Ici pour obtenir une bonne efficacité, on autorise seulement des concaténations « linéaires » tq dans Z = X oY , deux des tailles parmi celles de X, Y et Z sont connues. Exemple: X o [3] o Y = [1, 2, 3, 4] n’est pas accepté. Mais size(6, X), [1, 2, 3]o X = X o [1, 2, 3]. size(10000, X), [1]o X = X o [1]. le sont.
Réversibilité. Pseudo-linéarité Pour illustrer le fait que + de contraintes => + de « réversibilité », une nouvelle mouture: inverse([], []). inverse([E] o X, Y o [E]) :- inverse(X, Y). nécessite la vérification que les équations posées sont linéaires. inverse([1, 2], Y). termine mais pose des équations non linéaires. Cependant, elles sont conservées et elles deviennent linéaires par propagation (pseudo-linéarité).
Réversibilité. Pseudo-linéarité(1) Une solution sans équations pseudo-linéaires. inverse(X, Y) :- sixe(N, X), size(N, Y), palindrome(X o Y). palindrome([]). palindrome([E] o X o [E]):- palindrome(X).
5. Analyse syntaxique et traduction Motivations - faire ressortir l’analogie entre la restriction que représente les clauses de Horn face aux contraintes générales et celle des grammaires hors contexte face aux grammaires générales (« context-sensitive ») appliquer le principe de composition dans un cadre rigoureux qui fait apparaître le besoin de valeurs sous la forme de fonctions Analyse syntaxique Exemple: le langage ancbn, n 0 défini par la gramaire G d’axiome S: S -> c S -> a S b
Analyse syntaxique Il s’agit de définir le prédicat analyse-G(M) : vrai si la liste M représente une chaîne du langage décrit par G: Exemple: analyse_G([a, a, c, b, b]) est vrai Deux clause sont dérivées à partir des deux règles de grammaires: analyse_G(M) :- M= [c]. analyse_G(M) :- M = [a]o M1o[b], analyse_G(M1). Mais à cause de la restriction sur la concaténation, cette dérivation ne peut être généralisée.
Analyse syntaxique(cont. 1) Exemple: la grammaire suivante d’axiome A A -> X Y X -> X -> a X b Y -> Y -> c Y La démonstration de analyse-G([a, b, c]) pose l’équation: M = M1o M2 et demande les démonstrations de analyse_X([M1]) et de analyse_Y(M2) qui ne terminent pas
Analyse syntaxique(cont. 2) Une solution correcte mais très inefficace: remplacer les contraintes sur les listes par des contraintes sur les arbres. Exemple: analyse_G(M) :- concat(M1, M2, M), analyse_X(M1), analyse_Y(M2). Autre solution. Idée: « préparer » la concaténation à droite. Formellement: faire abstraction du suffixe d’une chaîne , ie au lieu de considérer qu’il s’agit de la chaîne vide, on l’ »abstrait » (on le considère comme inconnu). En lambda-calcul, la représentation serait l’abstraction S.M o S où M est la chaîne et S le suffixe. Pratiquement, en Prolog qui est basé sur la logique du 1er ordre (sans lambda-expression): une chaîne est représentée par [C, S ] comme le préfixe de la chaîne C dont le suffixe est S (« difference list »). Exemple: [[a, b, c| S], S] est la représentation de la chaîne abc.
Analyse syntaxique(cont. 3) Pratiquement, la représentation que nous adoptons en Prolog ne permet qu’une seule application pour une -expression. D’un point de vue mathématique, la concaténation des listes X = S.M1 o S et Y = S.M2 o S est Z = S.M2 o S Z est le résultat de l’application de X sur Y.
Analyse syntaxique(cont. 4) La concaténation devient: concat1([C1, S1], [C2, S2], [C, S]) :- C = C1, S1 = C2, S = S2. et d’une façon générale (n 0): concatn([C, S1], [S1, S2], … [Si-1 , Si], [Si , Si+1], …[Sn, S], [C, S]). Exemple: analyse_A([C, S]) :- analyse_X([C, S1]), analyse_Y([S1, S]). analyse_X([S, S]). analyse_X([C, S]) :- mot(C, a, S1), analyse_X([S1, S2]), mot(S2, b, S). analyse_Y([S, S]). analyse_Y([C, S]) :- mot(C, c, S1), analyse_Y([S1, S]). où: mot([X|S], X, S). Exercice: ecrire la concatenation sous la forme d’une fonctionelle
Analyse syntaxique(cont. 5) analyse_A([C, S]) se lit: vrai si C débute par une dérivation du non-terminal A et se termine par S. Remarque: analyse_A([[a, b, c], []]) est vrai si abc est une dérivation de A. Note: la chaîne vide est représentée formellement par la fonction identité, soit S.S.
Analyse syntaxique(cont. 6) Dans le cas général, pour la règle hors contexte: X0 à X1 … Xn, n 0 on obtient la clause: analyse_ X0([C, S]) :- p1(C1,S1), …, pn(Cn,Sn). avec C1 = C, Sn = S Et si Xi est non terminal, alors pi(Ci, Si) est: analyse_ Xi([Ci, Si]) avec Ci = Si-1 et S0= C si Xi est terminal, alors pi(Ci, Si) est: mot(Ci, Xi, Si) avec Ci = Si-1
Traduction Principe de composition (dit aussi de FREGE): la valeur d’un tout est une fonction de la valeur de ses composants. Peut sembler trivial. En fait une méthode de construction de définitions (et du coup de programmes) très utile. Exemple: on considère les expressions décrites par la grammaire G suivante d’axiome E: E à T E à T + E T à F T à F * T F à a F à b F à c F à ( E )
Traduction(cont. 1) Il s’agit de définir la traduction d’une telle expression dans le langage des termes A suivants: a, b, c, plus2(A1, A2), mul2(A1, A2) où A1 et A2 sont de type A. Donc de définir le prédicat tradE_A([C, S ], A) : vrai si C débute par une dérivation de E dont la traduction est A et se termine par S. tradE_A([C, S ], A) :- tradT_A([C, S ], A) . tradE_A([C, S ], A) :- tradT_A([C, S1 ], A1), mot(S1, +, S2), tradE_A([S2, S ], A2), A=plus2(A1, A2). tradT_A([C, S ], A) :- tradF_A([C, S ], A) . tradT_A([C, S ], A) :- tradF_A([C, S1 ], A1), mot(S1, *, S2), tradT_A([S2, S ], A2), A=mul2(A1, A2). tradF_A([C, S ], a) :- mot(C, a, S). … tradF_A([C, S ], A):- mot(C, ‘(, S1). tradE_A([S1, S2], A), mot(S2, ‘), S).
Traduction(cont. 2) Le principe de composition s’applique sans difficulté. La décomposition est donnée par les règles de grammaire. Les valeurs des expressions termes et facteurs sont des arbres. La recomposition des valeurs est donné par construction de nouveaux arbres. Un programme se déduit aisément de cette spécification pour une requête où la taille de la chaîne C est donnée. En effet, la démonstration termine car la taille de C diminue strictement pour des démonstrations successives portant sur un même prédicat. Nous procédons dans ce qui suit à une étude montrant la nécessité de revenir sur la décomposition (ie déterminer de nouvelles grammaires pour le même langage) et sur la notion de valeur à associer à un composant (abstraction comme dans les grammaires de Montague)
Traduction(cont. 3) Les opérateurs que nous avons considérés (+ et *) sont associatifs ((a + b) + c = a + (b+ c)). Introduisons l’opérateur - qui ne l’est pas et le terme moins2(A1, A2) dans le langage des termes. Une idée immédiate consiste à remplacer + par - (et plus2 par moins2). E à T E à T - E avec tradE_A([C, S ], A) :- tradT_A([C, S ], A) . tradE_A([C, S ], moins2(A1, A2)) :- tradT_A([C, S1 ], A1), mot(S1, -, S2), tradE_A([S2, S ], A2). Ce n’est pas bonne idée. En effet tradE_A([[a, -, b, - , c], []], A) donne A = moins2(a, moins2(b, c)) qui n’est pas la bonne traduction, mais celle de a – (b – c). Rappel: C’est opérateur le plus à gauche qui a la plus forte priorité.
Traduction(cont. 4) La méthode consiste à reprendre le principe de composition et à rechercher une nouvelle décomposition, c’est-à-dire une grammaire différente, mais toujours pour le même langage. Un bonne décomposition est la suivante: E à T E à E - T avec tradE_A([C, S ], A) :- tradT_A([C, S ], A) . tradE_A([C, S ], moins2(A1, A2)) :- tradE_A([C, S1 ], A1), mot(S1, -, S2), tradT_A([S2, S ], A2). Malheureusement, cette fois, c’est le démonstrateur (+ notre représentation pour les chaînes) qui flanche: tradE_A([[a, -, b, - , c], []], A) ne termine pas, comme c’est la cas pour les analyseurs descendants, à cause de la récursivité à gauche (sur E).
Traduction(cont. 5) Une autre grammaire doit donc être construite avec les caractéristiques suivantes: non récursive à gauche, mettant en évidence l’opérateur – et son opérande droit. La grammaire suivante (où RT, pour « reste de terme », est un nouveau non-terminal) convient: E -> T RT RT -> RT -> - T RT Le traducteur qui s’en déduit pour E: tradE_A([C, S ], A) :- tradT_A([C, S1 ], A1), tradRT_V([S1, S ], V), composition(A, A1, V). pose naturellement: - la question de la nature de la valeur V d’un reste de terme et - celle la composition des valeurs A1 et V qui doit donner celle d’une expression, c’est-à-dire A.
Traduction(cont. 6) Un exemple dans un autre domaine permet d’éclairer la démarche pour déterminer V et la composition recherchée. En linguistique, on se pose typiquement le problème de la traduction de groupe nominaux du type: La maison qui est belle dans un langage logique, en l’occurrence ici en la formule atomique: belle(maison). Le problème rencontré est similaire au nôtre dans la mesure où la décomposition fait apparaître la proposition relative (le sujet est inconnu comme le premier opérande du premier – dans un reste de terme). La solution est de donner à une subordonnée la valeur d’une principale dans laquelle le référent (ici maison) est « abstrait » (ie inconnu): X. belle (X). La composition est alors l’application de cette fonction sur le référent, soit: (X. belle (X))(maison) =belle(maison)
Traduction(cont. 7) Comme dans le cas des « difference lists », une lambda-expression X.F.est représentée en Prolog par un couple : nous le notons ici lambada[X, F]. L’application, comme on l’a vu, ne peut avoir lieu qu’une fois (ici, comme dans le cas des « difference lists » ce n’est pas gênant). On obtien, tradE_A([C, S ], A) :- tradE_A([C, S1 ], A1), tradRT_V([S1, S ], lambada(X, A2)), A = A2, X = A1. De la même façon: tradRT_A ([C, S ], lambada(X, X)). tradRT_A ([C, S ], lambada(Z, A)) :- mot(C, -, S1), tradE_A([S1, S2], A1), tradRT_V([S2, S ], lambada(X, A2)), A = A2, X = moins(Z, A1).
Traduction(cont. 7) Remarque1: dans cette dernière règle la valeur de Z. A est le résultat de l’application de X. A2 à la valeur du reste de terme - T, c’est-à-dire Y. A1. Exercice: Soit X. A2 = X. moins(X, c), Y. A1= Y. moins(Y, b). Déterminer Z. A. Remarque2: la règle définissant tradE_A s’écrit aussi tradE_A([C, S ], A2) :- tradE_A([C, S1 ], A1), tradRT_V([S2, S ], lambada(A1, A2)). qui fait apparaître une exécution qui n’est pas celle naturellement issue du principe de composition. Il n’y a pas contradiction: celui-ci est utilisé à des fins de spécification.
6. Contraintes numériques Contraintes linéaires: relations: =, >, dif(X, Y) opérations: +, -, *, / Restriction: dans un produit X*Y, X ou Y doit être connu. Exemple: nbpattestêtes(Chats, Oiseaux, Np, Nt):- Np = 4* Chats + 2* Oiseaux, Nt =Chats + Oiseaux,. Exercice: réponses aux requêtes: nbpattestêtes(Chats, Oiseaux, 16, 5). nbpattestêtes(Chats, Oiseaux, 3, 1).
Contraintes + heuristique d’énumération Illustration de la méthodologie contraintes + heuristiques d’énumération: le cas de la recherche des solutions entières. Exemple ( cryptogramme): Déterminer S, E, N, D, M O, R, Y entiers de 0 à 9 tels que l’addition: S E N D + MORE ------------- M O N E Y soit vérifiée.Il s’agit de définir solution_crypt([S, E, N, D, M, O, R, Y]) vrai si les variables S, E, N, D, M, O, R et Y répondent aux conditions précédentes.
Contraintes + heuristique d’énumération (cont. 1) solution_crypt([S, E, N, D, M, O, R, Y]) :- tous_differents([S, E, N, D, M, O, R, Y]), K1 = 1000*S + 100*E + 10*N + D, K2 = 1000*M + 100*O + 10*R + E, K3 = 10000*M + 1000*O + 100*N + 10*E + Y, K3 = K1 + K2, dif(S, 0), dif(M,0), /* fin de la partie contraintes, suit l’énumération*/ enumeration([S, E, N, D, M, O, R, Y]). Exercice: trouver un « bon » (efficace) ordre dans lequel énumérer (voir ci-après) les variables (qui n’est pas forcément S, E, N, D, M, O, R, Y). C’est tout l’art de déterminer une heuristique (méta-connaissance). Par exemple, en énumérant d’abord les variables intervenant dans le plus grand nombre de contraintes.
Contraintes + heuristique d’énumération (cont. 2) tous_differents([]). tous_differents([X | L]) :- 0 X, X 9, different(X, L), tous_differents(L) different(X, []). different(X, [Y | L]):- dif(X, Y), different(X, L). enumeration([]). enumeration([X | L]) :- enum0_9(X), enumeration(L). enum0_9(0). enum0_9(1). … enum0_9(9). Ajouter la solution de Vivier
Démonstration automatique Illustrations de l’utilisation d’un résolveur pour la démonstration automatique (en particulier de formules universelles). A noter: le démonstrateur Prolog permet de faire de la démonstration, mais, comme on l’a vu, elle n’est pas vraiment automatique, il faut quelquefois l’aider pour que la démonstration termine. Méthode pour démontrer X P(X) => Q(X): montrer que ¬(X P(X) => Q(X)) n’est pas satisfaisable. Soit montrer que X ¬( P(X) => Q(X)) ou encore X P(X) ¬ Q(X) n’est pas satisfaisable. Exemple: le théorème de Varigon: « pour tout quadrilatère ABCD, le quadrilatère des milieux M1 M2 M3 M4 (M1 (resp. M2, M3, M4) milieu de AB(resp. BC, CD, DA)) est un parallélogramme ». Il s’agit de montrer que la proposition »il existe un quadrilatère ABCD pour lequel le quadrilatère des milieux n’est pas un parallélogramme » n’ est pas satisfaisable.
Démonstration automatique. Fig. B I D J C M2 M3
Démonstration automatique (cont. 1) Sachant qu’un parallélogramme est caractérisé par le fait que ses deux digaonales se coupent en leur mieux, il vient: varignon :- neg_par_echec(non_varignon_possible). non_varignon_possible: - milieu(A, B, M1), milieu(B, C, M2), milieu(C, D, M3), milieu(D, A, M4), milieu(M2, M4, J), milieu(M1, M3, I), dif(I, J) /* négation de la conclusion*/. milieu([X1, YI], [X2, Y2], [X, Y]) :- X = (X1 + X2) / 2, Y = (Y1 + Y2) / 2. Bien noter la nécessité que les équations posées soient linéaires (c’est le cas ici).
Démonstration automatique (cont. 2) Autre exemple: montrer que toute suite entière telle que ui+2= |ui+1 | - ui a une période de 9. periode9 :- neg_par_echec(periode9_non_possible). periode9_non_possible :- size(11, S), bonne_suite(S), size(2, S1), size(7, S2), size(2, S3), S = S1 o S2 o S3, dif(S1, S3) /* négation de la conclusion*/. bonne_suite([X1, X0]). bonne_suite(S o [X2, X1, X0]) :- val_abs(X1, X1p), X2 = X1p – X0, bonne_suite(S o [X2, X1]). val_abs(X, X) :- X 0. val_abs(X, -X):- X < 0.
7. Arbres infinis rationnels Introduits naturellement par des équations au point fixe sur les arbres. Exemple: X = f(1, X) Deux approches sont possibles: on admet que ces systèmes ont des solutions: des arbres infinis dits rationnels dans la mesure où ils possèdent un nombre fini de sous-arbres. Exemple: dans le cas précédent X admet deux sous-arbres : 1 et X. -on rejette ces systèmes pour des raisons théoriques (intuitivement, il devient possible qu’un système cohérent sur les arbres ne le soit pas si on donne des significations aux foncteurs: f(X, 4) = f(3, Y) est cohérent si f est interprété par + ou par *, ce n’est pas le cas pour X = f(1, X)). Ce rejet est coûteux: il faut vérifier que chaque unification ne donne pas lieu à de telles équations.
Arbres infinis rationnels. Fig. X = f 1 f 1 1 … Grâce aux arbres infinis on dispose en Prolog IV des graphes finis comme des « valeurs à part entière », c’est-à-dire manipulable comme des touts f 1
Arbres infinis rationnels (cont. 1) Il existe bien sur des arbres infinis non rationnels. Exemple: f g f 1 g f g 1 … ….
Arbres infinis rationnels (cont. 2) Illustrations: - d’une part, de représentation à l’aide de graphe fini et d’utilisation (grammaire et analyse syntaxique) - d’autre part, de la construction de graphes finis et de la terminaison de parcours (substitution appliquée à un arbres infini rationnel)
Grammaire et analyse syntaxique ou Exemple de graphe fini: à un non terminal est associé un arbre ou et à une règle un arbre et: E à T E à T + E T à F T à F * T F à a F à b F à c F à ( E ) et et ou + et et ou * et et et et a c b ( )
Grammaire et analyse syntaxique(1) Cet arbre est E, solution du système suivant: E = ou (et(T), et(T, +, E)), T = ou (et(F), et(F, *, T)), F= ou(et(a), et(b), et(c), et(‘(‘, E, ‘)’)). L’objectif consiste à construire: analyse_G(G, [C, S]): vrai si C commence par la dérivation du nom terminal G (représenté par un arbre infini) et se termine par S. Exemple: analyse_G(E, [[a, +, b, * , c], []]) est vrai. Faire remarquer l’intérêt de ces objets qui sont solutions d’équations
Grammaire et analyse syntaxique(2) Les sous-arbres sont soit une feuille, soit de la forme ou(…) , soit de la forme et(…). On utilise la contrainte =.. telle que: f(X1, …, Xn) =.. [f, X1, …, Xn] analyse_G(G,[C, S]) :- leaf(G), mot(C, G, S), dif(G, et). analyse_G(G,[C, S]) :- G =.. [ou | [R|L_r]], analyse_G(R, [C, S]). analyse_G(G,[C, S]) :- G =.. [ou | [R|L_r]], Gp =..[ou | L_r], analyse_G(Gp, [C, S]). analyse_G(G,[S, S]):- G =.. [et | []]. analyse_G(G,[C, S]) :- G =.. [et | [E|L_e]], analyse_G(E, [C, S1]), Gp =..[et| L_e], analyse_G(Gp, [ S1, S]). NB: il y a terminaison si la taille de C est connue Faire remarquer qu’il y a terminaison, même s’il y des chemins circulaires dans le graphe (c’est le cas ici). La terminaison est assurée par la taille connue de la chaîne.
Substitution Il s’agit de donner une méthode assurant la terminaison dans le cas où le graphe est parcouru et contient des cycles. Exemple: subst(A1, A2, X, Y): vrai si l’arbre A2 est l’arbre A1 dans lequel les feuilles X sont remplacées par Y. On suppose que les arbres sont de la classe A qui sont des feuilles ou de la forme f(A1, A2) où A1et A2 sont des arbres de la classe A. Exemple: subst(f(a, b), f(b, b), a, b) est vrai. En supposant que A1 et A2 sont finis, on obtient: subst(A1, A2, X, Y):- leaf(A1), leaf(A2), A1= X, A2 = Y. subst(A1, A2, X, Y):-l eaf(A1), leaf(A2), dif(A1, X), A2 = A1. subst(A1, A2, X, Y):- A1 = f(A11, A12), A2 = f(A21, A22), subst(A11, A21, X, Y), subst(A12, A22, X, Y).
Substitution (cont. 1) A1 = f(a, f(A1, b)), subst(A1, A2, b, a). f f f
Substitution (cont.2) Si A1 et A2 peuvent être infinis, il est nécessaire d’introduire une liste d’ancêtres L_a pour obtenir des chemins finis: la décomposition d’un arbre n’est considérée que si cet arbre n’appartient pas à L_a. subst(A1, A2, X, Y):- substp(A1, A2, X, Y, []). substp(A1, A2, X, Y, L_a):- leaf(A1), leaf(A2), A1= X, A2 = Y. subspt(A1, A2, X, Y, L_a):- leaf(A1), leaf(A2), dif(A1, X), A2 = A1. substp(A1, A2, X, Y, L_a):- membre([A1, A2], L_a). substp(A1, A2, X, Y, L_a):- non_membre([A1, A2], L_a), A1 = f(A11, A12), A2 = f(A21, A22), substp(A11, A21, X, Y, [[A1, A2] | L_a]), substp(A21, A22, X, Y, [[A1, A2] | L_a]). NB: On remarque qu’il y a terminaison si la profondeur de A1 où celle de A2 est connue.
Unification et arbres infinis rationnels L’unification de deux arbres infinis nécessite un algorithme étendu. Exemple: il faut détecter la satisfiabilité du système: X = f(1, X), Y = f(1, f(1, Y)), X = Y Un algorithme possible conserve les associations des sous-arbres (gauches et droits) déjà considérés comme des couples (arbre-g, arbre-d). Chaque fois qu’un sous-arbre gauche est considéré à nouveau, c’est son arbre droit dans l’association qui est considéré pour l’unification. Exemple: pour le système précédent, on donne les couples successifs (arbre-g, arbre-d) mis en jeu successivement.
Unification et arbres infinis rationnels (cont.1) Y = 1 X = 3 f 1 f 1 2 1 4 3 2 5 1 4 5
8. Suspension de processus Il s’agit de la quatrième règle de dérivation pour produire un programme à partir d’une spécification. L’idée consiste à permettre un « cadencement » entre des démonstrations pour les terminer plus rapidement. La nouvelle structure de contrôle est: freeze(X, B): -vrai si B est vrai -retarde la démonstration de B si X n’est pas « connu ». X est dit connu si: l’étiquette (ie le foncteur) de X est connu et si on sait si les nombre de feuilles de X est nul ou non. Exemples: dans les cas suivants X est connu: X = 1, X = [], X = [E| L] Les démonstrations dépendantes de X sont prioritaires si X devient connu. Exemple: freeze(X, B), B1, X = 3, B2. La démonstration de B est prioritaire par rapport à celle deB2. Entre deux démonstrations dépendantes de X, la priorité n’est pas définie. Dire qu’il s’agit d’une version de quasi-parallélisme (plusieurs processus, un seul processeur)
Suspension de processus (cont. 1) Exemple: même_mot_feuilles(A1, A2): - mot_feuilles(A1, M), mot_feuilles(A2, M). mot_feuilles(A2, M): vrai si M est le mot des feuilles (liste des feuilles de gauche à droite) de l’arbre A. On suppose que les arbres sont de laclasse A qui sont des feuilles ou de la forme f(A1, A2) où A1et A2 sont des arbres de la classe A. mot_feuilles(A, [A]) :- leaf(A). mot_feuilles(f(A1, A2), [A1| M]) :- leaf(A1), mot_feuilles(A2, M). mot_feuilles(f(f(A11, A12), A2), M) :- mot_feuilles(f(A11, f(A12, A2)), M) .
Suspension de processus (cont. 2) Une démonstration de même_mot_feuilles(A1, A2) où A1 et A2 sont connus procède par production et vérification (« generate and test ») de M. Elle peut être très coûteuse si par exemple, M est très grand et si les deux arbres diffèrent dès la première feuille. Exemple: même_mot_feuilles(f(a, f(b, c)), f(f(b, b), c)) La démonstration échoue seulement après que le mot des feuilles de f(a, f(b, c)) (soit [a, b, c]) ait été totalement produit alors que celui-ci diffère du mot des feuilles du second arbre (soit [b, b, c]) par son premier élément. Il serait plus efficace de considérer ces deux mots de feuilles de façon symétrique en comparant leurs deux premiers éléments, puis les deux suivants…etc, c’est-à-dire en cadençant les démonstrations des formules mot_feuilles(f(a, f(b, c)), ) et mot_feuilles(f(f(b, b), c))).
Suspension de processus (cont. 3) La méthode de dérivation est basée d’une part sur l’identification d’un producteur et d’un ou plusieurs consommateurs, d’autre part sur l’activation du (ou des) consommateur(s) une fois la production d’un élément (notion à préciser par le concepteur) faite et la reprise du producteur après ces consommations. Cette méthode est illustrée sur un exemple artificiel trivial. Soit prod(D, T) le producteur (produisant une liste T à partir d’une liste donnée D et conso(T, R) le consommateur de T fournissant en résultat la liste R. Si p(D1, T1) et c(T1, R1) modélisent respectivement les productions et consommations élémentaires, on obtient: programme :- prod(D, T), conso(T, R). prod([],[]). prod([D1 | D], [T1| T]) :- p(D1, T1), prod(D, T). conso([],[]). conso([T1 | T], [R1| R]) :- c(T1, R1), conso(T, R).
Suspension de processus (cont. 4) La transformation vise simplement mettre en attente de T le consommateur (et à le déclencher d’abord) et à laisser le producteur le reprendre. Concernant l’exemple précédent, pour montrer que c’est au concepteur de décider ce qu’est une production élémentaire, on décide que la production élémentaire est accomplie après la démonstration de p(D1, T1) . On obtient (les transformations sont indiquées en rouge). programmep :- consop(T, R), prodp (D, T). consop(T, R) : - freeze(T, conso(T, R)). prodp([],[]). prodp([D1 | D], M) :- p(D1, T1), M = [T1|T], prodp(D, T). conso([],[]). conso([T1 | T], [R1| R]) :- c(T1, R1), consop (T, R).
Suspension de processus (cont. 5) L’application de la méthode à meme_mot_feuilles, en choisissant mot_feuilles(A1, M) comme producteur donne: même_mot_feuillesp(A1, A2): - mot_feuillesp (A2, M), mot_feuillespp(A1, M). mot_feuillesp(A2, M) :- freeze(M, mot_feuilles(A2, M)) mot_feuilles(A, [A]) :- leaf(A). mot_feuilles(f(A1, A2), [A1| M]) :- leaf(A1), mot_feuillesp(A2, M). mot_feuilles(f(f(A11, A12), A2), M) :- mot_feuilles(f(A11, f(A12, A2), M) . NB. On note que dans la troisième règle, il n’est pas nécessaire de placer le consommateur en attente.
Suspension de processus (cont. 6) La production élémentaire demande que la première feuille du mot des feuilles du producteur soit connue: mot_feuillespp(A, [A]) :- leaf(A). mot_feuillespp (f(A1, A2), Mp) :- leaf(A1), Mp = [A1|M], mot_feuillespp (A2, M). mot_feuillespp (f(f(A11, A12), A2), M) :- mot_feuillespp (f(A11, f(A12, A2), M) .
Suspension de processus (cont. 7) On peut aussi concevoir un producteur artificiel, dont le rôle est de cadencer les consommateurs. Exemple: un producteur fournit successivement les éléments du mot du feuille. même_mot_feuilles(A1, A2): - mot_feuillesp (A1, M), mot_feuillesp (A2, M), liste(M). liste ([]). liste([E|M]):- liste(M). Faire remarquer que la terminaison est assurée par la connaissance des arbres.