logic programming logic programming had its beginning in early 1970s. it was primarily introduced by...
TRANSCRIPT
Logic Programming • Logic programming had its beginning in early 1970s.
It was primarily introduced by Kowalski (1974) and Colmerauer et al. (1973) following the earlier work in automated theorem proving and Artificial Intelligence.
Logic programming is based on first order predicate logic. Clauses are very common in logic programming which have special forms of first order predicate logic (FOL) formula.
Program in logic programming is a collection of clauses. Clause in logic programming adopts a special clausal notation.
Queries are solved using resolution principle. Several logic programming languages have been
developed since then reflecting different choices of clause selection.
In the family of logic programming there are two categories viz., Prolog and its extensions (Prolog-II, IC-Prolog, MU-Prolog, LPA Prolog, Edinburgh Prolog etc.) based on sequential execution and other languages, such as PARALOG, concurrent Prolog, GHC etc are based on parallel execution.
Prolog is an acronym for Programming in Logic and is based on logic programming concepts
The treatment of non determinism distinguishes between sequential and parallel languages.
• A clause in logic programming is represented in a clausal notation given by
A1, …, Ak B1,…, Bt ,
where Aj are positive literals and Bk are negative literals.
Conversion of a Clause into Clausal Representation
• A clause in FOL is a closed formula of the form, (x1) … (xn) (L1V… V Lm), where each Lk , (1 k n) is a literal and xk , (1 k n) are all the variables occurring in L1, …, Lm .
Let us remove the prefix (x1) … (xn) from the above representation for the sake of simplicity because all the variables appearing in a clause are universally quantified .
Therefore, a clause is represented as L1V L2 V… V Lm , where Lk , (1 k n) are literals free from quantifiers. Now separate positive and negative literals in the clause as follows.:
(L1V L2 V… V Lm)
(A1 V … VAk V ~ B1 V …V~ Bt),
where m = k + t, Aj, (1 j k) are positive literals and Bj , (1 j t) are negative literals.
(A1 V … VAk ) V ~ (B1 … Bt)
(B1 … Bt) (A1 V … VAk )
{since P Q ~ P V Q}
Clausal notation is written in the form :
(A1 V … VAk ) (B1 … Bt)
Or A1, …, Ak B1,…, Bt .
• Here Aj , (1 j k) are positive literals and Bi , (1 i t) are negative literals.
• It must be noted that interpretation of A B is same as B A .
• In clausal notation, all variables are assumed to be universally quantified.
• Bi , (1 i t) (negative literals) are called antecedents and Aj , (1 j k) (positive literals) are called consequents.
• Commas in antecedent and consequent denote conjunction and disjunction respectively.
Logic Programming Concepts• A program clause is a clause of the form A
B1, …, Bn that contains precisely one positive literal. It is also called conditional rule.
• A in a program clause is called head and (B1, …, Bn) is called body of the rule.
– The informal semantics of A B1, …, Bn in logic is that "for all interpretations, if B1... Bn is true, then A is true".
– Formal semantics of the rule A B1, …, Bn in logic programming is that "A is proved if B1, ….Bn are proved together".
• Here A, B1, B2, ….Bn are predicates along with the arguments and the symbol denotes "implied by”.
• A unit clause is a clause of the form A . This is unconditional clause. Alternatively, a program clause with only one positive literal and with no negative literals is called unit clause. It has an empty body always– Its informal semantics in logic is that "for all
interpretations, A is true”.– Formal interpretation of unit clause in logic
programming is that "A is valid".
A logic program is a finite sequence of program clauses.
A goal clause is a clause of the form B1, …, Bn with no positive literal or an empty consequent. Each Bk (1 k n) is called a sub goal of the goal clause.
The meaning of a logic program P is the set of ground goals deducible from P.
An empty clause is the clause with empty consequent and empty antecedents. This clause is to be understood as a contradiction. It is graphically denoted by square / rectangle.
Applying the results of FOL to the logic programs, a goal G with respect to a program P (finite set of clauses) is solved by showing that the set of clauses P { ~ G} is unsatisfiable or there is a resolution refutation of P { ~ G}.
If so, then G is logical consequence of a program P. The basic constructs of logic programming are inherited from FOL.
There are three basic statements: facts, rules and queries. These are special forms of clauses as already mentioned earlier.
Fact (Unit clause): The simplest kind of statement is called fact which states a relation between objects such as BROTHER ( john, mike) .
This fact says that john is a brother of mike. BROTHER is a predicate symbol that shows relationship between two individuals named john and mike.
Rule (Program Clause): If we have to define a rule “x is a grandfather of y, if x is a father of z and z is a parent of y” using logic programming convention, then we write
GRANDFATHER (x, y) FATHER (x, z) , PARENT (z, y)
Query (Goal): Query is used to retrieve information from logic program.
Answering a query with respect to a logic program is to determine whether the query is a logical consequence of a program or not.
Logical consequences are obtained by applying resolution rule.
Query can be simple or conjunctive. A simple query ( B) is one which has only one
sub goal and is a logical consequence of a program if B is deducible from it.
A conjunctive query is conjunction of sub goals ( B1, B2, ….Bn ) which is a logical consequence of a program if all the sub goals in the query are logical consequences of it.
Within each type of query, a goal(s) can be ground or non ground.
In ground query, the goal(s) contains constants. Answer to ground query is either yes or no depending upon whether it is a logical consequence of a logic program or not.
In non ground query, the goal(s) should have atleast one variable as an argument.
Comma “,” in the query denotes logical conjunction ( ) to separate sub goals and is different from the comma used to separate the arguments of the predicate in the goals of the query.
Selection of the sub goal to be reduced in a conjunctive query is arbitrary in logic programming. In any given resolvent, all the sub goals must be reduced.
Constants: Constants are numbers (e.g., 23, 5 etc), string (e.g., 'mary', 'robert' etc) and symbols (e.g., mary, robert etc) a descriptive names in lower case letters
Variables: Variable stands for an unspecified but single entity rather than a store location in memory. The scope of the variable is a clause in which it
appears. Variables can appear in facts, rules or goals.
Variables in a fact or rule are universally quantified whereas in goals are existentially quantified.
Example: Consider the following logic program.GRANDFATHER (x, y) FATHER (x, z) , PARENT (z, y)
PARENT (x, y) FATHER (x, y)
PARENT (x, y) MOTHER (x, y)
FATHER ( abraham, robert) FATHER ( robert, mike)
• In FOL, above program is represented by a set of clauses as:
S = { GRANDFATHER (x, y) V ~ FATHER (x, z) V ~ PARENT (z, y), PARENT(x, y) V ~ FATHER (x, y), PARENT(x, y) V ~ MOTHER (x, y), FATHER ( abraham, robert), FATHER ( robert, mike) }
• Let us number the clauses of S as follows:
i. GRANDFATHER (x, y) V ~ FATHER (x, z) V ~ PARENT(z, y)
ii. PARENT(x, y) V ~ FATHER (x, y)
iii. PARENT(x, y) V ~ MOTHER (x, y),
iv. FATHER ( abraham, robert)
v. FATHER ( robert, mike)
Simple queries :(a) Ground Query
Query: “Is abraham a grandfather of mike ?”
Goal: GRANDFATHER (abraham, mike).
• In FOL, ~ GRANDFATHER (abraham, mike) is negation of Goal = GRANDFATHER (abraham, mike).
• Include {~Goal} in the set S and show using resolution refutation that S {~ Goal} is unsatisfiable in order to conclude the Goal.
• Let ~ Goal is numbered as ( vi) in continuation of first five clauses of S listed above.
vi. ~ GRANDFATHER (abraham, mike)
( i ) ( vi )
{x / abraham, y / mike}
( iv) ~ FATHER (abraham, z) V ~ PARENT (z, mike)
{z / robert}
~ PARENT (robert, mike) ( ii)
~ FATHER (robert, mike) (v)
Answer: Yes
(b) Non ground queries
Query: “Does there exist x such that x is a father of robert ?” {Who is father of robert?}
FATHER (x, robert). • Include ~ FATHER (x, robert) in the given set S
and show that we get a refutation with some most general unifier.
• Hence it is proved that FATHER (x, robert) logical consequence of S with binding of { x / abraham}.
• Similarly the following simple non ground query can be solved
Query: “Does there exist x such that abraham is a father of x?" {abraham is father of whom?}
FATHER (abraham, x).
Query : “Do there exist x and y such that x is a father of y?" { who is father of whom?}
FATHER (x, y).
Conjunctive queries:(a) Ground Query
Query:“Is abraham a father of robert and robert is a father of mike?”
FATHER(abraham, robert), FATHER ( robert, mike).
Answer: yes
(b) Non ground queries: FATHER (abraham, x), FATHER ( x, mike).
Answer: x = robert
Resolution: Resolution is a powerful inference technique. There are three important cases of resolution.
In backward chaining, one of the clauses resolved is a goal clause.
In forward chaining, one of the clauses is always a given fact.
In rule collapsing, two rules are resolved with first rule’s left side matching some thing with the right side of second rule and combine into one rule. The resolution in all three cases is illustrated in the following ways.
Backward Chaining: Goal clause is resolved with one of the given clause.
In clausal form:
i. a b, c, d.
ii. a. {goal clause}
Result of resolution:
b, c, d. {conjunctive sub goals}
In logic form
i. a V ~ b V ~ c V ~ d
ii. ~ a
Result of resolution: ~ b V ~ c V ~ d
Forward Chaining: One of the clauses is always a given fact.
In clausal form:
i. a b, c, d.
ii. c.
Result of resolution: a b, d.
In logic form
i. a V ~ b V ~ c V ~ d
ii. c
Result of resolution: a V ~ b V ~ d
Rule Collapsing:Two given rules are resolved with one rule’s left side matching with the right side of other rule and combine into one rule.
In clausal form:
i. a b, c, d.ii. c e, f.
Result of resolution: a b, d, e, f.
In logic form
i. a V ~ b V ~ c V ~ d
ii. c V ~ e V ~ f
Result of resolution: a V ~ b V ~ d V ~ e V ~ f
Abstract Interpreter for Logic Program• An abstract interpreter for logic program takes
program P and a goal /query as an input.
• It performs computations and shows that the goal is a logical consequence of P or not.
• The heart of computational model of logic programs is unification.
• The following interpreter performs computations for a goal (ground or non ground).
Input: A program P and a goal G
Output: If G is a logical consequence of P then for ground query, output Yes and for non ground query, output the most general unifier obtained while satisfying goal G else output No.
Method: Initialize Resolvent with G; Progress = true; While ((Resolvent empty) and (Progress =
true) ) do
{• Choose a goal from Resolvent and store it in S;
if ( a clause A B1,….,Bn where, n 0 from P such that S and A unify with most general unifier (mgu) ) then replace S by B1,….,Bn where, n 0 in the Resolvent after applying to the Resolvent else Progress = false
} {end of while} If (Resolvent = empty and Progress = true) then
( if = then output Yes else output )else output No.
• Stop.
• For some computations, there is only one clause from the program that can reduce each subgoal.
• Such a computation is called deterministic. • If there are alternative choices for the sub goals,
then computation becomes non deterministic. • To handle non deterministic computation, develop
all possible reduction in parallel or sequentially using some clause selection strategy.
• Generate all possible computation paths and correct solutions are reported.
• Each iteration of the while loop of an interpreter corresponds to a single application of modus ponen rule which means that:
Given a rule of the form A B1, ….Bn ,
where, n 1 and the facts B1’ , ...., Bn’, using modus ponen law A’ can be deduced if A’ B1’,…,Bn’ is an instance of A B1, …,Bn .
• Proof tree of a ground goal consists of nodes and edges which represent the goals reduced during the computation.
• Search tree of a non ground goal with respect to a program is a trace of all possible paths in the computation of the goal.
Consider the following logic.SON(x, y) FATHER(y, x) , MALE(x)
FATHER( abraham, robert) FATHER( robert, mike) MALE(abraham) MALE(robert) MALE(mike)
Generate Proof tree for a goal :
SON( mike, robert)
Proof Tree:
SON( mike, robert)
FATHER(robert, mike), MALE(mike)
• Search tree for SON(x, y) SON(x, y)
FATHER(y, x), MALE(x)
{y / abraham, x / robert} {y / robert, x / mike}
MALE(robert) MALE(mike)
Prolog Language Prolog is unique in its ability to infer facts from
the given facts and rules. In Prolog, an order of clauses in the program and
goals in the body of a clause is fixed. There are two major issues in logic programming
which have been resolved in Prolog. First issue is the arbitrary choice of goal in the
resolvent to be reduced. Second one is the non deterministic choice of the
clause from the program to effect the reduction.
In Prolog, first issue is resolved by choosing the first sub goal from the resolvent and the second issue is resolved by choosing the first clause matched searching sequentially the clauses from top to bottom in a program.
A stack scheduling policy is adopted. It maintains the resolvent as a stack.
It pops the top sub goal for reduction, and pushes the derived goals on the resolvent stack.
Prolog is an approximate realization of logic programming computation model on a sequential machine.
General syntax of Prolog• A Prolog program is a collection of facts and
rules. Brief syntax of Prolog is given below:
• Constants are numerals and symbols such as, 4, mary, etc.
• String is a string of characters and is enclosed within single quotes e.g., 'This is a string'.
• Function and predicate names must start with alphabet and are formed by using lower case letters, numerals and underscore ( _ ).
Variable names are formed similar to function and predicate names but it must start with upper case letter. Normally, we use X, Y, Z, ... for variables.
Clause (representing fact or rule) in Prolog is terminated by full stop (.).
Goal to a Prolog program is given after symbols ?-. Goal might be simple or conjunction of sub goal(s) terminated by full stop.
Arithmetic assignment is achieved by a predicate called ‘is’ which is an infix operator. Evaluation of goal ‘X is Expression’ is done by first evaluating
Expression according to the evaluation rules of arithmetic and is unified with left hand side variable X in order to succeed the goal.
All variables on right hand side of ‘is’ must be known at the time of its execution otherwise the goal will fail.
Examples:1. X is 2 + 5 succeeds by unifying X with 7.
2. Y is X -3 succeeds by unifying Y with 4. (Here X = 7 is available prior to executing this goal).
3. 7 is X fails as left side of 'is' has to be variable.
Equality operator denoted by ‘=‘ is also an infix operator. When the goal T1 = T2 (pronounced as ‘T1
equals T2’) is attempted to be satisfied by Prolog, it succeeds if T1 and T2 match.
• Here T1 and T2 are any terms. It is to be noted that T1 = T2 or T2 = T1 are same.
Examples: 1. 3 = X, succeeds by unifying a variable X
with 3.
2. mary = mery , fails.
3. X = likes(ram, sita), succeeds by unifying X with likes(ram, sita).
4. X = 4, succeeds by unifying a variable X with 4.
5. love(X, sita) = love(ram, Y), succeeds by unifying a variable X with ram and
Y with sita.
Non equality is denoted \= which is opposite of equality. The goal T1 \= T2 succeeds if the goal T1 = T2 fails otherwise the goal T1 \= T2 fails.
It is pronounced as ‘T1 is not equals to T2’.
Examples:
1. likes(ram, X) \= likes(sita, ram) succeeds.
2. father(X, Y) \= father(john, mery) fails.
3. 4 \= 3 succeeds.
4. mary = mery succeeds.
Prolog Program Prolog program is a finite sequence of program
clauses. Informally, we can define Prolog program to be a
set of facts and rules. It is based on Horn clauses. Representation of a Horn clause in Prolog:
A :- B1 , B2 , …, Bn . (1)
The symbol of logic programming is represented by :- in Prolog .
‘A’ is positive atom and B1, …and Bn are negative atoms.
Clause given in (1) is a conditional rule which is interpreted as "A is true if B1 … Bn are simultaneously true".
A clause with no antecedent is a fact that is always true and is denoted by A. .
:- B1, …, Bn. means a conjunctive query interpreted as "if B1, … and Bn are true, then what is the consequent?". The goal in Prolog is represented as ?- B1, …, Bn. Here Bi , (1 i n) are called sub goals.
The symbols A and Bi , (1 i n) represent predicate names with arguments, if any. The scope of variables is only a clause in which it is appearing.
Comment in Prolog are preceded by percentage symbol ‘%’ if single line and enclosed within /* comments */ for multiple lines.
Examples:• Prolog rule for computing square of a number.
% square(X, Y) - succeeds if Y is a square of X. square(X, Y):- Y is X * X.
Query: ?- square(2, Y). Answer: Y = 4
• The goals ?- square(X, 9) and ?- square(3, 9) fail because left side of 'is' has to be a variable and the variables appearing in the right side of 'is' should be bound.
• If we define above rule using equality operator ( = ) such as
square(X, Y) :- Y = X * X. • Then the goal ?- square(X, 3 * 3) succeeds with
X = 3 whereas the goal ?- square(X, 9) fails because equality is for comparing the terms and not for evaluating an expression on right side of equal operator.
• Prolog rule to compare two terms for equality.
/* equal(X, Y) - succeeds if terms X and Y are equal
*/
equal(X, Y) :- X = Y.
Goals:
?- equal(5, 5). Answers: Yes
?- equal(5, X). Answers: X = 5
?- equal (5, 6). Answers: No
Prolog Control Strategy While executing Prolog goal, leftmost goal is
chosen instead of an arbitrary one as is done in logic programming.
Prolog is non deterministic because a goal can be satisfied using more than one rules.
Non determinism is resolved by choosing a program clause using sequential search in a program from top to bottom for a unifiable clause.
Prolog contains three basic control strategies. Forward movement Matching (Unification) Backward movement (Backtracking)
Forward Movement:• Choose a rule by searching sequentially in the
program from top to bottom whose head matches with the goal with possible unifier.
Remember the position of the matched rule.Join the rule body in front of the sequence of sub goals to be solved.
Repeat until either goal is satisfied or is failed.
Unification: Unification is a process of matching or finding the
most general unifier (mgu ). Constant matches with the same constant. A variable matches with any constant and
becomes instantiated. A variable matches with another variable.Examples :
• love(X, sita) and love(X, Y) unify with love(ram, sita) with mgu as {X / ram} and {X / ram, Y / sita} respectively.
• love(ram, sita) does not unify with love(sita, ram), love(X, mary), love(john, X) etc.
Backtracking: Backtracking in Prolog takes place in two
situations. First situation is when a sub goal fails and other is
when if the last sub goal succeeds, then to find alternative solutions. Backtracking is of two kinds viz., shallow and deep. Shallow backtracking: It occurs when the last sub
goal succeeds. To find alternative solution, the last sub goal is tried to be unified with another rule (if any) from the last place marker.
Deep backtracking: It occurs as a result of their failure as shallow in which it backtracks to previous sub goal.
Remove bindings generated by the sub goals introduced due to the current sub goal.
Look for an alternative rule (with the head matching goal, if any) after the place marker for previous sub goals.
Let us consider the following query to explain both types of backtracking more clearly.
?- G1, ... , Gi-1, Gi, ... , Gn
Suppose at some point we have satisfied G1,.., Gi-1 with some common bindings and have to satisfy sub goal Gi.
Choose a rule by searching sequentially from top to bottom whose head matches with sub goal Gi with some bindings.
If the sub goal Gi is satisfied, then proceed further to satisfy Gi+1 and continue the process in forward direction till we reach Gn else backtrack to Gi-1. Remove the bindings created earlier due to Gi-1 and try to satisfy Gi-1 again by using an alternative rule, if it exists, from last place marker of Gi-1.
If sub goal Gi-1 is satisfied then move forward to Gi, else backtrack to Gi-2. The process of backtracking continues till we reach G1.
If G1 fails for all possible definitions, then entire query fails.
In case, if entire conjunction of sub goals succeed, then the solution is displayed and in order to find alternative solution, the sub goal Gn is tried to be satisfied with alternative definition, if it exist, after last place marker (Shallow backtracking) of Gn .
Otherwise it backtracks to previous sub goal Gn-1 (Deep backtracking). Repeat the process till all possible solutions are generated.
Execution of a Prolog Query A computation of a ground query G (with no
variables) generates a proof tree. If the query succeeds, then answer is ‘yes’ otherwise it is ‘no’.
A computation of a non ground query G (with variables) generates search tree and finds of all possible solutions of G with respect to a program P.
As a result of computation, all possible values of the variables involved in query G are generated and displayed.
Conventions used for generating proof / search tree:
Start generating tree with root as a goal G to be satisfied.
Down ward arrow indicates the reduction of goal. Clause number on the left side of an arrow. Possible bindings enclosed within curley brackets
(if necessary) on right of an arrow. Leaf of the tree is labeled either succeeds or fails.
• Consider a simple Prolog program consisting of one rule for defining grandfather and five facts about father relations.
grandfather(X, Y) :- father(X, Z), parent(Z, Y). (1)
parent(X, Y) :- father(X, Y). (2)
parent(X, Y) :- mother(X, Y). (3)
father(james, robert). (4)
father(mike, william). (5)
father(william, james). (6)
father(robert, hency). (7)
father(robert, cris). (8)
Ground queries:?- grandfather(james, hency).
Proof tree: ?- grandfather(james, hency).
(1)
?- father(james, Z), parent(Z, hency).
(4) { Z = robert }
?- parent(robert, hency).
(2)
?- father(robert, hency).
(7)
succeeds Answer: Yes
Non ground queries:?- grandfather(william, X).
Search tree: ?- grandfather(william, X).
(1)
?- father(william, Z), parent(Z, X).
(6) {Z = james}
?- parent(james, X).
(2) (3)
?- father(james, X). ?- mother(james, X).
(4) {X = robert}
succeeds fails Answer: X = robert
Relational and Arithmetic operators• The following relational and arithmetic operators are
used in formulation of goals. The values of X and Y are compared and the goals succeed or fail as described below:
Relational Operators: All relational operators are infix operators.
X < Y succeeds if X is less than Y. X > Y succeeds if X is greater than Y. X =< Y succeeds if X is less than or equal to Y. X >= Y succeeds if X is greater than or equal to Y. Equality and non equality operators .
Arithmetic Operators: X + Y , X – Y, X * Y, X / Y, X mod Y with usual
meanings in arithmetic.
Examples:
• Define a rule to test whether a given number is positive or not.
/* positive_number(X) - succeeds if X is greater than zero otherwise it fails. */
positive_number(X) :- X > 0.
Goals: ?- positive_number(5). Answer: Yes
?- positive_number(-4). Answer: No
• Rule for identifying whether a number lies in a given range or not
/* number_in_range(X, Y, Z) - succeeds if Y lies between X and Z otherwise it fails. */
number_in_range(X, Y, Z) :-X =< Y, Y =< Z.
Goals: ?- number_in_range(10, 23, 100). Answer: No
?- number_in_range(10, 23, 20).
Answer: Yes
Programming Techniques in Prolog• Recursion is one of the most important execution
control techniques in Prolog. • Iteration is another important programming
technique. Prolog does not support it. It can achieve by making recursive call to be the last sub goal in the rule and by using the concept of accumulators which are special types of arguments used for holding intermediate results.
• The issues of termination, rule order, goal order and redundancy are also important.
Recursion in Prolog• Let us consider a classical example of computing
factorial using recursion.
• Recurrence relation or mathematical definition for computing factorial of positive integer is:
1, if n = 0
FACTORIAL (n)=
n* FACTORIAL (n-1),otherwise
• In Prolog, program for computing factorial of a positive number is written as follows:
/* factorial(N, R) – succeeds if R is unified with the factorial of N. */
factorial(0, 1). (1)factorial(N, R) :- N1 is N – 1,
factorial(N1, R1), R is N * R1. (2)• Rule (1) states that factorial of 0 is 1 which is a
terminating clause. • Rule (2) is a recursive clause which states that
factorial of N is calculated by multiplying N with factorial of (N-1).
Goal: ?- factorial(3, R).
Search tree:
?- factorial(3, R).
(2) {N = 3}
?- N1 is 2, factorial(N1, R1), R is N * R1
(2) {N1 = 2}
?-N2 is 1, factorial(N2, R2), R1 is N1 * R2,
(2) {N2 = 1} R is N * R1.
?-N3 is 0, factorial(N3, R3), R2 is N2 * R3 , ….
(1) {N3 = 0, R3 = 1}
?- R2 is 1 * R3 , R1 is 2 * R2, R is 3 * R1.
Succeeds {R2 = 1, R1 = 2, R = 6}
Example: GCD (using Euclidean algorithm).Solution:Recurrence relation for computing GCD using
Euclidean algorithm is defined as follows:
GCD(n, m), if n >m
GCD(m, n) = m , if n = 0
GCD(n, m mod n) , if n > 0
• Here 'mod' operator gives remainder.
• It is assumed that the value of first argument should be greater than equal to the value of the second argument.
• If not, then interchange their values. In Prolog, the program is written as follows:
gcd(M,N, G) :- N > M , gcd (N, M, G). (1)
gcd(M, 0, M) :- M > 0. (2)
gcd(M, N, G):- N > 0, R is M mod N, gcd(N, R, G) (3)
Goal: ?- gcd( 6,4, G).Search tree: ?- gcd(6, 4, G).
(3)
?- 4 > 0, R is 6 mod 4, gcd(4, R, G).
{R = 2}
?- gcd(4, 2, G).
(3)
?- 2 > 0, R is 4 mod 2, gcd(2, R, G).
{R = 0}
?- gcd(2, 0, G). { G = 2}
List Manipulation in Prolog List is a common and very useful recursive
data structure in non numeric programming. In Prolog, a list is represented by elements
separated by comma and enclosed in square brackets, e.g., A = [2, 4, 6, 8], B = [[a, b], [c], [p, q, t]] are examples of list
Empty list with no elements is represented by [ ] and is useful for terminating recursive programs for list.
Example: membership function
Solution: An element is a member of a list if it matches with the head or if it is contained in the tail of the list. – The fact that it matches with the head of a list is
written as: mem_list(X, [X|_] ).
– The rule that if an element does not match with the head, then it may be in the tail is written as: mem_list(X, [ _ | Y]) :- mem_list(X, Y).
• Underscore is used for non care entry. The complete program in Prolog is given below:
% mem_list(X, L) – succeeds if an element X is a member of list L.
mem_list(X, [X | _]). (1)
mem_list(X, [_ | Y]) :- mem_list(X, Y). (2)
Goal: ?- mem_list(d, [a, b, c, d, e, f]).
Proof tree: ?- mem_list(d, [a, b, c, d, e, f]).
(2) {X = d, Y = [b, c, d, e, f]}
?- mem_list(d, [b, c, d, e, f]).(2) {X = d, Y1 = [c, d, e, f]}
?- mem_list(d, [c, d, e, f]).
(2) {X = d, Y2 = [d, e, f]}
?- mem_list(d, [d, e, f]).
(1) succeeds Ans: Yes
Goal: ?- mem_list(X, [a, b, c]).
Search tree:
?- mem_list(X, [a, b, c]).
(1) {X = a} (2) {Y = [b, c]}
succeeds ?- mem_list(X, [b, c]).
(1) {X = b} (2) {Y = [c]}
succeeds ?- mem_list(X, [c]).
(1) {X = c} (2) {Y = []}
succeeds ?- mem_list(X, []).
Answer:X = a; X = b; X = c fails
Termination Depth first traversal of Prolog has a serious
problem. If the search tree of a goal with respect to a program contains an infinite branch, the computation will not terminate.
Prolog may fail to find a solution of a goal, even if the goal has a finite computation.
Non termination arises because of the following reasons: Left recursive rules: The rule having a recursive
goal as the first sub goal in the body.
Example: Consider acyclic directed graph { edge(p, q), edge(q, r), edge(q, s), edge(s, t)},
where edge(A, B) is a predicate indicating directed edge in a
graph from a node A to a node B.
• Write Prolog program to check whether there is a route from one node to another node.
Solution: The pictorial representation of graph {edge(p, q), edge(q, r), edge(q, s), edge(s, t)}is given below:
p
q
r s
t• { edge(p, q), edge(q, r), edge(q, s), edge(s, t)},• There is a route from node A to B if there is a
route from A to some node C and an edge from C to B or direct edge from A to B.
The prolog rules for finding route are written as:
route(A, B) :- route(A, C), edge(C, B). (1)
route(A, B) :- edge(A, B). (2)
edge(p, q).
edge(p, r).
edge(q, s).
edge(s, t).
Query: Check whether there exist a route between nodes p and t .
Goal: ?- route(p, t).
Proof tree: ?- route(p, t).
(1)
?- route(p, X), edge(X, t)
(1)
?- route(p, Y), edge(Y, X), edge(X, t).
Non terminating The above program is non terminating because of
left recursion. Better way of writing the above program is to write recursive call as the last call.
This type of rule is called tail recursive rule. The rule (1) is modified and is rewritten as
route(A, B) :- edge(A, C), route(C, B). (1’)
Proof tree of the goal ?- route(p, t)
(1’)
?- edge(p, X), route(X, t ).
{ X=q}
?- route(q, t).
(1’)
?- edge(q, X), route(X, t).
{X = r} {X = s}
?- route(r, t). ?- route(s, t).
continue
• Circular definition is another means of generating non terminating computation. For example, the goal ?- teacher(mike, robert) does not terminate with respect to the following program.
teacher(X, Y) :- student(Y, X). (1)
student(X, Y) :- teacher(Y, X). (2)
student(robert, mike).
student(john, robert).
Proof tree: ?- teacher(mike, robert)
(1)
?- student(robert, mike)
(2)
?- teacher(mike, robert) Non termination
Rule Order• A rule order is very important issue in Prolog
programs and is irrelevant for logic programs. • Changing the order of rules in a program,
permutes the branches in any search tree for a goal to be solved with that program.
• Since the search tree is traversed using depth first approach, the order of solutions will be different.
• It may affect the efficiency of executing goal. Let us consider again Prolog program for finding route between any two nodes of acyclic directed graph mentioned earlier.
• If we solve a query ?- route(p, X), then the answers {X = t; X = r; X = s; X = q }are produced on backtracking (verify yourself).
• Here we travel to the end of search tree and the solutions are obtained on backtracking. This program is inefficient with respect to time.
• If we change the order of rules i.e., if rule (1) and rule (2) are interchanged, then the same query will produce the answers in the order they are listed in the program as we go along the search tree .
• The order of facts is not important. The rules (1) and (2) are interchanged and written as:
route(A, B) :- edge(A, B). (1)
route(A, B) :- edge(A, C), route(C, B). (2)
Goal: ?- route(p, X).
Search tree: ?- route(p, X).
(1) (2)
?- edge(p, X). ?- edge(p, Y), route(Y, X).
{ X=q} {Y = q}
succeeds ?- route(q, X).
(1) (2)
?- edge (q, X). ?- edge(q, Y), route(Y, X).
Answer:{X = q; X = r; X = s; X = t}
Goal Order• In Prolog, goal order is more significant than rule order.
This also is irrelevant for logic programs.
• It may affect the termination because subsequent sub goals have different bindings and hence different search trees. Consider the following recursive rules.
(a) ancester(X, Y) :- parent(X, Z), ancester(Z, Y).
(b) route(X, Y) :- edge(X, Z), route(Z, Y).
• If the sub goals in the body of above rules are swapped, then the rules becomes left recursive and all the computations will become non terminating.
• It also affects the amount of search the program does in
solving a query .
Redundancy• Redundancy of the solution to the queries is
another important issue in Prolog programs. • It is again irrelevant for logic programs as the
meaning of logic program is the set of ground goals deducible from it no matter whether it is deduced uniquely or in several distinct ways.
In Prolog, each possible deduction means an extra branch in the search tree.
The bigger the search tree, higher the computation time. It is desirable in general to keep the size of the search tree as small as possible.
Redundant program at times may cause exponential increase in runtime in the event of backtracking.
There are various reasons by which redundancy gets introduced.
1. The same case is covered by several rules e.g.,
maximum(X, Y, X) :- X Y.(1)
maximum(X, Y, Y) :- Y X. (2)
Goal: ?- maximum(2, 2, X).
Search tree: ?- maximum(2, 2, X).
(1) {X = 2} (2) {X = 2}
succeeds succeeds
Here we get the same solution twice. We can avoid such redundancy by putting unique conditions.
The rule (2) can be rewritten as
maximum(X, Y, Y) :- Y > X.
• Redundancy in computation appears in the program by not handling special cases separately.
Consider a program append for concatenation of two lists. The computation time for the goal
?- append([2,4,6,7,8], [], X) is proportional to the number of elements in the first list even though the second list is empty.
If we add another rule in the program for this special case i.e., append(X, [], X) , then we get answer immediately and hence the redundancy in computation is reduced.
Iterative Programming in Prolog Prolog does not have storage variables that can hold
intermediate results of the computation and can be modified as the computation progress.
To implement iterative algorithm one requires the storage of intermediate results, Prolog predicates are augmented with additional arguments called accumulators.
These are similar to local variables in procedural languages. The programs written using accumulators are iterative in nature.
A Prolog program is iterative if it contains only unit clauses and iterative clauses.
If the recursive call is the last sub goal in a rule, then the same space is used for recursive call.
In iterative form of a program, make the recursive call as the last call if possible.
Let us write Prolog program for factorial using accumulators.
/* Here I and T are accumulators used for storing the counter value and intermediate computations */
fact (N, R) :- fact(N, R, 0, 1). (1)
fact(N, R, I, T) :- I < N, J is I + 1, T1 is J * T,
fact(N, R, J, T1). (2)
fact(N, R, N, R). (3)
• In rule (1), the goal fact calls fact with four arguments that corresponds to initialization stage.
Rule (2) is the basic iterative loop, performed by fact. The computation terminates when the accumulator I becomes equal to N.
Rule (3) is a base rule used to terminate the computation. The value of the factorial is returned as a result of the unification of second argument with the fourth argument.
In Prolog, predicates can be overloaded (i.e., same name can be used with different number of arguments).
Goal: ?- fact (3, R).
Search tree: ?- fact(3, R).
(1)
?- fact(3, R, 0, 1).
(2)
?- 0 < 3, J is 1, T1 is 1 * 1, fact(3, R, 1, 1).
(2)
?- 1 < 3, J is 2, T1 is 2 * 1, fact(3, R, 2, 2).
(2)
?- 2 < 3, J is 3, T1 is 3 * 2, fact(3, R, 3, 6).
(2) (3) {R = 6}
fails succeeds Answer: R = 6
• Consider another problem of computing fibonacci term using accumulators.
Version - I: Accumulators are used to keep two previous fibonacci terms counting up.
/* fib(N, F1, F2, F) – F is the fibonacci value of Nth term, F1 and F2 are accumulators holding values of two previous fibonacci terms. */
fib(1,1). (1)
fib(N,F) :- fib(N, 0, 1, F). (2)
fib(2, F1, F2, F) :- F is F1 + F2. (3)
fib(N, F1, F2, F) :- F3 is F1 + F2, N1 is N – 1, fib(N1, F2, F3, F). (4)
• Here rule (3) is base rule which terminates iteration by unifying appropriate arguments and (4) is an iterative rule. Draw Search tree for
Goal: ?- fib(5, F). Answer : F = 5
• This program can be rewritten more efficiently by taking another accumulator for storing counter value initially 1, increased at each iteration till it becomes N, then unify the last calculated sum with last argument.
Version - II: Another iterative version which uses counter increasing upward till n.
/* fib(N, I, F1, F2, F) – F is the fibonacci value of Nth term, accumulators F1 and F2 holding previous terms and I counter. */
fib(N, F) :- fib(N, 1, 0, 1, F). (1)
fib(N, I, F1, F2, F) :- I < N, J is I + 1,
F3 is F1 + F2, fib(N, J, F2, F3, F). (2)
fib(N, N, F1, F2, F2). (3)• It is to be noted that the first argument of fib
predicate must be ground otherwise it will fail.
Generations of Numbers• Let us consider a problem of generating numbers
in a given range. Such problems may give non termination if not handled carefully.
• Suppose we have to generate natural numbers L, L+1, L+2, ..., U-1, U between the range L and U.
/* generate(L,U,N) - The value N is generated between L and U. */
generate (L, U, L) :- L U. (1)
generate (L, U, N) :- L<U, L1 is L + 1,
generate (L1, U, N). (2)
Query: Generate a search tree for a goal
?- generate(5, 7, N).• Problem for generating integers from 1 to 100. The
numbers can be generated using above program but we can write another program which uses accumulator. In int(M, N), M is an accumu holding initially 1 and increased till N. int(N) :- int(1,N). (1)
int(N,N). (2)
int(M,N) :- M<100, N1 is M+1,
int(N1, N). (3)
Search tree for generating numbers 1 and 2. In rule (3), the condition becomes M < 2.
?- int(N).
(1)
?- int(1, N) .
(2) (3)
{N = 1} ?- 1 < 2, N1 is 1 + 1, int(2,N).
(2) (3)
{N = 2} fails