logic programming – part 2
DESCRIPTION
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|[]]. - PowerPoint PPT PresentationTRANSCRIPT
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 -> np vpnp -> det nvp -> v np | vdet -> a | then -> woman | manv -> shoots
Lists – CFG with PrologLets 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 vpnp -> det nvp -> v np | vdet -> a | then -> woman | manv -> shoots
Lists – CFG with PrologWe can ask simple queries like:
Prolog generates entire sentences!
s([a,woman,shoots,a,man]).yes
?-s(X). X = [the,woman,shoots,the,woman] ;X = [the,woman,shoots,the,man] ;X = [the,woman,shoots,a,woman] ;X = [the,woman,shoots,a,man] ;X = [the,woman,shoots] …
?-s([the,man|X]). X = [the,man,shoots,the,woman] ;X = [the,man,shoots,the,man] ;X = [the,man,shoots,a,woman] …
Lists – CFG with PrologQuestion: Add a few rules to the grammar
What should we change in the code?Answer: we add the following code
s -> np vpnp -> det n | det adj nvp -> v np | vdet -> a | then -> woman | manv -> shootsadj -> vicious | marvelous
np(Z) :- det(X), adj(W), n(Y), append([X,W,Y],Z).adj([vicious]).adj([marvelous]).
Lists – The date RelationIn this example we’ll work with
datesWe 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([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]).
Lists – The date Relation
Question: How can we tell if hour 2 is before hour 9?
Answer: 1. We can only do so by checking
precedence in the lists above2. A < relation isn’t really possible to
implement(There’s a more detailed answer in the PS document)
week_day(['Sun', 'Mon', 'Tue','Wed','Thu','Fri','Sat']).hour([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]).
Lists – The date Relation
Some queries:
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).date([1,'Sun']).true dateLT(date([5,'Mon']), date([1,'Tue'])).true
Lists – The date Relationprecedes is defined using append
/2
Notice that the first argument is a list of lists
This version of append is a strong pattern matcher
precedes(X,Y,Z) :- append( [_,[X],_,[Y],_] , Z).
Lists – Merging date ListsMerge 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)
true
merge([d3,d5], [d3] ,Zs_2)
dateLT(d2,d3), merge([d3,d5], [d3] ,Zs_2)
Rule 2 – failure branch…Rule 1 – failure branch…
{ X_3=d3,Xs_3=[d5],Ys_3=[],Zs_2=[d3,d3|Zs_3] } Rule 2
Rule 1 – failure branch…
merge([d5], [] ,Zs_3)
{ Xs_4=[d5], Zs_3=[d5] } Fact 4
Rule 2 – failure branch…
Rule 3 – failure branch…
Rule 3 – failure branch…
{ X_2=d3,Xs_2=[d5],Y_2=d2,Ys_2=[d3],Zs_1=[d2|Zs_2]} Rule 3
Backtracking Optimization - CutThe cut operator (denoted ‘!’) allows to prune trees from unwanted branches.
A cut prunes all the goals below itA cut prunes all alternative solutions of
goals to the left of itA cut does not affect the goals to it’s
right
The cut operator is a goal that always succeeds
Example - Merge with Cut In the merge example, only 1 of the 3 first
rules can be true. There is no reason to try to others.
Modify rule 1:merge([X|Xs] ,[Y|Ys], [X|Zs]) :- dateLT(X,Y), !, merge (Xs, [Y |Ys],Zs).
merge([d1,d3,d5],[d2,d3],Xs)
dateLT(d1,d2),!, merge([d3,d5], [d2,d3] ,Zs_1)
merge([d3,d5], [d2,d3] ,Zs_1)
!, merge([d3,d5], [d2,d3] ,Zs_1)
Rule 2 – failure branch…
Rule 3 – failure branch…
Another ExampleHow many results does this query
return?
Why does this happen?The query fits both rules 4 and 5How can we avoid this?Add cut to rule 4
?- merge([],[],X) .
merge(Xs, [ ],Xs) :- !.
X = [];X = [];No
Meta-Circular InterpretersWe have seen 3 different
interpreters in class
Version 1 is trivial
We can’t control the computation this way
solve( A ) :- A.
Interpreter Version 2
clause finds the first rule unifying with A with body B
% 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) :- A\=true, clause(A, B), solve(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)
{<A_1 = ancestor(abraham, P)>}Rule 3 solve
solve(ancestor(abraham, P))
clause(ancestor(abraham, P), B_1), solve(B_1) { <B_1 = parent(abraham, P)>,
<X_2 = abraham>, <Y_2 = P> }Rule 1 ancestor
solve(parent(abraham, P))
{<A_3 = parent(abraham, P)>}Rule 3 solve
{<P = issac>, <B_3 =true>}Fact 1 parent
solve(parent(abraham,Y_2), ancestor(Y_2, P))
{ <B_1 = parent(abraham,Y_2), ancestor(Y_2, P)> }Rule 2 ancestor
{<A_3 = parent(abraham,Y_2)><B_3 = ancestor(Y_2, P>}Rule 2 solve
clause(parent(abraham, P), B_3), solve(B_3).
solve(true)
true
solve( parent(abraham,Y_2)), solve(ancestor(Y_2, P))
clause(parent(abraham, Y_2), B_4), solve(B_4)
solve(ancestor(Y_2, P))Fact 1 solve
{<P = issac>}
{<A_4 = parent(abraham,Y_2)>}Rule 3 solve
{<Y_2 = issac>, <B_4 =true>}Fact 1 parent solve(true) ,
solve(ancestor(issac, P))
Interpreter Version 3In 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
Queries are checked against the new program
Interpreter Version 3
Sample converted program:
% 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).
%rule (Head, BodyList)/21. rule( member(X, [X|Xs] ), [] ).2. rule( member(X, [Y|Ys] ), [member(X, Ys)] ).
{ <X_3=X>,<Y_3= a>,<Ys_3=[b, c]>,<B_2 = [member(X, [b,c])] > } Rule 2 rule
{ <A_4= member(X, [b,c])>, <B_4=[]>, <Goals_4=[]> } Rule 3 rule
{ <Goals1_4=[]>}Rule of append
{ <A_5=member(X,[b,c])>, <Goals_5=[]>}Rule 4 solve
{ <X=b>,<X_6 = b>, <Xs_6=[c]>,<B_5 = []> }Rule 1 rule
{<Goal_1 = member(X, [a, b, c])>}Rule 1 solve
solve(member(X, [a, b, c]))
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, [])
solve([],[])
{ <X=a>,<X_3 = a>,<Xs_3=[b, c]>,<B_2 = []> }Rule 1 rule
true
{<X=a>}
Rule 1 solvesolve([member(X, [b,c])], [])
append([], [], Goals1_4), solve(member(X, [b,c]), Goals1_4).
solve(member(X, [b,c]), []).
rule(member(X,[b,c]), B_5), solve(B_5, [])
solve([],[])
true
{<X=b>}
Rule 1 solve