A review of Prolog • will be our choice of a shell implementation language because (i) KBS facts and rules are directly encoded as Prolog statements (ii) Prolog has built within it an inference engine • Prolog: programming in logic • a declarative language: program code are human-oriented descriptions of what is being computed, and not how it is to be computed - compare with imperative languages (Pascal, C,...) which encode the computational procedure • Note: a competitor is Lisp, which is a functional language B. Ross Cosc 4f79 1 Logic • 1st order predicate logic: a mathematical theory which models the world with sentences denoting truth eg. Either it rains or it does not rain. Every number different than 0 is the successor of another number. If Jack is the parent of Fred, then Jack is the father of Fred, or Jack is the mother of Fred. If Jack is the parent of Fred, and Jack is male, then Jack is the father of Fred. There exists someone who is a parent of Fred. • predicate logic is concerned with: - formally representing facts with a formal grammar ('Predicate logic") - determining ways of deducing logical truths from these sentences "deduction" B. Ross Cosc 4f79 2 Logic Rules for connectives: ~W is true if and only if W is false W1 & W2 is true if and only if W1 is true and W2 is true W1 or W2 is true if and only if either W1 is true or W2 is true W1 if W2 is false if and only if W1 is false and W2 is true W1 iff W2 is true if and only if either both W1 and W2 are true or both W1 and W2 are false Rules for Quantifiers: ( ∀ X ) W is true if and only if W is true for every substitution of a domain object for X within W ( ∃ X ) W is true if and only if W is true for at least one substitution of a domain object for X B. Ross Cosc 4f79 3 Logic • If a sentence contains quantifiers and variables, in order to determine the truth of the sentence, you need to define a domain from which objects will be selected. eg. (∀ X ∃ Y) likes(X, Y) --> need to determine what X and Y are representing: ie. people? Food? integers?... • whether the sentence is really true depends on the model you define • the pairs of objects in which "likes(X,Y)" is defined by a relation • a relation is a set of tuples that are deemed to be true eg. if domain is { john, joe, mary, jane } then the relation for likes in our model might be: likes= { (john, joe), (mary, john), (jane, jane)} So likes(X,Y) is true for likes(john, joe), likes(mary, john), likes(jane, jane) B. Ross Cosc 4f79 4 Logic • if a given sentence is true for at least one model, then it is satisfiable otherwise it is unsatisfiable eg. (∀ X ∃ Y) likes (X, Y) iff ~likes(X,Y) is unsatisfiable • some sentences are always true: tautologies eg. (∀ X) p(X) & p(X) iff p(X) • existential quantification makes logic complex: there is no automatic means for finding the object which satisfy an existential equation - you can have an infinite set of objects to consider, so it would require exhaustively searching the set B. Ross Cosc 4f79 5 Logical deduction • S1 ⊨ S2 eg. • - S2 is a logical consequence of sentence(s) S1 (∀ X) likes(chris, X) ⊨ likes(chris, mum) S1 ⊢ S2 eg. - S3 is derivable from S1 (∀ X,Y) likes(X,Y) or true ⊢ true • the useful part of logic is that it is possible to apply syntactic transformations of sentences which preserve their truth in the model being represented by the sentence denoted: ∀ P, Q P ⊨ Q iff P ⊢ Q • there are many procedures for deriving new sentences, and hence new statements of truth • automatic theorem proving is concerned with proving a sentence is true, by applying syntactic transformations to it B. Ross Cosc 4f79 6 Logical deduction • logic programming technique: (a) convert statements of logic into Horn clauses (exists an algorithm to do that) (∀ X's) p(X's) if q1(X's) & ... & qn(X's) (∀ X's) p(X's) note: can have terms as arguments in predicate tuples (WFF's) • logic programming languages use the following deduction rule (called modus ponens): { ~A, (A if B) } ⊢ ~B { ~A, A } ⊢ false • this rule preserves soundness ie. truth is maintained. B. Ross Cosc 4f79 7 Logic Programming • logic programming languages use the following notation: (∀ X's) p(X's) if q1(X's) & ... & qn(X's) (∀ X's) p(X's) p(X's) :- q1(X's), ... , qn(X's). <-- called a "rule" p(X's). <-- called a "fact" • This restricted notation is just as computationally powerful as full predicate logic B. Ross Cosc 4f79 8 Syntax Constants: name specific objects i) atoms: constants or symbolic terms - constants begin with lower-case letter eg. bob, x25, aDog, - symbols are such things as =, +, ... (might be predefined in implementation, eg. :- ) ii) integers: 0, 25, -5 Variables: are generic place-holders of constants - begin with upper-case letter - eg. Answer, L, My_List, A3 - anonymous variable: the underscore _ , used when you don't need to use that variable anywhere else B. Ross Cosc 4f79 9 Syntax (cont) Structures : basic data structure term constant(arg 1, arg 2, ..., arg k) k •1 - eg. bob(25), age(old), age(X), tree(left(X), right(L)), *(4), ... illegal: B(bob) - there are useful buiiltin structures, for example... lists [] - empty list , short for '.'() [H|T] - list with first element H and tail list T, eg. [a,b,c], [25], [ _ ], '.'(H,T) [ t(a,b), t(X,Y), Z] • Term: a constant, variable, or structure B. Ross Cosc 4f79 10 Prolog Statements • Three basic kinds of statements 1. facts: parent(john, P). bank_account(36569483, smith, john, 25.66). finished. 2. rules: parent(X,Y) :- father(X,Y). grandparent(X,Y) :- parent(X,Z), parent(Z,Y). op_sys :- input(Job), run(Job,Res), cleanup(Res). 3. query: :- parent(jane,W). :- parent(X,Y), parent(Y,Z). B. Ross Cosc 4f79 11 Logic Programming • real example: fact or assertion rule append([ ], X, X). append([A|X], Y, [A|Z] ) :- append(X, Y, Z). head body • each fact or rule is called a clause • all the clauses with the same head name and # of arguments is a predicate (akin to a procedure) B. Ross Cosc 4f79 12 Program structure • predicate: all the clauses having the same identifier and number of arguments is - akin to a procedure • eg. parent(X,Y) :- father(X,Y). parent(X,Y) :- mother(X,Y). parent(adam). father(john, tim). father(john, jane). mother(jane, jill). grandparent(X,Y) :- parent(X,Z), parent(Z,Y). grandparent(adam, _). grandparent(eve, _). human_being(_). mother(john, tim). B. Ross Cosc 4f79 13 Prog. structure (cont) Good programming practices: - group all clauses of a predicate together in program file - be careful using same predicate identifier with different # args - never put more than one clause per line. - For large rules, put one goal per line also. eg. B. Ross Cosc 4f79 grandfather(X,Y) :male(X), parent(X,Z), parent(Z,Y). 14 Unification • Computing solutions: a powerful feature of logic programming systems is their ability to substitute values into the variables found in clauses - this lets the deduction compute values to a query • unification: pattern matching technique in which two atoms are matched together, and produces the most general substitution of variables that makes the 2 atoms identical • method: (i) two variables always unify: X = Y (ii) a variable always unifies with a non-variable term that does not contain that variable: X = s(Y, d(Q)) (iii) two non-var terms unify if their arguments unify : s(W, Y) = s(X, c) (because W = X and Y = c) • when a variable unifies with a term, we say it is bound to that term this binding is a data binding that holds forever ( until backtracking ) B. Ross Cosc 4f79 15 Unification examples • c = c --> result: 'true' • X=Y (where X, Y are uninstantiated) --> result: X <- Y (they are matched together) • X = d --> result: X <- d • s(X) = s(Y,c) --> fail • t(c) = t(Z) --> result: Z <- x • apples = oranges --> fail • apples = Apples --> result: Apples <- apples • a(b,C,d(e,F,g(h(i,J)))) = a(B,c,d(E,f,g(H))) --> result: B <- b, C <- c, E <- e, F <- f, H <- h(i,J) B. Ross Cosc 4f79 16 Unification (cont) • Note: unification can produce infinite terms, or even never terminate, under some conditions - this happens when the same variable occurs in both expressions eg. Y = father(Y) --> result: Y <- father(father(father(..... ))) – this usually only happens by accident in buggy programs B. Ross Cosc 4f79 17 Substitutions • the computed result from unification procedure • Also called bindings or computed answer substitutions • have form: Ω = { X1 <- t1,... Xk <- tk } ( k •0 ) where Xi's are variables and ti's are terms (constants, structures, variables) • each variable Xi is distinct To apply a substitution to a term: • simultaneously substitute all terms ti for the Xi in the term eg. let term W = p(U, g(V), Y) Ω = {U <- a, V <- Y, Y <- c} then W Ω = p(a, g(c), c) Note: it can simplify things to apply Y <- c for V in ž B. Ross Cosc 4f79 18 Unification algorithm Input: two terms T1 and T2 to be unified Output: the answer substitution set Ω, or FAIL Initialize: Ω := empty Algorithm: 1. If T1 (or T2) is a variable: (a) If variable T1 (or T2) occurs in expression T2 (or T1) then FAIL (b) else add T1 <- T2 (or T2 <- T1) to Ω, and reset T1 everywhere else 2. if either expression is a constant, and the other expression is a different constant or structured term, then FAIL 3. If both expressions are structured terms: (a) if they have different identifiers OR different # arguments then FAIL (b) else apply unification recursively to their arguments, unifying then by their corresponding ordered positions B. Ross Cosc 4f79 19 More examples 1) s(X, t(T ,X), Z) = s(a, t(b, c), W) 2) [ a, f(B), C, d, e, f ] = [ First, Second, Third | Rest ] B. Ross Cosc 4f79 20 Resolution • basic computation technique in logic programming • given a query :- G1, G2,...,Gk and a set of program clauses of form H :- B1, B2,..., Bj. (j≥0) 1. select a goal Gi (1 ≤ i ≤ k) in the query 2. find a clause Hn whose head unifies Gi resulting in a set of unification bindings Ω (might be empty) 3. replace Gi by the body of Hn, and apply žto new query ie. G1,...,G(i-1),Gi, G(i+1),...,Gk. Hn :- B1n,B2n,...,Bmn. (G1,...,G(i-1), B1n,B2n,...,Bmn, G(i+1),...,Gk.)ž 4. Repeat until an empty query is obtained; all the Ω 's collected will give the solution to the computation B. Ross Cosc 4f79 21 Resolution trees • Note that there are many ways to select a goal in the query to process next, and many ways to select a candidate clause in the program • The computation can be represented by a tree: :- Q1 Hi Hj :- Q2 :- Q2 :- Q3 Hm Hl Hr :- Q5 :- Q4 Hn Hk Ho Hp Hq (fail) (success) B. Ross Cosc 4f79 22 Logic Programming • example deduction using resolution: Program P: C1: C2: C3: C4: C5: C6: C7: A :B :B :D :D. E. F. B. C. D, F. E. Query: ?- A. (notation for queries) (1) apply C1: apply C3: apply C7: apply C4: apply C6: ?- A. ?- B. ?- D, F. ?- D. ?- E. ?- nil. --> thus ~A is false, or A is true. (2) apply C1: apply C3: apply C5: apply C7: ?- A. ?- B. ?- D, F. ?- F. ?- nil --> ditto. B. Ross Cosc 4f79 23 Prolog's Control • from previous examples, note that deduction proof 2 is shorter. Issues: (1) which clause to apply? (2) which goal in query to reduce? • Unfortunately, there is no automatic means of deciding this. • logic programming interpreters will use some sort of arbitary scheme • Prolog's Standard depth-first left-to-right control: a) control rule: always pick left-most goal to reduce b) search rule: always pick first clause that matches c) when there is no clause to apply, then go back to where you had a choice of clauses to choose from, and choose the next one --> "backtracking" • (c) above lets the interpreter find multiple solutions to a query • advantage: It's easy to learn, and efficient to implement • disadvantage: it is unfair, and tree searches can be nonterminating (contrast with breadth-first control) B. Ross Cosc 4f79 24 Backtracking • refers to searching for multiple solutions • a by-product of searching more than one clausewhen computing a goal 1: plan_date(Guy, Gal, Food) :- likes(Guy, Food), likes(Gal, Food). 2: 3: 4: 5: 6: likes(tom, sushi). likes(tom, pasta). likes(tom, bbq). likes(sue, pasta). likes(sue, bbq). ?- plan_date(tom, sue, Food) 1 ?- likes(tom,Food), likes(sue, Food) 2 ?- likes(sue, sushi) 3 ?- likes(sue, pasta) 5 B. Ross Cosc 4f79 {Food <- pasta } 4 ?- likes(sue, bbq) 6 { Food <- bbq } 25 Backtracking • Variables are unique within each clause. • when recursion occurs, rename variables in the clause so that they don't clash with those in the current goal eg. grandparent(A,B) :- parent(A,X), parent(X,B). parent(X,Y) :- father(X,Y). parent(X,Y) :- mother(X,Y). ?- grandparent(X, tom). ( grandparent(A,B) :- parent(A, X' ) , parent(X' , B). ) ž= { X <- A, B <- tom } ?- parent(A, X'), parent(X', tom). B. Ross Cosc 4f79 26 Some possible program behaviors - no solutions: eg. output --> "no" ?- pet(cat). pet(dog). - a finite number of solutions eg. ?- pet(X). pet(cat). pet(dog). - an infinite number of solutions eg. ?- pet(Y). pet(dog). pet(X) :- pet(X). - non-termination, and eventually memory overflow eg. ?- pet(X). pet(Y) :- pet(Y). - computation error: bad use of builtin predicates B. Ross Cosc 4f79 27 Real Prolog • Real prolog implementations add extra-logical features so that practical programming can be done. Some examples from Clocksin & Mellish are: (1) Input-Output: read\1, write \1 - read and write terms get\1, pur\1 - read and write characters nl - write a new line (2) Files see(X) - opens file X for input tell(X) - opens file X for output (3) Negation not G - succeeds if goal G fails, and fails if G succeeds (3) Equality X = Y - unifies X and Y, or fails if they don't unify X \= Y - opposite of X = Y, ie. not (X = Y) X == Y - like X=Y, except that all variables have to be the same too; == does not instantiate any variables B. Ross Cosc 4f79 28 Real prolog 3. equality (cont) X \== Y - same as: not X == Y 4. integer relations: < > >= =< = \= eg. X < Y - succeeds if value in X < Y. Note that X and Y can't be uninstantiated 5. arithmetic: X is Expr - where Expr is an arithmetic expression using +, - , *, /, mod, ... - (i)Y op Z is evaluated,; (ii) if X is not set, it is set to expr's value, or (iii) if X has a value, then it is compared with expression value. - Note that arithmetic expressions must use instantiated variables, else ERROR. 6. program database manipulation asserta(G), assertz(G) - adds clause G to the program database retract(G) - matches G with a clause, and removes it if found retractall(H) - retracts all clauses whose heads match with H B. Ross Cosc 4f79 29 Real Prolog 7. control repeat - always succeeds fail - always fails eg. loop :- repeat, write('hi!'), nl, fail. 7. structure manipulation S =.. List - Structure S is decomposed into List, or List is converted to S eg. A =.. [stock, macintosh, 128k] stock(pc2, 640k, mouse) =.. L : A <- stock(macintosh, 128k) : L <- [stock, pc2, 640k, mouse] eg. Testing the name of an atom checkname(Struct, Ident) :- Struct =.. [Ident| _ ]. ?- checkname(stock(pc2,64K,...), A). A = stock. B. Ross Cosc 4f79 30 Real Prolog 8. Variable inspection var(X) - succeeds if X is an uninstantated variable nonvar(X) - suceeds if X is not instantiated integer(X) - succeeds if X is an integer 9. program inspection clause(H,B) - matches head H and body B with a clause in database (facts have 'true' set for body) 10. atom inspection name(A,L) - breaks name A into a list L of integer ascii values eg. name(apple,L) : L <- [97, 112, 112, 108, 100] And Much More B. Ross Cosc 4f79 31 The Cut: ! • Used to permit more control over search. It deletes or 'cuts' parts of the computation tree. (1) p(X) :- q(X,Y). (2) p(X) :- r(X,Y), !, s(Y). (3) p(X) :- t(Y). ?- p(X). i) All the solutions from (1) are used. ii) If r(X,Y) succeeds, then ! is activated: - during backtracking, if s(Y) fails, then the whole clause fails - also, solutions from (3) will be ignored iii) if r(X,Y) fails, then (2) fails, and computation proceeds to (3) Note that s(Y) can still return multiple solutions B. Ross Cosc 4f79 32 Cut (cont) • Common use of cuts: p(X) :- G1, ..., Gk, !. : this means that, if clause succeeds, it will only return 1 solution eg. make member only return 1 solution member(X, [X| _ ]) :- !. member(X, [_ |Y]) :- member(X,Y). • possible to create cleaner control constructions with the cut eg. once(P) :- call(P), !. or once(P) :- P, !. eg. if_then_else(P, Q, R) :- P, !, Q. if_then_else(P, Q, R) :- R. ( could be : if_then_else(P, Q, R) :- P, Q. if_then_else(P, Q, R) :- (not P), R. except that test P might be expensive. ) B. Ross Cosc 4f79 33 Meta-interpreters • meta-interpreter: interpreter for a language written in the language itself • Prolog is ideally suited for this: solve(true). solve(not(A)) :- not solve(A). solve((A,B)) :- solve(A), solve(B). goals in Prolog are stored as: A, (B, (C, D)) ie. "," is right-associative solve(A) :- clause(A,B), solve(B). builtin predicate which gets a clause which unifies with head A, and has body B B. Ross Cosc 4f79 34 Defining operators • Prolog lets you define your own operators (or override builtin ones) • useful for writing customized programs, especially meta-interpreters eg. ?- op(300, yfx, ^). executed goal! right-associative infix: 2^3^4 --> 2^(3^4) relative precedence • to set precedence, you need the precedence settings of the system your using • see Clocksin & Mellish for explanation of op definitions B. Ross Cosc 4f79 35 Disjunction, if-then-else • disjunction (logical OR): parent(Parent, Child) :- mother(Parent, Child) ; father(Parent, Child). - can also do: p(X, Y) :- ( a(X, Z), b(Z, Y)) ; c(Y). - whenever you see a ";", you can convert the clause into multiple clauses • If - then : parent(X, Y) :- person(X) -> mother(X, Y). - behaves almost like parent(X,Y) :- person(X), mother(X, Y). except that person only returns one solution (no backtracking when mother fails) • If - then - else: parent(X, Y) :- male(X) -> father(X, Y) ; mother(X, Y). - if male(X) true, then father executed, else mother executed B. Ross Cosc 4f79 36 If-then-else • if-then-else can often be used instead of cuts • implemented as: parent(X, Y) :- male(X), !, father(X, Y). parent(X, Y) :- mother(X, Y). • trick: once(P) :- call(P), !. eg. once(member(apple, L)) B. Ross Cosc 4f79 <-- this saves putting cuts in lots of clauses <- don't need a "member" that has a cut in it 37 Debugging Prolog • interpreter's trace facility is very useful • standard debugging features: spy - set a breakpoint on a procedure trace - manually control execution skip - call a goal, and return control when goal finished creep - trace into the goal call redo - call goal again fail - force goal to fail • Beware! If you change the database (assert, retract), be sure to clean it up if necessary. Otherwise, you'll get side effects (doubly asserted clauses, etc) B. Ross Cosc 4f79 38 Example Prolog programs • • • • • Lists graph searching graph searching with cycle checking bubble sort sorted binary tree B. Ross Cosc 4f79 39 Example 1: appending lists append( [ ], L, L). append( [X | R] S, [X | T]) :- append(R, S, T). ?- append([ a, b, c], [1, 2, 3], L). L = [a, b, c, 1, 2, 3] . ?- append( Y, [1, 2, 3], [a, b, c, 1, 2, 3]). Y = [a, b, c] ?- append(X, Y, [1,2,3]). X=[ ], Y = [1, 2, 3] ; X=[1], Y = [2, 3] ; X=[1,2], Y = [3] ; X = [1,2,3], Y = [ ]; no ?- append([_|_], [Y|_], [1, 2, 3]). Y = 1; Y = 2; Y = 3; no B. Ross Cosc 4f79 % a way to use append like ‘member’! 40 Example: sorted binary trees nil : empty branch/tree tree(Left_branch, Key, Right_branch) : tree node with data – hence tree(tree(nil,2,nil), 4, tree(tree(nil,5,nil), 6, nil)) represents: 4 2 6 5 B. Ross Cosc 4f79 41 Binary tree (cont) add_bintree(Key, nil, tree(nil, Key, nil). add_bintree(Key, tree(L, Key, R), tree(L, Key, R)). add_bintree(Key, tree(L, K, R), tree(L2, K,R)) :Key < K, add_bintree(Key, L, L2). add_bintree(Key, tree(L, K, R), tree(L, K, R2) :Key > K, add_bintree(Key, R, R2). B. Ross Cosc 4f79 42 Example: graph search i a j f g k b e h l c d 5. Representing directed graphs as program clauses edge(d, c). edge(b, c). edge(b, f). edge(a, b). edge(e, f). B. Ross Cosc 4f79 edge(f, e). edge(f, i). edge(i, j). edge(j, k). edge(k, g). edge(h, l). edge(l, k). edge(g, f). edge(g, h). 43 Graphs • Graphs (cont) – Can then search for connections within the graph: path(A, B) :- edge(A,B). path(A,B) :- edge(A,C), path(C,B). note similarity with ‘ancestor’: ancestor(A,B) :- parent(A,B). ancestor(A,B) :- parent(A,C), ancestor(C,B). – However, unlike family relationships, graphs can have loops – using Prolog’s backtracking, it can easily get sidetracked into a loop – eg. ?- path(j,a) will go thru: j - k - g - f - i - j - etc B. Ross Cosc 4f79 44 Graphs • Graphs: another approach: use a list structure [v(Node1, Nodelist1), v(Node2, Nodelist2), ...] then graph([ v(a, [b, f]), v(b, [c,f]), v(d, [f]), v(f, [e,i]), v(g, [f,h]), v(i, [j]), v(j, [k]), v(k, [g]), v(l, [k]), v(h, [l]) ] ). • Can write a ‘path’ routine which keeps track of places it has already been to; if it has been to a place already, then don’t go there again! (can also do this with the other representation) B. Ross Cosc 4f79 45 Graphs path(Start, Finish) :- graph(G), smart_path(Start, Finish, G, [ ]). smart_path(A, B, G, _) :member(v(A,L), G), member(B, L). smart_path(A, B, G, Previous) :member(v(A,L), G), member(C, G), not member(C, Previous), smart_path(C, B, G, [A | Previous]). B. Ross Cosc 4f79 46 Bubble Sort bubble(L, S) :append(X, {A,B|Y], L), B < A, !, append(X, [B, A | Y], M), bubble(M, S). bubble(L, L). B. Ross Cosc 4f79 47