Grundläggande logik och modellteori Kapitel 12: Logikprogrammering Henrik Björklund Umeå universitet 16. oktober, 2014
Prolog Prolog har två klasser av formler. Atomära formler: country(sweden, 9000000). calc(plus(2,2), 4). har(peter, influensa). leq(x,x). Och regler: larger(x,y) :- country(x,sx), country(y,sy), Sx > Sy. leq(x,y) :- leq(x,z), leq(z,y). har_symptom(p,s) :- har(p,d), symptom(d,s). calc(neg(x), V) :- calc(x,vx), V is -Vx.
Prologs logik 1 Prologs formler är en begränsad predikatlogik. :- är en gammal visuell approximation av! Så har(peter, influensa) är en formel i en predikatlogik med relationen har (2). peter och influensa är konstantsymboler i signaturen. Vi skulle lika gärna kunna skriva p(a, b). har_symptom(p,s) :- har(p,d), symptom(d,s). är en formel i en predikatlogik med relationssymbolerna har_symptom (2), har (2) och symptom (2) och variabelsymbolerna P, D och S, nämligen: ( P)( D)( S)(har(P, D) symptom(d, S) har_symptom(p, S)) med andra symboler skulle vi skriva ( x)( y)( z)(p(x, y) q(y, z) r(x, z))
Prologs logik 2 Detta är helheten av Prologs predikatlogik i grunden, en konjunktion av: Faktum, atomära formler utan fria variabler, helt enkelt p(a, b) för p(a,b). Regler, all-kvantifierade implikationer, ( x)( y)((q(x) q(y)) p(x, y)) för p(x,y) :- q(x), q(y). Målklausulen, då resolution står på menyn är detta negationen av queryn (en konjunktion avatomära formler utan negationer). p(x,y) :- q(x) ; q(y). är bara syntaktiskt socker, vi kan dela upp det, och skriva t.ex. ( x)( y)(q(x) p(x, y)) ( x)( y)(q(y) p(x, y)). Men varför har Prolog just detta fragment? Det är ju en väldigt rigid formelstruktur, och har inga existentiella kvantifierare.
Horn-klausuler Definition: En litteral är positiv om den är en atom och negativ om den är en negerad atom. En Horn-klausul 1 är en klausul som maximalt innehåller en positiv litteral. Den har alltså formen p 1 p n q (där q = ifall klausulen inte innehåller någon positiv litteral alls), vilket alternativt kan skrivas q p 1,, p n. 1 Efter logikern Alfred Horn
Uppgift 1 Vilka av nedstående klausuler är Horn-klausuler? A 1 = A 5 = (p q r) A 2 = (p q) A 6 = (p) A 3 = ( p q) A 7 = ( q) A 4 = ( p q) A 8 = ( p q)
Lösning 1 Av klausulerna på föregående oh-bild var alla utom A 2, A 4 och A 8 Horn-klausuler.
Horn-klausuler i Prolog Alla formler som Prolog arbetar med blir Horn-klausuler: Faktum blir en enda positiv litteral. Regler skrivs lätt om: blir blir ( x)( y)(q(x) q(y) p(x, y)), ( x)( y)( (q(x) q(y)) p(x, y)), ( x)( y)( q(x) q(y) p(x, y)), vilket i klausulform är q(x) q(y) p(x, y). En Horn-klausul. Queryn till Prolog är en konjunktion av (icke negerade) litteraler. Vi kommer att använda resolution, så denna negeras, vilket ger en målklausul med bara negativa litteraler. Också på Horn-form.
Sidonotis: Negation i Prolog Som vi tidigare sett finns det en operator för negation i Prolog. Den beter sig dock inte alls på ett logiskt sätt. Prologs resolutionsmetod har ett litet specialfall där den kastar om värden för not. Den logiska grunden för Prolog ser allt som Horn-klausuler.
SLD-Resolution Varför den här begränsade logiken? Effektiv resolution! Låt P vara Horn-klausulerna för ett Prolog-program. Låt G 0 vara målklausulen (bara negativa atomära formler). 1. Låt i = 0 2. För G i = g 1... g n, välj någon g j. (Beräkningsregeln) 3. Välj en Horn-klausul k 1 k 2... k m från p, sådan att det finns en mgu σ i sådan att g j σ i = k 1 σ i. (Sökregeln) 4. Låt G i+1 = ( g 1... g j 1 g j+1... g n k 2... k m )σ i. 5. Låt i = i + 1 och gå till steg 2. Om någon G l = för något l är vederläggningsbeviset korrekt, svaret är σ 0 σ l.
Reglerna Valet av beräkningsregeln är irrelevant; förr eller senare lyckas alla vederläggningsbevis oberoende. Ett intuitivt argument: För att nå måste alla delar av målet motbevisas, det spelar ingen roll vilken del vi börjar med eller om vi byter mellan dem. Valet av sökregeln är viktigt; ett dåligt val kan orsaka oändlig sökning trots att ett ändligt bevis finns.
Exempel 1. q(x,y) :- p(x,y). 2. q(x,y) :- p(x,z), q(z,y). 3. p(b,a). 4. p(c,a). 5. p(d,b). 6. p(e,b). 7. p(f,b). 8. p(h,g). 9. p(i,h). 10. p(j,h). Tag queryn q(y,b), q(b,z). Går att bevisa. G 0 = q(y, b) q(b, z) Mål G 1 = p(y, b) q(b, z) 1 G 2 = q(b, z) 6, σ = {y e} G 3 = p(b, z) 1 G 4 = 3, σ = {z a}
Misslyckat exempel 1. q(x,y) :- p(x,y). 2. q(x,y) :- p(x,z), q(z,y). 3. p(b,a). 4. p(c,a). 5. p(d,b). 6. p(e,b). 7. p(f,b). 8. p(h,g). 9. p(i,h). 10. p(j,h). Tag queryn q(y,b), q(b,z). Går att bevisa. G 0 = q(y, b) q(b, z) Mål G 1 = q(y, b) p(b, z ) q(z, z) 2 G 2 = q(y, b) p(b, z ) p(z, z ) q(z, z) 2 G 3 = q(y, b) p(b, z ) p(z, z ) p(z, z ) q(z, z) 2.
Sökstrategier Så, vilken sökregel använder man lämpligen? Det finns inget enkelt sätt att generellt se vad som är rätt väg. Testa alla alternativ! Bredden först kommer alltid att fungera. Ha en bunt parallella G i -mängder. Använder mycket minne. Djupet först fungerar inte, man riskerar att loopa för evigt i en gren om saker händer i fel ordning Prolog gör det ändå! Ni har säkert sett Prolog hänga sig när det har chansen att upprepa sig. Litar på att programmeraren ordnar klausulerna på ett säkert sätt.
Mer Prolog-detaljer Prolog har alltså en stack som håller reda på hur den kan falla tillbaka i djupet-först-sökningen. Cut,!, tar bort backtracking-information. Prolog har också predikatet fail, som omedelbart misslyckas på en gren och försöker backtracka. SLD-resolutionen kan göras med en listig träd-datastruktur som gör att inte hela G i måste representeras. Som helhet: relativt sett mycket effektivt!