Presentation on theme: "Lecture 10 Logic programming, PROLOG (PROgramming in LOGic)"— Presentation transcript:
Lecture 10 Logic programming, PROLOG (PROgramming in LOGic)
2 Logic programming The method of pure logic programming is a special restricted case of the general resolution method. There are two principle constraints: Prolog works only with Horn clauses that contain at most one positive literal; Prolog applies the linear strategy for the generation of resolvents; This linear strategy is combined with backtracking
3 Prolog notation & terminology Rules (conditional statements): P:- Q 1, Q 2,..,Q n. Gloss: P, if Q 1 and Q 2 and … Q n Note that this is equivalent to these formulas: ( Q 1 Q 2 … Q n P) (Q 1 Q 2 … Q n ) P with one positive literal The former is a clause with one positive literal. Facts (unconditional statements): P. without negative literals This is a clause without negative literals. Goals (questions): ?- Q 1, Q 2,..,Q n. without a positive literal Note that this is equivalent to the clause without a positive literal: Q 1 Q 2 … Q n Empty clause:, YES: success, the goals have been met Remark: Unlike in the FOL language, variables are written as beginning with upper-case letters, other predicates and terms with lower-case letters.
4 Foundations of Prolog A logic program is a sequence of conditional and unconditional statements, that is rules and facts. A procedure A procedure of a logic program is the set of rules and facts with the same head (positive literal). The goal clauses represent questions to which the program should answer. The answer is YES, if a goal is entailed by the given set of statements, otherwise the answer is NO. Hence the way how to answer the questions (that is how to meet the goals) is a matter of the program interpreter. It should decide whether the goals are entailed by the given program knowledge base. Moreover, the interpreter is also able to provide information about the values that yielded a positive answer. These values are obtained by unification of literals. Note. Logic program is a declarative rather than imperative specification of an algorithm. In this sense we should understand the notion of a statement. It is simply a logical formula. We specify what to do rather than how to do it. For details see, e.g.,
5 Prolog & Horn clauses Prolog works with the language of clauses restricted to Horn clauses. Example: “All members of a sport club are skiers or rock climbers”. Problem. The consequent is in the form of disjunction: x [SC(x) (SKI(x) CLIMB(x))] SKI(x) CLIMB(x) x [ SC(x) SKI(x) CLIMB(x)] This statement/rule cannot be specified in the form of a Horn clause; there are two positive literals.
Example: sport club Tom, Peter and John are members of a sport club. Every member of the club is a skier or a climber. No climber likes raining. All skiers like snow. Peter does not like what Tom likes, and does like what Tom does not like. Tom likes snow and raining. Question: Is there in the club a sportsman who is a climber but not a skier? Knowledge base (+ query 11): 1. SC(t) 2. SC(p) 3. SC(j) 4. x [ SC(x) (SKI(x) CLIMB(x)) ] problem: 2 pozitive literals 5. x [ CLIMB(x) LIKE(x,r) ] 6. x [ SKI(x) LIKE(x,s)] 7. x [LIKE(t,x) LIKE(p,x)] 8. x [ LIKE(t,x) LIKE(p,x)] 9. LIKE(t,s) 10. LIKE(t,r) 11. ? x [SC(x) CLIMB(x) SKI(x)]
Example: sport club This problem is easily solvable in the general resolution method of FOL. Try to do it as a homework However, its specification in Prolog is not possible in a direct way.
Prolog constraints (summary) 1. At most one positive literal (Horn clauses) 2. We cannot directly express negative facts Negation = fail of deduction; Prolog’s answer NO means this: The answer to your question cannot be derived from the program (knowledge base) (The closed-world assumption) declarative language two possibilitiesto affect program execution 3. Prolog is a declarative language. Thus there are only two possibilities how to affect program execution: The order The order of clauses in the program; the order of literals in a clause cut Predicate/Command ! (cut)
Example All students are younger than Peter’s mother. Tom and Anne are students. Who is younger than Peter’s mother? Formalisation in FOL:intended interpretation x [St(x) Y(x, f(a))]St the set of students St(b)Y relation of being younger; St(c)a Peter; b Tom; c Anne; f mother of … y Y(y, f(a)) ??? Program in Prolog: younger(X, mother_of(peter)):- student(X).rule student(tom).fact student(anne).fact ?- younger(Y, mother_of(peter)).question/goal
Solution in Prolog younger(X, mother_of(peter)):- student(X). student(tom). student(anne). ?- younger(Y, mother_of(peter)). The interpreter takes care of unification and resolution; it applies goal-driven linear strategy of program execution: 1) The goal ?- younger(Y, mother_of(peter)) is unified with younger(X, mother_of(peter)), Y=X; 2) A new goal is generated: ?- student(Y) 3) This goal is unified with the second fact; success: Y = tom 4) Answer: YES, Y = tom; Insertion of the semicolon (;) means that we ask „anybody else?“ This causes backtracking. The interpreter returns to the last goal and tries to meet it again: ?- student(Y). The second clause cannot be used again. Hence the interpreter uses the third fact: 5) The answer is again positive: YES, Y = anne;(anybody else?) NO Note. Semicolon ‘;’ means logical disjunction or comma ‘,’ means logical conjunction and
Example modified 1. younger(X, mother_of(peter)):- student(X). 2. student(tom). 3. student(anne). 4. younger(X, mother_of(peter)):- child(X, mother(peter)). 5. child(X,Y):-Y=mother_of(X). ?- younger(Y, mother_of(peter)).question (goal) a)YES, Y = tom; b) YES, Y = anne; Anybody else? The answer is Y = peter. (Peter is younger than his mother)
Example continued 1. younger(X, mother_of(peter)):- student(X). 2. student(tom). 3. student(anne). 4. younger(X, mother_of(peter)):- child(X, mother(peter)). 5. child(X,Y):-Y=mother_of(X). ?- younger(Y, mother_of(peter)).question (goal) a)YES, Y = tom; b) YES, Y = anne; c) Yes, Y = peter. Now we ask another question: ?- student(peter). Answer: NO This does not mean that Peter actually is not a student. Rather, due to the closed-world assumption it only means that the fact that Peter is a student is not entailed by the given program. Negation is a failure of deduction. We cannot directly insert negative facts to the program.
Negation in Prolog If we want to express the fact that Marie is not a student, we can use the predicate not: not(student(marie)):- call(student(marie)), !, fail. not(student(marie)). semantics not(student(marie))(semantics) failNO call(student(marie)): If the program entails that Marie is a student then the call succeeds; another goal ! (cut) also succeeds; but the predicate fail yields the answer NO. cut ! : no backtracking; „do not try again“, it is not true that Marie is not a student. If call(student(marie)) fails then not(student(marie)) succeeds; it is true that Marie is not a student.
Example: Eukleides algorithm In mathematics, the greatest common divisor (gcd), also known as the greatest common factor (gcf), or highest common factor (hcf), of two or more non-zero integers, is the largest positive integer that divides the numbers without a remainder. For example, the GCD of 8 and 12 is gcd X,X,X . 2.gcd X,Y,Z :- X>Y, gcd X-Y,Y,Z . 3.gcd X,Y,Z :- Y>X, gcd X,Y-X,Z . Note. Prolog has built in basic arithmetic. 4. ?- gcd 4,6,Z . Execution: 5. ?- 4>6, gcd 4-6,6,Z resolution: 4,2. 4. ?- gcd 4,6,Z backtracking 5. ?- 6>4, gcd 4,6-4,Z resolution: 4,3. 6. ?- gcd 4,2,Z fact "6>4“; hence calculate gcd 4,2,Z 7. ?- 4>2, gcd 4-2,2,Z resolution: 6,2. 8. ?- gcd 2,2,Z Calculating the clause 7, fact "4>2" 9. yes, Z=2.resolution: 8,1.
Calculating natural numbers 1. nat 0 .0 is a natural number 2. nat s(X) :-nat(X).the successor of a natural number is a natural number 3. ?- nat s(X) .Which are all the natural numbers? Execution: 4. ?-nat X resolution 3,2. 5. YES, X = 0; 3. ?- nat s(X) backtracking 4. ?-nat(X) resolution 3,5. 6. YES, X = s(0); 7. YES, X = s(s(0)); 8. YES, X = s(s(s(0))); and so on, ad infinity potential infinity Note. This program generates a potential infinity, not actual. Potentially, if we had an infinite time at our disposal, we’d obtain all the naturals.
The order of clauses can change the execution of a program due to the linear strategy Program A: 1. parent(tom, marie). 2. parent(anne, marie). 3. descendant(X,Y):- ancestor(Y,X). 4. child(X,Y):- parent(Y,X). 5. ancestor(X,Y):- descendant(Y,X). 6. descendant(X,Y):- child(X,Y). 7. descendant(X,Y):- child(X,Z), descendant(Z,Y). 8. ?- descendant(marie,X). Note. Prolog renames variables, if needed
Execution tree descendant(marie,X) 36 ancestor(X,marie)child(marie,X) 54 descendant(marie,X)parent(X,marie) 31 ancestor(X,marie)X=tom; etc.....X=anne (2) The program does not answer though there is a solution; „first aid“: move the clauses 3, 5 down and cut them off (!); “there is a cycle, it will be corrected later on”
The order of literals 1. parent(tom, marie). 2. parent(anne,marie). 3. descendant(X,Y):- child(X,Y). descendant(X,Z) 4. descendant(X,Y):- descendant(X,Z), child(Z,Y). 5. child(X,Y):- parent(Y,X). 6. ?- descendant(marie,W). Execution: ?- child(marie,W), ?-parent(W,marie), W=tom; W=anne; ????? infinite cycle After the second semicolon the program starts looping: ?-parent(W,marie), ?- child(marie,W), ?- descendant(marie,W), ?-descendant(marie,Z), ?- child(Z’,Z), ?- descendant(marie,Z’), ?- child(Z,Z’), ?- child(Z’,Z’’), … It suffices to change the order of literals in the clause 4.
Correctly: 1. parent(tom, marie). 2. parent(anne,marie). 3. descendant(X,Y):- child(X,Y). descendant(Z,Y). 4. descendant(X,Y):- child(X,Z), descendant(Z,Y). 5. child(X,Y):- parent(Y,X). 6. ?- descendant(marie,W). first facts, then rules Remedial tenet: first facts, then rules. recursive predicate as the last one In the recursive rules, recursive predicate as the last one. Recursive rules: the predicate of a consequent (rule head) is applied again in the antecedent (rule body) These rules correspond to cycles in imperative languages
The function factorial: x! = x × x – 1 × … × 1, 0! = 1 fact(0, Factorial, Factorial). fact(Number,Help,Factorial) :- P1 is Number – 1, I1 is Help × Number, fact(P1,I1,Factorial). ?- fact(3,1,X). Execution: fact(3,1,Factorial)(Help = 1) fact(2,3,Factorial)(Help = 3) fact(1,6,Factorial)(Help = 6) fact(0,6,Factorial) YES, Factorial = 6
Goal-driven linear strategy of execution + backtracking Prolog interpreter examines the execution tree to the depth and left-to-right; This strategy is not complete; the problem can be left unsolved though there is a solution. This is due to the fact that the interpreter can get stuck in an infinite branch. Reordering clauses and/or literals can solve the problem. There are complete control strategies that examine the execution tree to the width; all branches simultaneously. However, whereas the above depth strategy needs only one stack (“LIFO” memory structure), the width strategy needs for each execution branch another stack less effective See, e.g.,
Prolog proof strategy When a query has been typed, the interpreter searches for a head that matches the (first) goal in the query (it tries to prove this goal before attempting to prove a second goal, if there is one). If the goal matches a fact, it has been proven, and the interpreter is finished with it. If it matches the head of a rule, each of the terms in the body becomes a new goal that must be satisfied. The interpreter does not finish until all goals have been satisfied by reaching the level of matching a fact; if one of them does not, the whole query fails. There are two cases where the interpreter might have to make a decision about how to tackle a query which has nothing to do with the logic of the program. This happens when there is more than one head which matches a goal, and when the body of a rule consists of more than one clause. Prolog's control strategy works as follows: in the former case, the first matching clause in the files is tried first, and in the second case, the goals in a body are attempted in the order they appear. It makes it possible to write programs such that although a query can be proven by a program in principle, the interpreter fails to do so. This can however to some extent be avoided by ordering the clauses carefully in the program, and if required the programmer can write a meta-interpeter which changes the proof strategy.
example sibling_in_law(X, Y) :- married(X, Z), sibling(Z, Y). sibling_in_law(X, Y) :- sibling(X, Z), married(Z, Y). This program identifies two ways of being a sibling-in-law: assuming that you correspond to the first argument of sibling_in_law, the siblings of the person you are married to are your siblings in law (the first case above), as are your siblings' spouses (second case). In attempting to answer the query ?- sibling_in_law(owen, geraint). there will be several points where the interpreter has to make a choice. Firstly, there are two clauses whose heads match the initial query (this corresponds to the OR of formal logic). Secondly (once one rule has been selected), the two terms in the body are separated by a comma which corresponds to logical conjunction, so from a logical point of view, we could start with either. the first matching clause in the files is tried first the goals in a body are attempted in the order they appear Prolog's control strategy works as follows: in the former case, the first matching clause in the files is tried first, and in the second case, the goals in a body are attempted in the order they appear.
Example; incompleteness Program execution tree 1. A:- E.A 2. A:- B B:- C.EB 4. E:- D C.DC 6. D:- E.E ?- A.D 4Yes 8. E 6 9. … There are two ways of computing the procedure A; the first one does not yield the solution
Clause cut: Clause cut: ! (compare with „go to“ in imperative languages) Controls and restricts backtracking It cuts off “branches that are not needed in this execution” This goal is met only once; if we attempt backtracking over !, the interpreter omits all the clauses of the procedure being executed and goes back to the goal coming before the procedure Red cut changes the declarative semantics of the program; it should not be used (wrong programming practice) Green cut does not change the semantics; it is applied in order to make the program more effective in these cases: a) Realization of „if, then, else“ (exclusive or) b) Management of exceptions (errors) c) In order to restrict seeking over large knowledge bases (when a success is reached after the given number of attempts)
„if, then, else“ by means of „fail“ Repeat: If the temperature is high (more than 30%), then switch off heating, else if the temperature is low (less than 15%), then switch on heating, else don’t do anything. thermostat(Action) :- temperature(X), action(X,Action), write(‘Do’, Action), nl, fail. % fail yields backtraching action(X,’switch-on’) :- X < 15, !. action(X,’switch-off’) :- X > 30, !. action(X,’don’t do anything’). ?- thermostat(X).
„if, then, else“ by means of „fail“ thermostat(Action) :- temperature(X), action(X,Action), write(‘Do’, Action), nl, fail. % fail yields backtraching action(X,’switch-on’) :- X < 15, !. action(X,’switch-off’) :- X > 30, !. action(X,’don’t do anything’). ?- thermostat(X). Possible actions: switch on, switch off, don’t do anything Without cut the possible instructions would be like this: switch on, don’t do anything, switch off, don’t do anything, don’t do anything, …
“if, then, else“ action(X,’switch-on’) :- X < 15. action(X,’swithch-off’) :- X > 30. action(X,’noting’) :- not(X 30). Less effective; we keep testing the condition
Exceptions, errors test(X) :- X = error, !, fail. test(X) :- write(‘valid:’, X). If there is an error then the cut lets fail be applied; the test fails. However, backtracking to the second clause is blocked. The execution returns to the clause preceding the first test. Equivalent, less effective but more comprehensible program: test(X) :- not(X = error), write(‘valid:’, X).
Data structure list List is a potentially infinite ordered n-tuple. [fish, tortoise, crab, octopus, …] Head is an element of the listBody is again a list Notation: [Head|Body], where Head is an element of the list and Body is again a list. Empty list [ ]. Procedure member (mostly built-in) tests whether the first argument is an element of the list member(X,[X|_]). X is an element if it is the head member(X,[_|Y]) :- member(X,Y). else check body Procedure append (two lists) append([ ],L,L). append([H,T],L,[H,T1]) :- append(T,L,T1).