210 likes | 225 Views
Logic Programming – Part 2. Lists Backtracking Optimization (via the cut operator) Meta-Circular Interpreters. Lists – Basic Examples. [] – The empty list [X,2,f(Y)] – A 3 element list [X|Xs] – A list starting with X. Xs is a list as well . Example - [3,5] may be written as [3|5|[]].
E N D
Logic Programming – Part 2 Lists Backtracking Optimization (via the cut operator) Meta-Circular Interpreters
Lists – Basic Examples • [] – The empty list • [X,2,f(Y)] – A 3 element list • [X|Xs] – A list starting with X. Xs is a list as well. • Example - [3,5] may be written as [3|5|[]]
Lists – CFG with Prolog Question: Which sentences can be constructed using this grammar? s -> npvp np -> det n vp -> v np| v det -> a | the n -> woman | man v -> shoots
Lists – CFG with Prolog Lets make relations out of it: s(Z) :- np(X), vp(Y), append(X,Y,Z).np(Z) :- det(X), n(Y), append(X,Y,Z).vp(Z) :- v(X), np(Y), append(X,Y,Z).vp(Z) :- v(Z).det([the]).det([a]).n([woman]).n([man]).v([shoots]). s -> np vp np -> det n vp -> v np| v det -> a | the n -> woman | man v -> shoots
Lists – CFG with Prolog • We can ask simple queries like: • Prolog generates entire sentences! s([a,woman,shoots,a,man]).true ?-s(X).X = [a,woman,shoots,a,woman] ; X = [a,woman,shoots,a,man] ;X = [a,woman,shoots,the,woman] ;X = [a,woman,shoots,the,man] ;X = [a,woman,shoots] … ?-s([the,man|X]). X = [shoots,a,woman] ;X = [shoots,a,man] ;X = [shoots,the,woman] …
Lists – CFG with Prolog • Question: Add a few rules to the grammar What should we change in the code? • Answer: we add the following code s -> npvp np -> det n | detadj n vp -> v np| v det -> a | the n -> woman | man v -> shoots adj -> vicious | marvelous np(Z) :- det(X), adj(W), n(Y), append([X,W,Y],Z). adj([vicious]). adj([marvelous]).
Lists – The date Relation • In this example we’ll work with dates • We assume, for simplicity that a date comprises of a week day and an hour • We define the possible week days and hours with lists: week_day(['Sun', 'Mon', 'Tue','Wed','Thu','Fri','Sat']). hour([0,1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]).
Lists – The date Relation • Q: How can we tell if hour 2 is before 9? • A: • A < relation isn’t really possible to implement.(More details in the PS document) • We can only do so by checking precedence in the lists above. week_day(['Sun', 'Mon', 'Tue','Wed','Thu','Fri','Sat']). hour([0,1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]).
Lists – The date Relation date([H,W]) :- hour(Hour_list), member(H, Hour_list), week_day(Weekday_list), member(W, Weekday_list). dateLT( date([_,W1]), date([_,W2]) ) :- week_day(Weekday_list), precedes(W1,W2,Weekday_list). dateLT( date([H1,W]), date([H2,W]) ) :- hour(Hour_list), precedes(H1,H2,Hour_list). Example queries: ?- date([1,'Sun']). true dateLT(date([5,'Mon']), date([1,'Tue'])). true
Lists – The date Relation • precedes is defined using append /2 precedes(X,Y,Z) :- append( [_,[X],_,[Y],_] , Z ). • Notice that the first argument is a list of lists • This version of append is a strong pattern matcher
Lists – Merging date Lists • Merge 2 ordered date-lists % Signature: merge(Xs, Ys, Zs)/3 % purpose: Zs is an ordered list of dates obtained % by merging the ordered lists of dates Xs and Ys. merge([X|Xs] , [Y|Ys] , [X|Zs]) :- dateLT(X,Y), merge(Xs, [Y|Ys] ,Zs). merge([X|Xs] , [X|Ys] , [X,X|Zs]) :- merge(Xs, Ys, Zs). merge([X|Xs],[Y|Ys],[Y|Zs]) :- dateLT(Y,X), merge( [X|Xs] ,Ys, Zs). merge(Xs,[ ], Xs). merge([ ],Ys, Ys). ?- merge( [date([5,'Sun']), date([5,'Mon'])], X, [date([2, 'Sun']), date([5,'Sun']), date([5, 'Mon'])]). X = [date([2, 'Sun'])]
merge([d1,d3,d5],[d2,d3],Xs) {X_1=d1,Xs_1=[d3,d5], Y_1=d2,Ys_1=[d3],Xs=[d1|Zs_1] } Rule 1 dateLT(d1,d2), merge([d3,d5], [d2,d3] ,Zs_1) merge([d3,d5], [d2,d3] ,Zs_1) Rule 1 – failure branch… Rule 2 – failure branch… dateLT(d2,d3), merge([d3,d5], [d3] ,Zs_2) merge([d3,d5], [d3] ,Zs_2) Rule 1 – failure branch… { X_3=d3,Xs_3=[d5],Ys_3=[], Zs_2=[d3,d3|Zs_3] } Rule 2 merge([d5], [] ,Zs_3) true { Xs_4=[d5], Zs_3=[d5] } Fact 4 Rule 3 – failure branch… Rule 2 – failure branch… { X_2=d3,Xs_2=[d5], Y_2=d2,Ys_2=[d3], Zs_1=[d2|Zs_2]} Rule 3 Rule 3 – failure branch…
Backtracking Optimization - Cut The cut operator (denoted ‘!’) allows to prune trees from unwanted branches. • A cut prunes all the goals below it • A cut prunes all alternative solutions of goals to the left of it • A cut does not affect the goals to it’s right • The cut operator is a goal that always succeeds
merge([d1,d3,d5],[d2,d3],Xs) Rule 3 – failure branch… dateLT(d1,d2), !, merge([d3,d5], [d2,d3] ,Zs_1) Rule 2 – failure branch… !, merge([d3,d5], [d2,d3] ,Zs_1) merge([d3,d5], [d2,d3] ,Zs_1) Example - Merge with Cut • In the merge example, only 1 of the 3 first rules can be true. There is no reason to try the others. • Modify rule 1: merge([X|Xs] ,[Y|Ys], [X|Zs]) :- dateLT(X,Y), !, merge (Xs, [Y |Ys],Zs).
Another Example • How many results does this query return? • Why does this happen? The query fits both rules 4 and 5 • How can we avoid this? Add cut to rule 4 ?- merge([],[],X) . X = []; X = []; No merge(Xs, [ ],Xs) :- !.
Meta-Circular Interpreter Version 1 % Signature: solve(Goal)/1 % Purpose: Goal is true if it is true when posed to the original program P. solve(true) :- !. solve( (A, B) ) :- solve(A), solve(B). solve(A) :- clause(A, B), solve(B). clause finds the first rule unifying with A with body B ?- clause( parent(X,isaac),Body). X = abraham Body = true ?- clause(ancestor(abraham, P),Body). P = Y, Body = parent(abraham, Y) ; P = Z, Body = parent(abraham, Y), ancestor(Y, Z)
solve(ancestor(abraham, P)) {A_1 = ancestor(abraham, P)} Rule 3 solve clause(ancestor(abraham, P), B_1), solve(B_1) { B_1 = parent(abraham, P), X_2 = abraham, Y_2 = P } Rule 1 ancestor { B_1 = parent(abraham,Y_2), ancestor(Y_2, P) }Rule 2 ancestor solve(parent(abraham, P)) solve(parent(abraham,Y_2), ancestor(Y_2, P)) {A_3 = parent(abraham,Y_2) B_3 = ancestor(Y_2, P} Rule 2 solve {A_3 = parent(abraham, P)} Rule 3 solve solve( parent(abraham,Y_2)), solve(ancestor(Y_2, P)) clause(parent(abraham, P), B_3), solve(B_3). {P = issac, B_3 =true} Fact 1 parent {A_4 = parent(abraham,Y_2)} Rule 3 solve solve(true) clause(parent(abraham, Y_2), B_4), solve(B_4) solve(ancestor(Y_2, P)) Fact 1 solve ! {Y_2 = issac, B_4 =true} Fact 1 parent solve(true) , solve(ancestor(issac, P)) {P = issac} true
Meta-Circular Interpreter Version 2 • In this version we control the goal selection order by using a stack of goals • Preprocessing – The given program is converted into a program with a single predicate rule containing only facts • Queries are checked against the new program
Meta-Circular Interpreter Version 2 % Signature: solve(Goal)/1% Purpose: Goal is true if it is true when posed to the original program P.1. solve(Goal) :- solve(Goal, []). % Signature: solve(Goal, Rest_of_goals)/21. solve([],[]).2. solve([],[G | Goals]):- solve(G, Goals).3. solve([A|B],Goals):- append(B, Goals, Goals1), solve(A, Goals1).4. solve( A, Goals) :- rule(A, B), solve(B, Goals). Sample converted program: %rule (Head, BodyList)/21. rule( member(X, [X|_] ), [] ).2. rule( member(X, [_|Ys] ), [member(X, Ys)] ).
solve(member(X, [a, b, c])) {Goal_1 = member(X, [a, b, c])} Rule 1 solve solve(member(X, [a, b, c]), []) { A_2 = member(X, [a, b, c],Goals_1 = [] } Rule 4 solve rule(member(X, [a, b, c], B_2), solve(B_2, []) { X_3=X,Ys_3=[b, c],B_2 = [member(X, [b,c])]}Rule 2 rule { X=a,X_3 = a, Xs_3=[b, c],B_2 = [] } Rule 1 rule solve([],[]) solve([member(X, [b,c])], []) Rule 1 solve { A_4= member(X, [b,c]), B_4=[], Goals_4=[] } Rule 3 rule true append([], [], Goals1_4), solve(member(X, [b,c]), Goals1_4). {X=a} { Goals1_4=[]} Rule of append solve(member(X, [b,c]), []). { A_5=member(X,[b,c]), Goals_5=[]} Rule 4 solve rule(member(X,[b,c]), B_5), solve(B_5, []) { X=b,X_6 = b,B_5 = [] } Rule 1 rule solve([],[]) Rule 1 solve true {X=b}