X. Satchmo
- Ein Regelformat zur Darstellung von Formeln der Prädikatenlogik erster Stufe
- Modellgenerierung
- Grundlegende Programme
- Modellgenerierung zum Theorembeweisen
- SATCHMO optimieren
- Logeleien
© 2001
François Bry
Dieses Lehrmaterial wird ausschließlich zur privaten Verwendung
angeboten. Eine nichtprivate Nutzung (z.B. im Unterricht oder eine
Veröffentlichung von Kopien oder Übersetzungen) dieses Lehrmaterials
bedarf der Erlaubnis des Autors.
Skolemisierung:
All x All y ( p(x,y) => Ex z q(x,z) )
wird umgeformt in:
All x All y ( p(x,y) => q(x, f(x,y)) )
wobei f
ein neues 2-stelliges Funktionssymbol
ist, d. h. ein 2-stelliges Funktionssymbol, das in keiner der
betrachteten Formeln vorkommt.
Ex x All y p(x,y)
wird umgeformt in:
All y p(f,y)
wobei f
ein neues 0-stelliges Funktionssymbol
ist, also eine Konstante. Neu heißt, daß die
Konstante f
in keiner der betrachteten Formeln
vorkommt.
F
nennt man die Skolemisierung von
F
, das Ergebnis der Umformung die Skolemform von
F
. Ist M eine Formelmenge, spricht man ebenfalls von der
Skolemform von M und meint damit die Menge aller Skolemformen der
Elemente von M.
Seien M eine Formelmenge und Msko eine Skolemform von M.
M ist (un)erfüllbar genau dann wenn Msko (un)erfüllbar ist.
All x ( p(x) /\ All y q(x,y) )
wird umgeformt in:
All x All y ( p(x) /\ q(x,y) )
Der Teil All x All y
wird Präfix,
der Teil p(x) /\ q(x,y)
Matrix genannt.
Konjunktive Normalform = Konjunktion von Disjunktionen.
All x All y ( p(x,y) => (q(x,y) /\ r(x,y))
)
wird umgeformt in:
All x All y ( [ ¬p(x,y) \/ q(x,y) ] /\ [ ¬p(x,y)
\/ r(x,y) ] )
Die Formelmenge
{ All x All y ( [ ¬p(x,y) \/ q(x,y) ] /\ [
¬p(x,y) \/ r(x,y) ] ) }
wird umgeformt in die Formelmenge:
{ All x All y [ ¬p(x,y) \/ q(x,y) ],
All x All y [ ¬p(x,y) \/ r(x,y) ] }
Die Quantifikation wird als implizit angesehen, was die folgende Klauselmenge ergibt:
{ [ ¬p(x,y) \/ q(x,y) ], [ ¬p(x,y) \/ r(x,y) ]
}
Die Variablen in der Klauselmenge werden so umbenannt, daß keine zwei Klauseln eine gemeinsame Variable haben.
Die Klausel
L1 \/ L2 ... \/
Lk
wird dargestellt als
C1 /\ ... /\ Cn =>
D1 \/ ... \/ Dm
wobei
Ci
und
Dj
positive Literale (Atome) sind,Lh
negativ), ist true
die Prämisse der
Implikation,Lh
positiv), ist false
die Konklusion der
Implikation,
C1 /\ ... /\ Cn => D1 \/ ... \/ Dm
Dj
vorkommt, kommt auch in einem
Ci
vor. Man kann jede Klauselmenge in eine
bereichsbeschränkte Klauselmenge transformieren. Für viele Anwendungen ist das
aber gar nicht nötig, weil die gegebene Formelmenge sowieso schon einer
bereichsbeschränkten Klauselmenge entspricht.
C1 /\ ... /\ Cn => D1 \/ ... \/ Dm
:- op(1200, xfx, --->).
C1, ..., Cn ---> D1 ; ... ; Dm
true
und false
werden in PROLOG so
dargestellt. Variablen werden als PROLOG-Variablen,
Funktionssymbole und Konstanten als PROLOG-Funktionssymbole und
PROLOG-Atome dargestellt.
true ---> dozent(francois).
true ---> semester ; semesterferien.
dozent(X), semester ---> liest(X).
dozent(X), semesterferien ---> forscht(X).
liest(X), forscht(X) ---> false.
true
|
dozent(francois)
semester ; semesterferien
|
+--------------------+--------------------+
| |
semester semesterferien
| |
liest(francois) forscht(francois)
true ---> p(a).
p(X) ---> q(X) ; p(f(X)).
true
|
p(a)
|
q(a) ; p(f(a))
|
+----------+----------+
| |
q(a) p(f(a))
|
q(f(a)) ; p(f(f(a)))
|
+----------+----------+
| |
q(f(a)) p(f(f(a)))
|
.
.
.
true ---> p(a).
p(X) ---> q(X) ; p(f(X)).
p(X), q(X) ---> false.
true
|
p(a)
|
q(a) ; p(f(a))
|
+----------+----------+
| |
q(a) p(f(a))
| |
false q(f(a)) ; p(f(f(a)))
|
+----------+----------+
| |
q(f(a)) p(f(f(a)))
| |
false .
.
.
true ---> p(a) ; q(b).
q(X) ---> s(f(X)).
r(X) ---> s(X).
p(X) ---> q(X) ; r(X).
q(X), s(Y) ---> false.
p(X), s(X) ---> false.
true
|
+----------------+---------------------+
| |
p(a) q(b)
| |
+-----------+-----------+ s(f(b))
| | |
q(a) r(a) false
| |
s(f(a)) s(a)
| |
false false
false
als Kopf, dann
gibt es ein Herbrand-Modell (möglicherweise ein
unendliches).
true
als Rumpf, dann
gibt es ein Herbrand-Modell (bestimmt durch die leere Menge von
Grundatomen).
:- op(1200, xfx, --->).
satisfiable :- (Body ---> Head),
Body,
not Head,
!,
disjunct(Atom, Head),
assume(Atom),
not false,
satisfiable.
satisfiable.
disjunct(Atom, (Atom ; _) ).
disjunct(Atom, (_ ; Rest) ) :- !, disjunct(Atom, Rest).
disjunct(Atom, Atom ).
assume(Fact) :- asserta(Fact).
assume(Fact) :- retract(Fact), !, fail.
Das Cut in der ersten Klausel von satisfiable
ist notwendig, damit die zweite Klausel nicht zu früh
aufgerufen wird, sondern erst, wenn alle vorhandenen
Klauseln in Implikationsform erfüllt sind.
In assume
kann der Aufruf von
asserta
nicht durch einen Aufruf von
assert
ersetzt werden: die "angenommenen"
Fakten sollen kellerartig gespeichert werden, so daß
zunächst das Faktum gelöscht wird, das zuletzt hinzugefügt
wurde.
true ---> p(a).
p(X) ---> p(f(X)).
p(X) ---> p(g(X)).
p(g(f(X))) ---> false.
p(a)
folgt
p(f(a))
, aus p(f(a))
folgt p(g(f(a)))
,
und aus p(g(f(a)))
folgt false
.
true
|
p(a)
|
p(f(a))
|
p(f(f(a)))
|
p(f(f(f(a))))
|
.
.
.
p(X) --->
p(g(X)).
nie berücksichtigt. Man sagt, daß das Programm
P93 nicht fair ist.
:- op(1200, xfx, --->).
satisfiable_level :- findall(Head,
( (Body ---> Head), Body, not Head ),
List_of_Heads),
not List_of_Heads = [],
!,
not member(false, List_of_Heads),
disjunct_tuple(List_of_Atoms, List_of_Heads),
assume_all(List_of_Atoms),
satisfiable_level.
satisfiable_level.
disjunct_tuple([], []).
disjunct_tuple([Atom|Atoms], [Head|Heads]) :-
disjunct(Atom, Head),
disjunct_tuple(Atoms, Heads).
disjunct(Atom, (Atom ; _) ).
disjunct(Atom, (_ ; Rest) ) :- !, disjunct(Atom, Rest).
disjunct(Atom, Atom ).
assume_all([]).
assume_all([Atom|Atoms]) :- Atom, !, assume_all(Atoms).
assume_all([Atom|Atoms]) :- assume(Atom), assume_all(Atoms).
assume(Fact) :- asserta(Fact).
assume(Fact) :- retract(Fact), !, fail.
satisfiable_level
scheitert, dann ist die
Klauselmenge unerfüllbar. Wenn die Klauselmenge
unerfüllbar ist, dann scheitert
satisfiable_level
nach endlich vielen Schritten.
Das Programm ist außerdem korrekt für die
Erfüllbarkeit, aber nicht vollständig für die
Erfüllbarkeit: Wenn satisfiable_level
erfolgreich terminiert, dann ist die Klauselmenge erfüllbar,
und die Fakten in der PROLOG-Datenbasis repräsentieren ein
Modell. Aber satisfiable_level
terminiert nicht
für jede erfüllbare Klauselmenge, zum Beispiel nicht
für P91. Es kann grundsätzlich auch
kein Programm geben, das in allen Fällen mit der korrekten
Antwort terminiert. Die Unerfüllbarkeit ist nur
semi-entscheidbar, aber nicht entscheidbar.
Oft ist man nicht an der (Un)Erfüllbarkeit interessiert, sondern an der sogenannten Folgerungsbeziehung zwischen Formelmengen oder Formeln der Prädikatenlogik erster Stufe: ob aus einer Axiomenmenge A eine Formel F folgt.
false
generiert wird, ist es günstig, die Klauseln mit Kopf false
vor allen anderen auszuwerten.
:- op(1200, xfx, --->).
satisfiable_1 :- contradiction, !, fail.
satisfiable_1 :- findall(Head,
( (Body ---> Head),
not Head = false, Body, not Head ),
List_of_Heads),
not List_of_Heads = [],
!,
disjunct_tuple(List_of_Atoms, List_of_Heads),
assume_all(List_of_Atoms),
satisfiable_1.
satisfiable_1.
contradiction :- (Body ---> false), Body.
% Folgende Prozeduren unveraendert:
disjunct_tuple([], []).
disjunct_tuple([Atom|Atoms], [Head|Heads]) :-
disjunct(Atom, Head),
disjunct_tuple(Atoms, Heads).
disjunct(Atom, (Atom ; _) ).
disjunct(Atom, (_ ; Rest) ) :- !, disjunct(Atom, Rest).
disjunct(Atom, Atom ).
assume_all([]).
assume_all([Atom|Atoms]) :- Atom, !, assume_all(Atoms).
assume_all([Atom|Atoms]) :- assume(Atom), assume_all(Atoms).
assume(Fact) :- asserta(Fact).
assume(Fact) :- retract(Fact), !, fail.
A
und andererseits eine Disjunktion
D1 ; D2
herleitbar, so ist die Herleitung von A
vorzuziehen. Grund dafür ist, daß die Herleitung von D1 ; D2
eine Fallunterscheidung nach D1
und D2
nach sich
zieht, gefolgt von einer Herleitung von A
in jedem der beiden
Fälle.
:- op(1200, xfx, --->).
satisfiable_2 :- contradiction, !, fail.
satisfiable_2 :- findall(Head,
( (Body ---> Head),
not Head = false, Body, not Head ),
List_of_Heads),
not List_of_Heads = [],
!,
disjunct_tuple(List_of_Atoms, List_of_Heads),
consequences(List_of_Atoms, Consequences),
append(List_of_Atoms, Consequences,
Extended_List_of_Atoms),
assume_all(Extended_List_of_Atoms),
satisfiable_2.
satisfiable_2.
consequences(List_of_Atoms, Consequences) :-
findall(Head,
( (Body ---> Head),
not Head = false, not Head = (_ ; _),
conjunct_rest(Atom, RestBody, Body),
member(Atom, List_of_Atoms),
RestBody,
not Head ),
Consequences).
conjunct_rest(C, R, (C,R) ).
conjunct_rest(C, (X,R), (X,Y,Z)) :- !, conjunct_rest(C, R, (Y,Z)).
conjunct_rest(C, R, (R,C) ) :- !.
conjunct_rest(C, true, C ).
% Folgende Prozeduren unveraendert:
contradiction :- (Body ---> false), Body.
disjunct_tuple([], []).
disjunct_tuple([Atom|Atoms], [Head|Heads]) :-
disjunct(Atom, Head),
disjunct_tuple(Atoms, Heads).
disjunct(Atom, (Atom ; _) ).
disjunct(Atom, (_ ; Rest) ) :- !, disjunct(Atom, Rest).
disjunct(Atom, Atom ).
assume_all([]).
assume_all([Atom|Atoms]) :- Atom, !, assume_all(Atoms).
assume_all([Atom|Atoms]) :- assume(Atom), assume_all(Atoms).
assume(Fact) :- asserta(Fact).
assume(Fact) :- retract(Fact), !, fail.
false
als null
Disjunktionsglieder.
C1, ..., Cn ---> D1 ; ... ; Dm
m : C1, ..., Cn ---> D1 ; ... ; Dm
m
die Länge des Klauselkopfes
ist. Zu dieser Darstellung wird ein zusätzlicher 2-stelliger
Infix-Operator :
verwendet.
:- op(1200, xfx, :), op(1190, xfx, --->).
satisfiable_3 :- contradiction, !, fail.
satisfiable_3 :- findall(Length : Head,
( (Length : Body ---> Head),
not Head = false, Body, not Head ),
List_of_Heads),
not List_of_Heads = [],
!,
quicksort(List_of_Heads, Sorted_List_of_Heads),
remove_lengths(Sorted_List_of_Heads, Heads),
disjunct_tuple(List_of_Atoms, Heads),
assume_all(List_of_Atoms),
satisfiable_3.
satisfiable_3.
contradiction :- (0 : Body ---> false), Body.
quicksort([], []).
quicksort([Head|Tail], Sorted) :-
partition(Tail, Head, Smalls, Bigs),
quicksort(Smalls, SortedSmalls),
quicksort(Bigs, SortedBigs),
append(SortedSmalls, [Head|SortedBigs], Sorted).
partition([], _Element, [], []).
partition([Head|Tail], Element, [Head|Smalls], Bigs) :-
precedes(Head, Element),
partition(Tail, Element, Smalls, Bigs).
partition([Head|Tail], Element, Smalls, [Head|Bigs]) :-
strictly_precedes(Element, Head),
partition(Tail, Eement, Smalls, Bigs).
precedes( (L1 : _Head1), (L2 : _Head2)) :- L1 =< L2.
strictly_precedes((L1 : _Head1), (L2 : _Head2)) :- L1 < L2.
remove_lengths([], []).
remove_lengths([_L:Head|Tail], [Head|New_Tail]) :-
remove_lengths(Tail, New_Tail).
% Folgende Prozeduren unveraendert:
disjunct_tuple([], []).
disjunct_tuple([Atom|Atoms], [Head|Heads]) :-
disjunct(Atom, Head),
disjunct_tuple(Atoms, Heads).
disjunct(Atom, (Atom ; _) ).
disjunct(Atom, (_ ; Rest) ) :- !, disjunct(Atom, Rest).
disjunct(Atom, Atom ).
assume_all([]).
assume_all([Atom|Atoms]) :- Atom, !, assume_all(Atoms).
assume_all([Atom|Atoms]) :- assume(Atom), assume_all(Atoms).
assume(Fact) :- asserta(Fact).
assume(Fact) :- retract(Fact), !, fail.
%% Welche Musikerin spielt heute welches Instrument?
%% ZEITmagazin Nr. 23, 3. Juni 1994
%% Die Musikerinnen Annette, Beate, Corinna, Esther
%% und Dagmar haben sich zu einem Blaeserquintett
%% zusammengefunden.
true ---> musikerin(corinna).
true ---> musikerin(esther).
true ---> musikerin(annette).
true ---> musikerin(beate).
true ---> musikerin(dagmar).
%% Heute spielt Corinna Fagott, aber Dagmar blaest nicht
%% Klarinette.
true ---> spielt(corinna, fagott).
spielt(dagmar, klarinette) ---> false.
%% Das Blaeserquintett ist besetzt mit den Instrumenten
%% Floete, Klarinette, Oboe, Fagott, Horn. Jede der Damen
%% beherrscht alle fuenf Instrumente.
musikerin(X) ---> spielt(X, floete);
spielt(X, horn);
spielt(X, oboe);
spielt(X, klarinette);
spielt(X, fagott).
%% Kein Instrument kann gleichzeitig von zwei Musikerinnen
%% gespielt werden.
spielt(Spielerin1, Instrument),
spielt(Spielerin2, Instrument),
not Spielerin1 = Spielerin2 ---> false.
%% Keine Musikerin kann gleichzeitig zwei Instrumente spielen.
spielt(Spielerin, Instrument1),
spielt(Spielerin, Instrument2),
not Instrument1 = Instrument2 ---> false.
%% Wenn Beate Oboe spielt, dann blaest Annette Klarinette.
spielt(beate, oboe) ---> spielt(annette, klarinette).
%% Spielt Esther nicht Horn, dann blaest Annette nicht Oboe.
spielt(annette, oboe) ---> spielt(esther, horn).
%% Spielen Esther Floete und Annette Klarinette, dann spielt
%% Dagmar nicht Horn.
spielt(esther,floete),
spielt(annette,klarinette),
spielt(dagmar,horn) ---> false.
%% Blaest Annette Klarinette, dann spielt Beate Oboe.
spielt(annette, klarinette) ---> spielt(beate, oboe).
%% Spielen Esther Floete und Annette Horn, dann blaest Dagmar
%% Fagott.
spielt(esther, floete),
spielt(annette, horn) ---> spielt(dagmar, fagott).
%% Spielen Dagmar Floete und Beate Klarinette, dann spielt
%% Annette Fagott.
spielt(dagmar,floete),
spielt(beate,klarinette) ---> spielt(annette,fagott).
%% Blaest Annette Floete, dann spielt Dagmar Horn.
spielt(annette,floete) ---> spielt(dagmar,horn) .
%% Wenn Corinna nicht Floete, aber Beate Klarinette spielt,
%% dann blaest Dagmar nicht Horn.
spielt(dagmar, horn),
spielt(beate, klarinette) ---> spielt(corinna, floete).
%% Spielt Esther Horn, dann blaest Annette Oboe.
spielt(esther, horn) ---> spielt(annette, oboe).
%% Welchen Tag verbringt Lisa mit wem?
%% (ZEITmagazin Nr. 6, 3. Februar 1995)
%% Lisa kennt vier junge Maenner: Axel, Bernd, Clemens und Dieter.
%% Jeden Tag der Woche verbringt sie mit einem von ihnen.
:- op(40, xfx, am).
Person1 am Tag,
Person2 am Tag,
not Person1 = Person2 ---> false.
true ---> axel am montag ;
bernd am montag ;
clemens am montag ;
dieter am montag.
true ---> axel am dienstag ;
bernd am dienstag ;
clemens am dienstag ;
dieter am dienstag.
true ---> axel am mittwoch ;
bernd am mittwoch ;
clemens am mittwoch ;
dieter am mittwoch.
true ---> axel am donnerstag ;
bernd am donnerstag ;
clemens am donnerstag ;
dieter am donnerstag.
true ---> axel am freitag ;
bernd am freitag ;
clemens am freitag ;
dieter am freitag.
true ---> axel am samstag ;
bernd am samstag ;
clemens am samstag ;
dieter am samstag.
true ---> axel am sonntag ;
bernd am sonntag ;
clemens am sonntag ;
dieter am sonntag.
%% Sonntags macht sie ihren Besuchsplan fuer die kommende Woche.
%% Den freilich verklausuliert sie ziemlich.
%% Fuer die naechste Woche sieht er so aus:
%% Wenn ich Dieter nicht am Montag besuche, dann bin ich am
%% Freitag bei ihm.
Person am montag,
not Person = dieter ---> dieter am freitag.
%% Falls ich den Samstag weder bei Axel noch bei Clemens verbringe,
%% dann begluecke ich Axel am Sonntag.
Person am samstag,
not Person = axel,
not Person = clemens ---> axel am sonntag.
%% Besuche ich Dieter nicht am Sonntag, dann bin ich am Montag bei
%% Bernd oder Clemens.
Person am sonntag,
not Person = dieter ---> bernd am montag ;
clemens am montag.
%% Bin ich am Donnerstag weder bei Bernd noch bei Clemens, so
%% besuche ich Clemens auch nicht am Samstag.
Person am donnerstag,
not Person = bernd,
not person = clemens,
clemens am samstag ---> false.
%% Verbringe ich den Mittwoch mit Axel oder Bernd, so begluecke ich
%% am Freitag Axel oder Clemens.
axel am mittwoch ---> axel am freitag ;
clemens am freitag.
bernd am mittwoch ---> axel am freitag ;
clemens am freitag.
%% Falls ich am Sonntag bei Axel bin, ist am Montag entweder Dieter
%% oder Axel an der Reihe.
axel am sonntag ---> dieter am montag ;
axel am montag.
%% Bin ich mit Bernd weder am Dienstag noch am Freitag zusammen,
%% dann sehe ich Axel nicht am Samstag.
Person1 am dienstag,
not Person1 = bernd,
Person2 am freitag,
not Person2 = bernd,
axel am samstag ---> false.
%% Wenn ich Bernd am Montag besuche, dann ist Axel am Sonntag an
%% der Reihe.
bernd am montag ---> axel am sonntag.
%% Sehe ich Axel nicht am Donnerstag, dann bin ich am Dienstag
%% nicht bei Dieter.
Person am donnerstag,
not Person = axel,
dieter am dienstag ---> false.
%% Verbringe ich den Mittwoch mit Dieter, dann werde ich am
%% Dienstag bei Axel, jedoch am Donnerstag nicht bei Dieter sein.
dieter am mittwoch ---> axel am dienstag.
dieter am mittwoch,
dieter am donnerstag ---> false.
%% Sehe ich Bernd nicht am Sonntag, so bin ich am Samstag bei
%% Dieter.
Person am sonntag,
not Person = bernd ---> dieter am samstag.
%% Bin ich am Donnerstag entweder mit Bernd oder mit Clemens
%% zusammen, dann bin ich am Sonntag bei Axel.
bernd am donnerstag ---> axel am sonntag.
clemens am donnerstag ---> axel am sonntag.
Letzte Änderung: 18. August 1998
(Beispiel:
Ex x p(a,x)
).