Copyright (C) R.A. van Engelen, FSU Department of Computer Science, 2000-2003

Logic programming is a form of declarative programming 10. Logic Programming With A program is a collection of axioms Each axiom is a Horn clause of the form: H :- B1, B2, ..., Bn. where H is the head term and B are the body terms Overview i Meaning H is true if all Bi are true Logic Programming A user of the program states a goal (a theorem) to be proven Prolog The logic programming system attempts to find axioms using steps that imply the goal (theorem) is true

Note: Study Section 11.3 of the textbook, excluding 11.3.2.

Resolution Prolog

To deduce a goal (theorem), the logic programming system Uses backward chaining searches axioms and combines sub-goals More efficient than forward chaining for larger collections For example, given the axioms: of axioms C :- A, B. Interactive (hybrid compiled/interpreted) D :- C. Applications: expert systems, , natural To deduce goal D given that A and B are true: language understanding, logical puzzles and games Forward chaining deduces that C is true: Popular system: SWI-Prolog C :- A, B Login: program.cs.fsu.edu and then that D is true: Type: pl to start SWI-Prolog D :- C Type: halt. to halt Prolog (note that a period is used as a Backward chaining finds that D can be proven if sub-goal C command terminator) is true: D :- C the system then deduces that the sub-goal is C is true: C :- A, B Since the system could prove C it has proven D

Prolog Terms Prolog Clauses

Terms are symbolic expressions that form the building blocks of A program consists of a database of Horn clauses Prolog Each clause consists of a head predicate and body predicates: A Prolog program consists of terms H :- B1, B2, ..., Bn. Data structures processed by a Prolog program are terms A clause is either a rule, e.g. A term is either snowy(X) :- rainy(X), cold(X). a variable: a name beginning with an upper case letter Meaning "If X is rainy and X is cold then this implies that X a constant: a number or string is snowy" an atom: a symbol or a name beginning with a lower case Or a clause is a fact, e.g.

letter rainy(rochester).

a structure of the form: Meaning "Rochester is rainy." functor(arg1, arg2, ..., argn) This fact is identical to the rule with true as the body where functor is an atom and argi are terms predicate: Examples: rainy(rochester) :- true. X, Y, ABC, and Alice are variables A predicate is a term (must be an atom or a structure) 7, 3.14, and "hello" are constants rainy(rochester) foo, bAR, and + are atoms member(X,Y) bin_tree(foo, bin_tree(bar, glarch)) and +(3,4) are true structures

Queries and Goals Example

Queries are used to "execute" goals Program with three facts and one rule: A query is interactively entered by a user after a program is rainy(seattle). loaded and stored in the database rainy(rochester). A query has the form cold(rochester). ?- G1, G2, ..., Gn. snowy(X) :- rainy(X), cold(X). Query and response: where Gi are goals A goal is a predicate to be proven true by the programming ?- snowy(rochester). system yes Example program with two facts: Query and response: rainy(seattle). ?- snowy(seattle). rainy(rochester). no Query with one goal to find which city C is rainy (if any): Query and response: ?- rainy(C). ?- snowy(paris). Response by the interpreter: no C = seattle Type a semicolon ; to get next solution:

C = rochester Type another semicolon ;: no (no more solutions)

Example (cont'd) Backtracking

Program: For every successful match of a (sub-)goal with a head predicate rainy(seattle). of a clause, the system keeps this execution point in memory rainy(rochester). together with the current variable bindings to enable cold(rochester). backtracking snowy(X) :- rainy(X), cold(X). An unsuccessful match later forces backtracking in which ?- snowy(C). alternative clauses are searched that match (sub-)goals C = rochester Backtracking unwinds variable bindings to enable establishing because rainy(rochester) and cold(rochester) are sub-goals that new bindings are both true facts in the database snowy(X) with X=seattle is a goal that fails, because cold(X) fails, triggering backtracking

Example: Family Relationships attire(mr_pope, watch). % If sir_raymond had tattered cuffs then mr_woodley had the Facts: pincenez: male(albert). attire(mr_woodley, pincenez) :- male(edward). attire(sir_raymond, tattered_cuffs). female(alice). female(victoria). % and vice versa: Rules: attire(sir_raymond,pincenez) :- parents(edward, victoria, albert). attire(mr_woodley, tattered_cuffs). parents(alice, victoria, albert). sister(X,Y) :- female(X), parents(X,M,F), parents(Y,M,F). % A person has tattered cuffs if he is in room 16: Query: attire(X, tattered_cuffs) :- room(X, 16). ?- sister(alice, X1). The system applies backward chaining to find the answer: % A person has black hair if he is in room 14, etc: 1. sister(alice, X1) matches 2nd rule: X = alice, Y = X1 hair(X, black) :- room(X, 14). 2. New goals: female(alice), parents(alice, M, F), hair(X, grey) :- room(X, 12). parents(X1, M, F) hair(X, brown) :- attire(X, pincenez). 3. female(alice) matches 3rd fact hair(X, red) :- attire(X, tattered_cuffs). 4. parents(alice, M, F) matches 2nd fule: M = victoria, F = albert % mr_holman was in room 12, etc: 5. parents(X1, victoria, albert) matches 1st rule: X1 = room(mr_holman, 12). edward room(sir_raymond, 10). room(mr_woodley, 16). Example: Murder Mystery room(X, 14) :- attire(X, watch).

% the murderer had brown hair: murderer(X) :- hair(X, brown).

% mr_holman had a ring: attire(mr_holman, ring).

% mr_pope had a watch: Example: Murder Mystery (cont'd) Unification and Variables

Question: who is the murderer? In the previous examples we saw the use of variables, e.g. C and ?- murderer(X). X Trace (indentation showing nesting depth): A variable is instantiated to a term as a result of unification murderer(X) Unification takes place when goals are matched to head hair(X, brown) predicates of rules and facts attire(X, pincenez) Goal in query: rainy(C) X = mr_woodley Fact in database: rainy(seattle) attire(sir_raymond, tattered_cuffs) Unification is the result of the goal-fact match: C = seattle room(sir_raymond, 16) Unification is recursive: FAIL (no facts or rules) An uninstantiated variable unifies with anything, even with FAIL (no alternative rules) other variables which makes them identical (aliases) REDO (found one alternative rule) An atom unifies with an identical atom attire(X, pincenez) A constant unifies with an identical constant X = sir_raymond A structure unfies with another structure if the functor and attire(mr_woodley, tattered_cuffs) number of arguments are the same and the corresponding room(mr_woodley, 16) arguments unify recursively SUCCESS Once a variable is instantiated to a non-variable term, it cannot SUCCESS: X = sir_raymond be changed and cannot be instantiated with a term that has a SUCCESS: X = sir_raymond different structure SUCCESS: X = sir_raymond

SUCCESS: X = sir_raymond

Unification Examples Lists

The built-in predicate =(A,B) succeeds if and only if A and B A list is of the form: can be unified [elt1,elt2, ..., eltn] The goal =(A,B)may be written as A = B where elti are terms ?- a = a. The special list form yes [elt1,elt2, ..., eltn | tail] ?- a = 5. denotes a list whose tail list is tail no ?- [a,b,c] = [a|T]. ?- 5 = 5.0. T = [b,c] no ?- [a,b,c] = [a,b|T]. ?- a = X. T = [c] X = a ?- [a,b,c] = [a,b,c|T]. ?- foo(a,b) = foo(a,b). T = [] yes ?- foo(a,b) = foo(X,b). X = a ?- foo(X,b) = Y.

Y = foo(X,b) ?- foo(Z,Z) = foo(a,b). no

List Membership Predicates are Relations

List membership is tested with the member predicate, defined Predicates are not functions with distinct inputs and outputs by Predicates are more general and define relationships between member(X, [X|T]). objects (terms) member(X, [H|T]) :- member(X, T). member(b, [a,b,c]) relates term b to the list that contains b ?- member(b, [a,b,c]). ?- member(X, [a,b,c]). Execution: X = a ; % type ';' to try to find more solutions member(b, [a,b,c]) does not match predicate member(X1, X = b ; % ... try to find more solutions X = c ; % ... try to find more solutions [X1|T1]) no member(b, [a,b,c]) matches predicate member(X , 1 ?- member(b, [a,Y,c]). [H1|T1]) Y = b with X1 = b, H1 = a, and T1 = [b,c] ?- member(b, L). Sub-goal to prove:member(X , T ) with X = b and T = L = [b|_G255] 1 1 1 1 therefore, L is a list with b as head and _G255 as tail, where [b,c] _G255 is a new variable member(b, [b,c]) matches predicate member(X , [X |T ]) 2 2 2 List appending predicate: with X2 = b and T2 = [c] append([], A, A). The sub-goal is proven, so member(b, [a,b,c]) is proven append([H|T], A, [H|L]) :- append(T, A, L). (deduced) ?- append([a,b,c], [d,e], X). Note: variables are "local" to a clause (just like the formal X = [a,b,c,d,e] arguments of a function) ?- append(Y, [d,e], [a,b,c,d,e]). Local variables such as X1 and X2 are used to indicate a Y = [a,b,c] match of a (sub)-goal and a head predicate of a clause ?- append([a,b,c], Z, [a,b,c,d,e]). Z = [d,e]

Example: Bubble Sort

bubble(List, Sorted) :-

append(InitList, [B,A|Tail], List), Prolog offers a few built-in constructs to support a form of A < B, control-flow append(InitList, [A,B|Tail], NewList), \+ G negates a (sub-)goal G bubble(NewList, Sorted). ! (cut) terminates backtracking for a predicate and within bubble(List, List). the body of the clause of that predicate ?- bubble([2,3,1], L). fail always fails append([], [2,3,1], [2,3,1]), Examples 3 < 2, fails: backtrack ?- \+ member(b, [a,b,c]). append([2], [3,1], [2,3,1]), no 1 < 3, ?- \+ member(b, []). append([2], [1,3], NewList1), this makes: NewList1=[2,1,3] yes Define:

bubble([2,1,3], L). if(Cond, Then, Else) :- Cond, !, Then.

append([], [2,1,3], [2,1,3]), if(Cond, Then, Else) :- Else.

1 < 2, ?- if(true, X=a, X=b). append([], [1,2,3], NewList2), this makes: X = a ; % type ';' to try to find more solutions NewList2=[1,2,3] no bubble([1,2,3], L). ?- if(fail, X=a, X=b). append([], [1,2,3], [1,2,3]), X = b ; % type ';' to try to find more solutions 2 < 1, fails: backtrack no append([1], [2,3], [1,2,3]), ?- if(true, a=b, X=b). 3 < 2, fails: backtrack no append([1,2], [3], [1,2,3]), does not unify: backtrack The cut makes sure that the Cond is not executed again upon backtracking and that the second if-clause is not executed bubble([1,2,3], L). try second bubble-clause which makes when Cond is true when backtracking L=[1,2,3] Therefore, this example would not work without the cut bubble([2,1,3], [1,2,3]). when backtracking bubble([2,3,1], [1,2,3]).

Imperative Control Flow Example: Tic-Tac-Toe How to make a good move to a cell: move(A) :- good(A), empty(A). Board layout: Which cell is empty? 1 2 3 empty(A) :- \+ full(A). Which cell is full? 4 5 6 full(A) :- x(A). 7 8 9 full(A) :- o(A). Facts: Which cell is best to move to? (check this in this order) ordered_line(1,2,3). good(A) :- win(A). % a cell where we win ordered_line(4,5,6). good(A) :- block_win(A). % a cell where we block the ordered_line(7,8,9). opponent from a win ordered_line(1,4,7). good(A) :- split(A). % a cell where we can make a split to ordered_line(2,5,8). win ordered_line(3,6,9). good(A) :- block_split(A). % a cell where we block the ordered_line(1,5,9). opponent from a split ordered_line(3,5,7). good(A) :- build(A). % choose a cell to get a line good(5). % choose a cell in a good location good(1). Note: You can download the program from here good(3). (instructions are included in the source). good(7). good(9). good(2). Example: Tic-Tac-Toe (cont'd) good(4). good(6). Rules to find line of three (permuted) cells: good(8).

line(A,B,C) :- ordered_line(A,B,C). line(A,B,C) :- ordered_line(A,C,B). line(A,B,C) :- ordered_line(B,A,C). line(A,B,C) :- ordered_line(B,C,A). line(A,B,C) :- ordered_line(C,A,B). line(A,B,C) :- ordered_line(C,B,A).

Example: Tic-Tac-Toe (cont'd) Example: Tic-Tac-Toe (cont'd)

How to find a winning cell: Board positions: win(A) :- x(B), x(C), line(A,B,C). O Choose a cell to block the opponent from choosing a winning X O cell: X block_win(A) :- o(B), o(C), line(A,B,C). Choose a cell to split for a win later: Are stored as facts in the database: split(A) :- x(B), x(C), \+ (B = C), line(A,B,D), line(A,C,E), x(7).

empty(D), empty(E). o(5). x(4). O o(1). X O Move query: ?- move(A). X X A = 9 Choose a cell to block the opponent from making a split: block_split(A) :- o(B), o(C), \+ (B = C), line(A,B,D), line(A,C,E), empty(D), empty(E). Choose a cell to get a line:

build(A) :- x(B), line(A,B,C), empty(C).

Arithmetic Arithmetic Examples

Arithmetic is useful for many computations in Prolog A predicate to compute the length of a list: The is predicate evaluates an arithmetic expression and length([], 0). instantiates a variable with the result length([H|T], N) :- length(T, K), N is K + 1. For example where the first argument of length is a list and the second is the X is 2*sin(1)+1 computed length instantiates X with the results of 2*sin(1)+1 Example query: ?- length([1,2,3], X). X = 3 A predicate to compute GCD: gcd(A, A, A). gcd(A, B, G) :- A > B, N is A-B, gcd(N, B, G). gcd(A, B, G) :- A < B, N is B-A, gcd(A, N, G).