Programowanie logiczne a negacja Adrian Woźniak 12 stycznia 2006r.
SPIS TREŚCI Programowanie logiczne a negacja Spis treści 1 Wstęp 2 2 Wnioskowanie negatywnych informacji 2 2.1 Reguła CWA (Closed World Assumption)............. 2 2.2 Reguła NF (Negation as Failure).................. 3 3 Programy z negacją 3 3.1 Dopełnienie programu........................ 4 3.2 SLDNF rezolucja........................... 6 4 Negacja w Prologu 7 5 Bibliografia 9 1
1 Wstęp Programowanie logiczne a negacja 1 Wstęp SLD-rezolucja pozwala nam tylko na wywodzenie pozytywnych informacji z pozytywnych programów. W wielu okolicznościach użyteczna może być możliwość wywodzenia negatywnych informacji. Jeśli negatywne informacje mogą być wywodzone z pozytywnych programów, to naturalnym staje się rozszerzenie syntaktyki tych programów oraz przyjęcie negatywnych założeń. To jest klasa programów, zwana klasą ogólnych programów, w których negatywne literały mogą występować wewnątrz klauzul. Niestety, dokonując rozszerzeń programów prostych do programów ogólnych, może się pojawić wiele komplikacji. Poniżej omówię kilka najbardziej znanych dróg rozszerzania programów pozytywnych do ogólnych oraz konsekwencje i paradoksy jakie mogą powstać podczas korzystania w każdej z dróg. 2 Wnioskowanie negatywnych informacji Problem polega na tym, że chcielibyśmy zadawać pytania postaci x p(x), gdzie p(x) jest jakąś formułą atomową. Poniżej omówię dwie drogi, którymi można podążyć, aby na tego typu pytania można było odpowiadać. Są to jednak metody niemonotoniczne, co oznacza, że dodając nowe aksjomaty do reguł programowych może się zmniejszyć zbiór uprzednio wyprowadzanych twierdzeń. 2.1 Reguła CWA (Closed World Assumption) Niech A będzie ustaloną formułą atomową, a P programem logicznym. Reguła zamkniętości świata CWA brzmi następująco: CWA: Jeśli A nie jest logiczną konsekwencją P, to wywnioskuj A. Aby wywieść A musimy stwierdzić, że A nie jest logiczną konsekwencją P. To oznaczałoby, że nie istnieje SLD dowód dla A, czyli żadne SLD drzewo nie posiada gałęzi sukcesu. Tutaj mogą wystąpić dwa przypadki: SLD drzewo jest skończone (uzyskamy odpowiedź NIE i możemy wywnioskować A), SLD drzewo jest nieskończone (nie uzyskamy odpowiedzi). Ponadto problem, czy formuła atomowa jest logiczną konsekwencją zbioru formuł, jest nierozstrzygalny, zatem nie ma skończonego algorytmu do zweryfikowania tej przesłanki. Przykład: Rozważmy prosty program: student(joe) student(bill) student(jim) teacher(mary) 2
2.2 Reguła NF (Negation as Failure) Programowanie logiczne a negacja Przy pomocy reguły CWA możemy wnioskować, że student(mary). 2.2 Reguła NF (Negation as Failure) Zbiorem skończonych SLD porażek programu P nazywamy zbiór takich formuł atomowych A, dla których istnieje skończone SLD drzewo (dla P { A}) nie zawierające gałęzi sukcesu. Reguła negacji przez porażkę brzmi następująco: NF: Jeśli A należy do zbioru skończonych SLD porażek P ( A ponosi skończoną porażkę), to wywnioskuj A. W tym przypadku, jeśli ograniczamy się tylko do skończonych SLD dowodów możemy rozstrzygnąć, czy dana formuła atomowa A jest logiczną konsekwencją jakiegoś programu P. Przez to reguła NF jest słabsza od reguły CWA. W praktyce jednak trudno jest uzyskać coś więcej. Należy jednak uważać, ponieważ ta reguła obliczeniowa jest bezpieczna tylko wtedy, gdy każdy wybrany literał negatywny jest ustalony. Przykład: Rozważmy program: loves(x, y) mother(x), child of(y, x) loves(john, tom) loves(mary, john) mother(mary) child of(tom, mary) Dla ustalonych literałów, możemy wywieść, że na przykład mother(john), czy loves(tom, john), ponieważ za pomocą SLD rezolucji nie możemy wywieść mother(john), czy loves(tom, john). Stąd zastosowanie tutaj reguły NF jest jak najbardziej uzasadnione. Podobne wyniki otrzymamy stosując regułę CWA. Co się jednak stanie, gdy nie wszystkie literały będą ustalone? Rozważmy formułę loves(x, john). Aby zastosować regułę NF to loves(x, john) nie może być konsekwencją naszego programu. Jednak SLD rezolucja da nam odpowiedź θ = {x/mary}. Stąd nie możemy wywieść loves(x, john), a wiemy, że tom jest odpowiedzią. Dlaczego tak się dzieje? Odpowiedź jest prosta. Zadajemy pytanie x p(x) (chcemy poznać podstawienia x), lecz w praktyce oznacza to, że najpierw musimy sprawdzić wywodliwość p(x), co oznacza nic innego jak x p(x), a to jest równoważne zdaniu x p(x). Stąd zamiast odpowiedzi na pytanie Czy istnieje taki x, że nie prawdą jest p(x)? otrzymamy odpowiedź na pytanie Czy nie istnieje taki x, że p(x)?, co nie jest tym, o co nam chodziło. 3 Programy z negacją Klauzula programu (ogólna klauzula programu) z negacją wygląda następująco: A L 1,..., L n, 3
3.1 Dopełnienie programu Programowanie logiczne a negacja gdzie A to atom, a L 1,..., L n to literały (atomy, lub zaprzeczenia atomów). Program z negacją (ogólny program w logice) to skończony, niepusty zbiór klauzul programu z negacją. Ogólna klauzula negatywna (zapytanie) ma postać: L 1,..., L n. Przykład programu z negacją program porównujący zbiory: different(x, y) member(z, x), member(z, y) different(x, y) member(z, x), member(z, y) Programy z negacją przyczyniają się do wzrostu ekspresji języka programowania. Poniżej omówię najbardziej znane metody dowodzenia twierdzeń w programach z negacją. 3.1 Dopełnienie programu Intuicyjnie dopełnienie programu to przekształcenie implikacji w równoważność: P = {A B, A C,...} = A B C.... Niech = będzie nowym binarnym predykatem nie występującym w P. Zapis s t oznacza (s = t). Niech x 1,..., x n,... będą nowymi zmiennymi, nie występującymi w P. Kolejne kroki transformacji programu P, IFF(P): Krok 1: (usunięcie termów z nagłówków klauzul) Przekształcamy każdą klauzulę p(t 1,..., t n ) B 1,..., B m na p(x 1,..., x n ) (x 1 = t 1 )... (x n = t n ) B 1... B m. Krok 2: (wprowadzenie kwantyfikatorów egzystencjalnych) Zapisujemy każdą formułę p(x 1,..., x n ) F jako p(x 1,..., x n ) y1... yd F, gdzie y 1,..., y d to wszystkie zmienne oryginalnej klauzuli. Krok 3: (grupowanie klauzul) Niech p(x 1,..., x n ) E 1,..., p(x 1,..., x n ) E k to wszystkie formuły, w których nagłówkach występuje p. Zastępujemy je formułą p(x 1,..., x n ) E 1... E k. Jeśli E 1... E k jest puste, to zastępujemy przez true. Krok 4: (obsługa niezdefiniowanych predykatów) Dla każdego n argumentowego predykatu q nie występującego w nagłówku żadnej klauzuli P dodaj formułę q(x 1,..., x n ) false. Krok 5: (wprowadzenie kwantyfikatorów ogólnych) Zastąp każdą formułę p(x 1,..., x n ) E przez x1... xn (p(x 1,..., x n ) E). Krok 6: (wprowadzenie równoważności) W każdej formule zastąp przez. 4
3.1 Dopełnienie programu Programowanie logiczne a negacja Teoria równości Clarka CET (zakładamy, że stałe to symbole funkcyjne 0 argumentowe): 1. x = x. 2. f(x 1,..., x n ) (y 1,..., y m ) dla każdych symboli funkcyjnych f i g takich, że f g. 3. x 1 y 1... x n y n f(x 1,..., x n ) f(y 1,..., y n ) dla każdego symbolu funkcyjnego f. 4. x 1 = y 1... x n = y n f(x 1,..., x n ) = f(y 1,..., y n ) dla każdego symbolu funkcyjnego f. 5. x 1 = y 1... x n = y n (p(x 1,..., x n ) p(y 1,..., y n )) dla każdego predykatu p (łącznie z = ). 6. x t dla każdej zmiennej x oraz termu t takiego, że x t i x występuje w t. Teraz możemy zdefiniować dopełnienie programu P jako: comp(p ) = IFF(P ) CET. Przykład: Rozważmy ponownie program: loves(x, y) mother(x), child of(y, x) loves(john, tom) loves(mary, john) mother(mary) child of(tom, mary) Dopełnienie programu (IFF) wygląda następująco: v z (loves(v, z) x y (v = x z = y mother(x) child of(y, x)) (v = john z = tom) (v = mary z = john)) x (mother(x) x = mary) x y (child of(x, y) (x = tom y = mary)) Aksjomaty CET: {mary = mary, john = john, tom = tom}. Teraz możemy stwierdzić, że na przykład loves(john, mary) jest logiczną konsekwencją comp(p ). Należy jednak mieć świadomość następujących faktów: 1. Każdy program z negacją jest niesprzeczny, ale jego dopełnienie może być sprzeczne. Niech P : p p, wówczas IFF(P ): p p. 2. Dopełnienie programu zależy nie tylko od jego logicznej zawartości. Zauważmy, że p q oraz q p są logicznie równoważne (p q), natomiast ich dopełnienia p q oraz q p nie są równoważne. 5
3.2 SLDNF rezolucja Programowanie logiczne a negacja 3.2 SLDNF rezolucja SLDNF rezolucja to najprościej rzecz ujmując SLD rezolucja z dodaną regułą NF dla programów w logice. Stosuje się tutaj ograniczenie tylko do bezpiecznych reguł obliczeniowych, czyli takich, gdzie każdy wybrany literał negatywny jest ustalony. Jest to konieczne w celu zapewnienia poprawności. Niech P będzie programem z negacją, G ogólną klauzulą negatywną, a k 0. SLDNF dowód rzędu k G 0, G 1,..., G n ciąg rezolwent; C 1, C 2,..., C n ciąg wariantów klauzul P lub ; θ 1, θ 2,..., θ n ciąg podstawień mgu; dla k = 0: zwykły SLD dowód (bez literałów negatywnych); dla k > 0, niech i > 0: jeśli w G i 1 wybrano literał pozytywny, to G i jest rezolwentą G i 1 oraz C i przy użyciu θ i, jeśli w G i 1 = L 1,..., L j,..., L m wybrano ustalony literał negatywny L j = A j i dla A j istnieje SLDNF drzewo skończonej porażki rzędu k 1, to G i = L 1,..., L j 1, L j+1,..., L m, θ i = ɛ oraz C i =. SLDNF drzewo skończonej porażki rzędu k +1 (a) drzewo skończone, a każdy wierzchołek jest niepustą klauzulą negatywną (b) korzeń drzewa to klauzula G (c) niech G = L 1,..., L j,..., L m będzie korzeniem drzewa, a L j wybranym literałem: jeśli literał L j jest pozytywny, to dla każdej klauzuli P postaci A M 1,..., M l takiej, że L j i A są uzgadnialne, wierzchołek ma dziecko będące rezolwentą G oraz wariantu tej klauzuli jeśli L j to ustalony literał negatywny postaci A j oraz: dla P { A j } istnieje SLDNF drzewo skończonej porażki rzędu k, to ma jedno dziecko postaci L 1,..., L j 1, L j+1,..., L m dla P { A j } istnieje SLDNF dowód rzędu k, to wierzchołek jest liściem drzewa. Każda odpowiedź obliczona dla P {G} jest odpowiedzią poprawną dla comp(p ) {G}. Niech Q = L 1,..., L k oraz G = Q. 6
4 Negacja w Prologu Programowanie logiczne a negacja Jeśli istnieje SLDNF dowód dla P {G} z odpowiedzią obliczoną θ, to comp(p ) Qθ. Jeśli istnieje SLDNF drzewo skończonej porażki dla P {G}, to comp(p ) Q. Przykład: Niech naszym programem P będzie następujący program: odd(s(x)) odd(x), wówczas jego dopełnienie wygląda następująco: comp(p ) = ( z odd(z) x (z = s(x) odd(x))) CET. Stosując SLDNF rezolucję możemy stwierdzić, że zachodzi: comp(p ) odd(0), comp(p ) odd(s(0)), comp(p ) odd(s(s(0))),... Przykład SLDNF dowodu dla G = odd(s(s(s(0)))): G 0 0 : odd(s(s(s(0)))) G 0 1 : odd(s(s(0))) G 0 2 : SLDNF drzewo dla odd(s(s(0))): G 1 0 : odd(s(s(0))) G 1 1 : odd(s(0)) SLDNF drzewo dla odd(s(0)): G 2 0 : odd(s(0)) G 2 1 : odd(0) SLDNF drzewo dla odd(0): G 3 0 : odd(0) G 3 1 : (skończona porażka) G 2 2 : G 1 2 : (skończona porażka) 4 Negacja w Prologu W języku Prolog nie ma negacji logicznej. Został tylko udostępniony predykat not, który jest zdefiniowany następująco: not(p) :- p,!, fail. not(p) :- true. Jeśli p zawiedzie, to znaczy, że zostało przeszukane SLD drzewo, które okazało się skończenie zawodne. Predykat not nie jest dokładną implementacją negacji 7
4 Negacja w Prologu Programowanie logiczne a negacja logicznej. Problemy powstają dla wyrażeń ze zmiennymi. Jeśli p zależy od zmiennej, to not(p(x)) ma interpretację: nie istnieje X, że p(x). Innymi słowy not(p(x)) nie definiuje zbioru będącego dopełnieniem dla {X p(x)}. Proceduralna interpretacja dla not(p) jako testu dla nie posiadania własności p stosuje się tylko wtedy, gdy p nie zawiera nieukonkretnionych zmiennych w momencie wywoływania not(p). Przykład: Wywołajmy not z nieukonkretnioną zmienną: student(ewa). student(adam). student(jerzy). student(karol). profesor(karol). profesor(teofil). sprawny(x) :- student(x), not(profesor(x)). mlody(x) :- not(profesor(x)), student(x). Wywołanie?-sprawny(X). działa poprawnie, to znaczy generuje odpowiedzi: ewa, adam, jerzy. Wywołanie?-mlody(X). działa niepoprawnie, to znaczy generuje odpowiedź: no, a na przykład wywołanie?-mlody(jerzy). daje odpowiedź yes. Dzieje się tak dlatego, że semantyka not(p(x)) przy nieukonkretnionej zmiennej X to nieprawda, że istnieje X taki, że p(x), czyli dla każdego X nieprawda, że p(x). Przykład: Spójrzmy na kolejny przykład: zielony(jablko). czerwony(wisnia). nie_zielony(x) :- zielony(x),!, fail. nie_zielony(x).?-zielony(x). (odp. X=jablko) Co jest zielone??-nie_zielony(wisnia). (odp. yes) Czy wiśnia nie jest zielona??-nie_zielony(x). (odp. no) no zamiast spodziewanego yes. Dlaczego? Ponieważ zapytanie znaczy Czy nie istnieje zielone?, a nie Co jest nie zielone?. Problemy z negacją w Prologu: 1. W logice p p jest równoważna z p, ale w Prologu nie można potwierdzić :-p w oparciu o klauzulę programową p :- not(p). Otrzymamy wówczas nieskończoną pętlę. 8
5 Bibliografia Programowanie logiczne a negacja 2. Dwie klauzule z poniższego programu są łącznie równoważne z a: a :- p(x). a :- not(p(x)). Zastąpmy a przez p(x):!!!!!!. Wniosek jest oczywisty, że not(x) różni się od negacji logicznej. 3. W szczególności zawodzi prawo podwójnego przeczenia: member(a,[a _]) :-!. member(a,[_ C]) :- member(a,c). Zapytanie?-member(X,[a,b,c]),write(X). daje wyniki: a X=a Yes ale?-not(not(member(x,[a,b,c]))),write(x). natomiast: _0084 X=_0084 Yes Dlaczego? Ponieważ member(x,[a,b,c]) skutkuje, zatem not(member(x,[a,b,c])) zawodzi i zmienna X w wyniku nawrotu staje się na powrót nieukonkretniona (nawrót po porażce), zatem not(not(member(x,[a,b,c]))) skutkuje z nieoznaczoną wartością X. 5 Bibliografia 1. Krzysztof R. Apt, Roland Bol Logic Programming and Negation: A Survey, magazyn programowania logicznego, 1994. http://citeseer.ist.psu.edu/apt94logic.html 2. Ulf Nilsson, Jan Małuszyński Logic, Programming and Prolog (2ed), 2000. http://www.ida.liu.se/ ulfni/lpp/ 3. dr Mirosława Miłkowska Programowanie w logice, wykład monograficzny dla studentów, 2005. http://rainbow.mimuw.edu.pl/lp-zsi/ http://rainbow.mimuw.edu.pl/lp/ 9