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)