• Aucun résultat trouvé

Implémentation de TaMed

Dans le document Bonnes démonstrations en déduction modulo (Page 151-155)

4.4 Implémentation

4.4.2 Implémentation de TaMed

La méthode des tableaux que nous avons implémentée est essentiellement celle de

RichardBonichonet Olivier Hermant(2006b) (cf. section 3.2.4), si ce n’est que nous

n’appliquonsrw que sur les propositions atomiques, ce qui revient à se placer en calcul

des séquences avec dépliage plutôt que dans le calcul des séquences modulo avec règle

de conversion explicite.

Les termes et les formules de la logique du premier ordre sont représentées par les

structures de donnéesOCaml

type term =

Var of string

| Fun of string * term list

type prop =

Atomic of string * term list

| True

| False

| Not of prop

| Or of prop * prop

| And of prop * prop

| All of string * prop * int

| Ex of string * prop * int

PourAlletEx, l’entier associé correspondant au nombre de fois auquel on a déjà appliqué

respectivement ∀ − ou −∃. Ces structures de données sont ancrées en TOM de façon

triviale.

On a également besoin d’équations pour les contraintes. Ces équations sont soit entre

termes, soit entre formules, d’où la structure

type eq =

Term of term * term

| Prop of prop * prop

Ce type servira également pour les règles de réécriture. On annote une formule par une

liste de méta-variable (représentées par des chaînes), une liste de contraintes, et une liste

de règles de réécriture déjà appliquée sur la formule. On a donc un type

type a_prop = prop * (string list * eq list * eq list)

Une branche est une liste de formules annotées, elle-même annotée par un booléen

indi-quant l’alternance de priorité entreγ etrw. Un tableau est une liste de branches.

Pour résoudre les contraintes, on a besoin d’un algorithme d’unification. Nous avons

utilisée pour cela une des stratégies proposées par ClaudeKirchneret Hélène

Kirch-ner (2001, section 3.2.4) pour résoudre des ensembles d’équations à unifier.

On écrit alors une fonctionclosurequi vérifie si le tableau en entier peut-être fermé,

ce qui est assez facile grâce au filtrage associative deTOM. En effet, il s’agit de collecter

toutes les propositions atomiques potentielles qui pourraient s’unifier, et on n’utilise pour

cela les motifs(_*,a(not(atomic(s,a1)),c(_,c1,_)),_*,a(atomic(s,a2),c(_,c2,_)),_*)

et(_*,a(atomic(s,a1),c(_,c1,_)),_*,a(not(atomic(s,a2)),c(_,c2,_)),_*). Pour

chacun des choix potentiels pour chaque branche, on essaie d’unifier le tableau en entier

en prenant soin de tenir compte des contraintes sur les formules (filtrées par c1 et c2

ci-dessus).

On peut alors écrire la méthode des tableaux. On utilise une table de hachage dans

laquelle on stocke à chaque étape le tableau, pour vérifier qu’on ne boucle pas, car dans

un tel cas on peut arrêter la méthode, puisqu’on ne fermera jamais le tableau. On a un

certain nombre d’étapes qui ferment une branche quand on n’a pas besoin d’unification :

(b1*,b((_*,a(True(),_),_*),_),b2*) -> { next (‘(b1*,b2*)) }

(b1*,b((_*,a(not(False()),_),_*),_),b2*) -> { next (‘(b1*,b2*)) }

(b1*,b((_*,a(p,_),_*,a(not(p),_),_*),_),b2*) -> { next (‘(b1*,b2*)) }

(b1*,b((_*,a(not(p),_),_*,a(p,_),_*),_),b2*) -> { next (‘(b1*,b2*)) }

D’autres étapes suppriment les branches et les formules inutiles ou redondantes :

(b1*,b,b2*,b,b3*) -> { next (‘(b1*,b,b2*,b3*)) }

(b1*,b((gamma*,a(False(),_),delta*),s),b2*) ->

(b1*,b((gamma*,a(not(True()),_),delta*),s),b2*) ->

{ next (‘(b1*,b((gamma*,delta*),s),b2*)) }

(b1*,b((gamma*,ap,eta*,ap,delta*),s),b2*) ->

{ next (‘(b1*,b((gamma*,ap,eta*,delta*),s),b2*)) }

On essaie ensuite de fermer le tableau à l’aide de la fonction closure définie

précé-demment. On peut alors appliquer les règles logiques (on n’indiquera que l’application

sur les formules non niées, le reste étant dual). L’ordre d’application traduira la

prio-rité définie par Richard Bonichon et Olivier Hermant (2006b). La fait de mettre la

branche modifiée à la droite du tableau permet de s’assurer de ne pas rester toujours

dans la même branche, ce qui revient à choisir en premier les branches qui auraient un

nombre minimal de formules si on gardaient les formules de départ.

(b1*,b((gamma*,a(not(not(p)),l),delta*),s),b2*) ->

{ (* suppression double négation *)

next (‘(b1*,b((gamma*,a(p,l),delta*),s),b2*)) }

(b1*,b((gamma*,a(or(p,q),l),delta*),s),b2*) ->

{ (* alpha *)

next (‘(b1*,b2*,b((gamma*,a(p,l),a(q,l),delta*),s))) }

(b1*,b(conc(gamma*,a(all(x,p,_),cs@c(l,_,_)),delta*),s),b2*) ->

{ (* delta *)

let e = Term(Var (‘x),

Fun(fresh_var "sk_",

List.rev_map (fun x -> Var x) (‘l))) in

let q = subst [e] (‘p) (* on substitue x par un terme de Skolem *)

next (‘(b1*,b2*,b((gamma*,a(q,cs),delta*),s))) }

(b1*,b((gamma*,a(and(p,q),l),delta*),s),b2*) ->

{ (* beta *)

next (‘(b1*,b2*,b((gamma*,a(p,l),delta*),s),

b(conc(gamma*,a(q,l),delta*),s))) }

On applique en prioritéγ devantrwqui si la branche est annotée partrueet si on ne l’a

pas appliqué plus delimfois,limétant un paramètre fourni à la méthode des tableaux.

(b1*,b(bg@(_*,a(ex(_,_,i),_),_*),true()),b2*) && i < lim ->

{ (* gamma *)

let nbg = do_gamma (‘bg) lim in

next (‘(b1*,b2*,b(nbg,false())))

}

do_gammaest une fonction qui applique Γ à toutes les formules de typeγ de la branche

(écrite en OCaml) :

let do_gamma bg lim =

let rec aux accu = function

| (Ex(x,p,i),(l,c,rr))::ps when i < lim ->

let y = fresh_var "v_" in

let e = Term(Var x, Var y) in

let q = subst [e] p

(* on substitue x par une méta-variable fraiche *)

and r = y::l, c, rr

(* on rajoute cette variable dans les contraintes *)

in aux ((Ex(x,p,i+1),(l,c,rr))::(q,r)::accu) ps

| (Not(All(x,p,i)),(l,c,rr))::ps when i < lim ->

(* cas dual *)

...

| ap::ps -> aux (ap::accu) ps

| [] -> List.rev accu

in aux [] (bg)

Dans la méthode des tableaux, il faut maintenant le code pourrw, qui utilise le

pa-ramètre rules qui contient la liste des règles de réécriture disponibles. narrow p r

surréduit une proposition pà l’aide d’une règle de réécriture r, qui retourne cette

pro-position et une ensemble de contraintes ou qui renvoie une exception Not_match si ce

n’est pas possible.

(b1*,b((gamma*,a(p@atomic(_,_),(l,c,rr)),delta*),_),b2*) ->

{ (* rw *)

List.iter

(fun r ->

if not (List.mem r rr)

then try

let q, nc = narrow (‘p) r in

let nrr = r :: rr in

next (‘(b1*,b2*,b((gamma*,delta*,

a(q,(l,nc,())),a(p,(l,c,nrr))),True())))

with Not_match -> () )

rules

}

Il faut ensuite remettre la décomposition γ au cas où on ait une branche annotée par

Pour définir la méthode des tableaux finale, on procède par approfondissement itératif

(en anglaisiterative deepening) : on applique la méthode pourlim = 0puis on augmente

progressivement la limite jusqu’à obtenir une démonstration.

Dans le document Bonnes démonstrations en déduction modulo (Page 151-155)