Download as pdf or txt
Download as pdf or txt
You are on page 1of 17

SIT305: Artificial Intelligence

Introduction to Logic Programming

 Logic programming

Logic programming languages

Logic programming languages, also called declarative languages, differ


substantially from the other programming languages we have discussed.

In a logic programming language, the programmer does not identify the


computations or functions necessary to derive an answer.

Rather, the programmer provides a set of facts, a set of rules which can be
applied, and then issues one or more queries.

It is the language engine which is responsible for applying the rules to the set of
facts to derive answers to the query.

For example,

o The programmer might supply a list of facts, such as:


 Fred eats meat.
 Wilma eats meat.
 Wilma eats vegetables.
 Betty eats vegetables.
 Barney eats meat.
 Barney eats vegetables.
o The programmer might then also supply a list of rules, such as:
 If someone eats meat then they are a carnivore.
 If someone eats meat and vegetables they are an omnivore.
 If someone eats X then X is food.
o Finally, the programmer can ask questions based on those facts and rules,
such as:
 Is meat a food?
 Is Fred a carnivore?
 Is dirt a food?
 Who are the omnivore?
o The language engine then tries to apply the rules to answer the questions,
e.g.:
 Yes (meat is a food).
 Yes (Fred is a carnivore).
 No (dirt is not a food).
 Barney and Wilma (are the omnivores).

Note that the answers are based only on the available information.

1
The great advantage of a declarative language is that the programmer only needs
to supply the information that is known (facts and rules), and ask questions - NOT
worry about the ways the rules must be applied to answer those questions.

>From a programmer's perspective, this is a much more high-level approach to


problem solving than in imperative or functional languages.

The disadvantages are that

o the query process can be quite slow and inefficient,


o the programmer must supply sufficient facts and rules to allow the engine
to answer the queries
o the programmer must understand how to phrase the facts, rules, and
queries

Logic programming language application areas include natural language


processing, expert systems, specifications checking, theorem proving, and control
systems (among others).

By far the dominant logic programming language is Prolog, so that is the


language we will examine in some detail.

Getting started in Prolog

In fact, in Prolog all the queries are expressed like yes/no questions, and the
answers come back either as

o a simple "No" (meaning no way could be found to make the query true),
or
o "Yes", plus all the ways in which the query could be made true.

In the example discussed earlier we might have asked the equivalent of "Is
there an omnivore?" and the answer would have been "Yes, Wilma and
Barney".

(the syntax would be a little different, but that is the effect)

Core components

The basic elements of a Prolog "program" are

o entities: the "things" we are discussing, such as "Betty", "Fred",


"vegetables", etc. in the example above

entities in Prolog are always written in lowercase letters, e.g. fred, or


wilma

2
o variables: as with more traditional programming languages, we can
declare a variety of variables

variables in Prolog always begin with an uppercase letter, e.g.


Individual, or Thing

o properties: we can assign properties to individual entities, for example


fred would have the property of being a carnivore

properties look like a C++ function call - the name of the property then the
entity that has that property is given in brackets, e.g. carnivore(fred),
or food(vegetables)

o relationships: we can assign relationships between entities, for example


fred eats meat, or wilma eats vegetables

relationships in Prolog again look like a C++ function call, we give the
relationship name first, then in brackets the two entities that are related,
e.g. eats(wilma,meat), eats(wilma,vegetables)

o rules: we can establish rules that describe how one relationship or


property is true if some other properties or conditions are true.

For example, a rule could state that if someone eats meat and they eat
vegetables then they are an omnivore.

rules in Prolog have three parts:

 The result: this is the relationship or property which results from


the rule, and goes on the left hand side of our rule, e.g.
omnivore(Individual)

Note that the entity here will usually be a variable.

 The "if" symbol: :- which separates the result from the clauses
 The clauses: the set of relationships and properties which
determine if the rule is to be applied, this goes on the right hand
side of the rule.

There are several logic symbols which can be used in the right
hand side:

 the comma represents logical AND,


 the semi-colon represents logical OR,
 the word NOT in some ways functions like logical NOT, but
there are some important differences that we'll consider
later
3
Since an omnivore must eat meat and eat vegetables, our clauses
would look like
eats(Individual,meat),eats(Individual,vegetables)

In the omnivore example, the whole rule would look like:


omnivore(Individual) :-
eats(Individual,meat),eats(Individual,vegetables)

Miscellaneous important facts:


To use Prolog we must follow a few basic steps:

1. Create our set of properties, relationships, and rules.

Typically we will enter these into a text file ending with the extension
".pl" (e.g. "flint.pl").

2. Start Prolog and load our database from the file.

A common syntax for loading a file is ['filename'].

a. Begin issuing queries based on the loaded information.

From this point on, everything you type is treated as a query, not a new
fact or rule!.

For instance, eats(fred,vegetables). is now asking whether or not


fred eats vegetables, not stating it as a new fact.

b. Note: the query that exits prolog is halt.

SYNTAX NOTE: all statements of properties, relationships, rules, and queries


must end with a period -- the Prolog interpreter will not regard any of the above as
completed until you enter a period.

Example: to repeat our introductory example, we would first create a file (let's
say "flint.pl") containing our list of facts and rules:

eats(fred,meat).
eats(wilma,meat).
eats(betty,vegetables).
eats(wilma,vegetables).
eats(barney,meat).
eats(barney,vegetables).

carnivore(Individual) :- eats(Individual,meat).
omnivore(Individual) :-
eats(Individual,meat),eats(Individual,vegetables).
food(Thing) :- eats(Individual,Thing).

4
Once that is created, we can load the database of facts and rules from the file, then
begin issuing queries and getting answers.

In the example below assume > is the prompt:

>['flint.pl'].
flint.pl compiled

Yes
>food(meat).

Yes
>carnivore(fred).

Yes
>food(dirt).

No
>omnivore(Individual).

Individual = wilma

Yes

How queries are answered:

It is very important to understand how Prolog searches for answers to queries -


poorly designed rules and queries can lead to very slow searches or infinite loops,
and the results from queries are not always intuitive.

Here we'll just give a very brief explanation-by-example, we'll go into things in
greater detail later.

The omnivore rule we had essentially states:

For any Individual:


the Individual is an omnivore if
(the Individual eats meat)
and
(the Individual eats vegetables).

Suppose we are given the query "is fred an omnivore?"

omnivore(fred).

In trying to solve this query, Prolog does the following:

o search for all the relationships which match the goal, i.e. everything in our
database that begins with omnivore(...)

5
o if we encounter a fact in our database that explicitly states
omnivore(fred). then we can quit and return Yes as soon as we
encountered it.
o if we encounter a rule of the form omnivore(...variable...) :- ...
then we can say fred is an omnivore if we can substiture fred for the
variable (throughout the rule) and still satisfy all the clauses on the right
side of the rule

In the case of our example, the rule with this form is

omnivore(Individual) :-
eats(Individual,meat),eats(Individual,vegetables).

and after substituting we want to check if eats(fred,meat) AND


eats(fred,vegetables).

Thus we have two new goals to try to satisfy to check out this rule, and we
begin searching the database for facts and rules beginning with eats(...,
trying to substitute "fred" and "meat" into the rules to continue the query.

If we cannot prove eats(fred,meat) then that clause fails, and hence the
rule fails as well, so we must continue on checking for other facts/rules
defining omnivores.

o As soon as we satisfy one of the facts or rules that prove fred is an


omnivore we can stop and return Yes
o If we go through all our facts and rules and cannot prove fred is an
omnivore then we return no.

Observe again that a "No" result doesn't mean something isn't true, it only means
that it cannot be proved from the given set of facts and rules.

For instance, the clause not(X = Y) is satisfied if from the database we cannot
prove X and Y are the same thing.

That is a very different thing from saying X and Y are actually different.

Negation example, from Sebesta (p. 631):

Suppose we have the following facts and rule:

parent(bill,jake).
parent(bill,shelley).
sibling(X,Y) :- parent(M,X),parent(M,Y).

I.e. two entities are siblings if they have a common parent.

6
This causes a problem if we look for a pair of siblings with the query
sibling(A,B).

The search for an answer goes as follows:

o the sibling rule is found, and substituting A,B for X,Y we come up with
the new pair of goals parent(M,A) and parent(M,B)
o In trying to satisfy the first goal, we hit parent(bill,jake), and as a
result try substituting bill for M and jake for A.
o Now we try to satisfy the second goal with this substitution, i.e.
parent(bill,B)
o In trying to satisfy this goal, we again hit parent(bill,jake), and
substitute jake for B
o We have now satisfied both goals using bill,jake,jake for M,A,B
respectively
o Since our goals are all satisfied, we respond with our final answer
o A = jake
o B = jake
o Yes

essentially saying jake is his own sibling

A logical way to avoid this situation is to use the NOT operator, to specify that
two entities are siblings if they have the same parent but are not (provably) the
same entity

sibling(X,Y) :- parent(M,X),parent(M,Y).

Predicate Calculus

Propositions are essentially logical statements which may be either true or false.

Predicate calculus gives us a method for expressing and manipulating collections of


propositions.

General forms of predicate calculus allow a tremendous variety of ways to express


logically equivalent statements such as

 A is B if C is true
 if C is true then A is B
 if A is not B then C is not true
 etc

While this freedom of expression is a strength of predicate calculus, it poses serious


problems when implementing programming languages based upon predicate calculus.

7
As a result, logic programming languages such as Prolog are based upon a restricted
version of predicate calculus

The goal of a language designer being to pick a restricted version which still allows
expression of all logical possibilities yet is easily implemented

Terminology:(informal)

 A simple term is either a constant or a variable


 A constant is any named entity, such as fred, sky, rock, verb, etc.

The most noteworthy attribute of constants is that they are distinguishable one
from another

 A variable, much as with variables in other languages, can be assigned the value
of different entities
 A compound term consists of a functor (the name or symbol representing a
function or relation) and an ordered list of parameters
 An atomic proposition is a proposition which consists of a set of compound
terms
 A compound proposition consists of two or more atomic propositions
 Propositions can be stated in one of two modes: either as a statement of fact or as
a query
 A Horn clause is a proposition in the following form: either
o A single atomic proposition (e.g. brown(dirt)), or
o A single atomic proposition on the left-hand-side of the clause, followed
by the if symbol followed by the logical AND of one or more atomic
propositions on the right hand side, e.g.:

PROLOG - in greater detail

 Term: a Prolog term can be any one of the following


o Constant: constants have one of two forms:
 Atom: an atom can be either
 an alphanumeric string (underscores permitted) which
begins with a lowercase letter, or
 an ascii string enclosed in apostrophes
 Number: numbers can be either integers or reals
o Variable: variables are alphanumeric strings (underscores permitted)
which begin with an uppercase letter.

Variables are like variables in other languages in that they can be


instantiated with a value, and that value can be altered or re-instantiated
during backtracking.

8
They are unlike more traditional variables in that if a variable has been
instantiated you cannot explicitly assign a new value. Thus statements like
X is X + 1 are not valid.

o Structure: structures (or relations) consist of a functor (the symbol or


name for the structure) followed by an ordered parameter list enclosed in
parentheses.

Note that we can use the structure to describe/define data types and
manipulate them.

For example, suppose our knowledge base is

% define the origin point


origin(point(X,Y)) :- X =:= 0, Y =:= 0.

% rule to determine if a point is within Radius of origin


near_origin(point(X,Y), Radius) :- X*X+Y*Y < Radius*Radius.

% rule to determine a point's distance from the origin


distance(point(X,Y), Distance) :- Distance is
sqrt(X*X+Y*Y).

We would obtain the following results to queries:

>origin(point(0,0)).
Yes
>origin(point(1,1)).
No
>origin(0,0).
No
>near_origin(point(1,1),1).
No
>near_origin(point(1,1),2).
Yes
>distance(point(3,4),D).
D = 5
Yes
>near_origin(point(1,1),R).
WARNING: Arguments not instantiated

(Note the last query cannot be handled, as it tries to use the uninstantiated
variable Radius in evaluating the expression X*X+Y*Y < Radius*Radius)

 Goals: goals have the same syntactic form as structures


 Rules: a rule consists of a head, with the same syntactic structure as goal, and
may include an if symbol, :-, followed by a series of goals separated by commas
(representing logical AND) or semi-colons (representing logical OR).
 head_name(Parameter) :- goal1(X), goal2(a,b), goal3(C,d).

9
 Bindings: both values and types are dynamically bound to variables in Prolog.
The temporary binding of values is referred to as instantiation, and takes place
only during the resolution process.

Unification

Unification in Prolog is the matching of values, and can be carried out either explicitly: X
= Y. or implicitly through parameters.
Suppose our knowledge base contains the fact even(X) :- divides(2, X).,
and we issue the query divides(Y).

X and Y are implicitly unified.

Unification is a symmetric operation - you are tying two elements together, X = Y is


exactly the same as Y = X.

Thus unification is NOT the same as assignment,

The following rules determine where unification can occur:

 any value can be unified with itself, e.g. a(b) = b(a)


 any two variables can be unified, effectively giving two different names to refer to
the same variable from this point forward, e.g. X = Y
 A variable can be unified with a value, thus instantiating the variable.

The variable can be either fully or partially instantiated:

X = entity. % X is fully instantiated,


% has a specific value
X = rel(Y). % X is only partially instantiated,
% as Y is a variable

 Two values containing variables can be unified if the constituent variables can
also be unified to values that make the two values the same. E.g.:
 lesser(3, Num1) = lesser(Num2, bignumber(X)).
 % if we unify 3 = Num2
 % and Num1 = bignumber(X),
 % we would have lesser(3, bignumber(X)) = lesser(3,
bignumber(X))
 % so the statements really can be unified

When can unification take place?

Compare a goal and the head of a rule, parameter by parameter.

f(X, b)
f(c, D)
---------------------
Difference: <X,c>, <b,D>
10
In each of these two cases, unification is allowable, giving f(c,b)

Resolution is the process used to try to carry out unification during a Prolog query, and
instantiation takes place as a direct result.

Resolution examples

As discussed earlier, resolution is the process of trying to apply facts and rules from the
knowledge database to try and prove the truth of a query.

If resolution fails to prove a query is true, then the query is said to fail.

Here we give four examples of resolution:

 Example 1: suppose our knowledge base is


 cloudy(today).

Suppose we issue the query cloudy(today).

To respond to the query, Prolog searches through the knowledge base for a fact or
rule which can tell us if it is cloudy today.

Facts and rules are considered in the same order as they appear in the knowledge
base.

In this case, the first rule encountered tells us directly that cloudy(today) is true,
so a response of Yes is generated.

 Example 2: suppose our knowledge base is


 rainy(yesterday).
 rainy(today).
 cloudy(today) :- rainy(today).

Suppose we again issue the query cloudy(today).

Again we search through the database, but this time we encounter a rule for
cloudy rather than a directly applicable fact.

Specifically, today is cloudy if today is rainy.

We now have a new goal, if we can prove rainy(today) then we have satisfied
the necessary conditions for cloudy(today) to be true.

A new search of the knowledge base begins, looking for facts or rules to prove
rainy(today)

In this case, the second fact examined specifies that, yes, today is rainy.
11
Thus our "subgoal" is satisfied, and (consequently) our original goal is also
satisfied, and a response of Yes can be generated to the query.

 Example 3: suppose our knowledge base is


 rainy(yesterday).
 rainy(today).
 cloudy(Day) :- rainy(Day).

Again, let us make the query cloudy(today).

In this case, the first applicable rule (i.e. that has cloudy as a head and one
parameter) is cloudy(Day) :- rainy(Day).

Because Day is a variable, and we are looking for the specific value today, we
can instantiate the variable Day with the value today, giving us the rule

cloudy(today) :- rainy(today).

>>From this point the process continues exactly as in the previous example, i.e.
try to satisfy subgoal rainy(today) and succeed because of the applicable fact in
the knowledge base.

 Example 4: suppose our knowledge base is


 rainy(today).
 rainy(yesterday).
 cloudy(Day) :- rainy(Day).

Now let's make the query cloudy(tomorrow) to see what happens when a query
fails.

As in the previous example, the applicable rule is cloudy(Day) :- rainy(Day).


and we instantiate variable Day with value tomorrow, giving us the new rule

cloudy(tomorrow) :- rainy(tomorrow).

We now search the knowledge base for facts or rules to prove our subgoal,
rainy(tomorrow).

No such facts or rules can be found in the database, so our subgoal fails, and
hence this rule fails to apply.

No other applicable facts or rules can be found in the knowledge base, so our
query as a whole also fails.

Design decisions

There are several key decisions in implementing a logic programming language:

12
 In what order should you consider the facts and rules in the knowledge base?

In a "pure" logic programming language, the programmer would only have to list
the available facts and rules -- without any regard to the order they are listed in.

(After all, facts are facts and rules are rules).

It should be the up to the system to apply the facts and rules in an appropriate
order.

Unfortunately, this would make implementation of the system very difficult, and a
much simpler implementation can be attained if the system simply considers facts
and rules in the order in which they are presented.

This creates problems for the programmer, however. Consider the following
knowledge base:

factorial(N,FactorialN) :- A is N-1, B is FactorialN/N,


factorial(A,B).
factorial(1,1).

In theory, this is an acceptable recursive description of factorials:

o the factorial of 1 is 1, and


o FactorialN = N! if (FactorialN/N = (N-1)!)

In practice, suppose we issue the query factorial(1,1).

Because the knowledge base contents are searched in order for every goal, the
following happens:

o we try to apply the rule first, i.e. unify 1 with N and 1 with FactorialN,
giving the rule
o factorial(1,1) :- A is 1-1, B is 1/1, factorial(A,B)
o when A and B are evaluated, this gives us a new subgoal of
factorial(0,1).
o when we try to prove this subgoal, we again find the rule first and try to
apply that, giving
o factorial(0,1) :- A is 0-1, B is 1/0, factorial(A,B).
o As you can see, we clearly are not obtaining the desired results

If we simply reverse the order within the knowledge base the query will work:

factorial(1,1).
factorial(N,FactorialN) :- A is N-1, B is FactorialN/N,
factorial(A,B).

Another issue is that in Prolog each time we begin processing a new subgoal we
start searching the knowledge base from the beginning.
13
Suppose we have a knowledge base with thousands of facts, and very near the end
are two facts that are critical to many queries.

Each time we need to apply either of those two facts we must first search the
entire knowledge base first, trying (unsuccessfully presumably) to apply any facts
or rules headed with the same functor.

Thus the Prolog programmer has to be very careful about the order of facts and
rules in the knowledge base to ensure that queries are handled correctly and
efficiently.

 Should you use forward chaining or backward chaining?


o Backward chaining (as used in Prolog) starts with a goal and attempts to
find facts and rules which might satisfy that goal.

If a rule is used, then the RHS clauses of it become subgoals, and


backward chaining must also be applied to these to try to work backwards
to some set of suitable facts.

Backward chaining works well when the number of possible ways to


answer a query is relatively small.

o Forward chaining starts with the set of available facts, and begins
applying rules to these with the hope of working "forward" to ultimately
arrive at the desired goal.

Forward chaining works well when the number of possible ways to answer
a query is relatively large (giving you a greater chance of quickly working
forward to a establish a match).

 When rules are applied, in what order should you consider the subgoals, and
their subgoals, etc...?

As with the ordering for facts and rules, in a "pure" logic programming language
the order in which goals are evaluated should not be a concern of the programmer.

However, implementation again becomes very difficult if the system is expected


to deduce an optimal (or at least reasonable) order for evaluation.

Suppose we have a rule with multiple clauses on the RHS, e.g.

A :- B,C,D.

There are two issues to concern ourselves with:

o What order should we evaluate subgoals B, C, and D in?

14
In Prolog, they are simply handled in left-to-right order.

o Should we wait until we have a success/fail from B before we start


evaluating C (and similarly wait until B and C are evaluated before
starting on D),

Or, should we start evaluating all three simultaneously?

This is referred to as depth-first versus breadth-first exploration.

Picture our set of goals and the subgoals they depend upon as a tree of
possibilities:

A
/|\
/ | \
B C D
/|\ \ |\
E F G H I J

Do we fully explore one subtree first (depth-first), or try and explore the tree level
by level (breadth-first)?

Breadth-first search requires we maintain more information about the searches


currently in progress - we typically have many more separate branches and goals
to keep track of.

Which search is faster depends on the answer and the tree - at what point(s) in the
tree do we identify the success/failure of subgoals and the overall goal, and how
much of the tree does each approach search before reaching the key point(s).

The Prolog approach is to use depth-first search, primarily because the


memory/storage requirements are significantly smaller.

15
LAB Exercise

Download a free version of Prolog and compile the following Program.

parent_of(peter,bob).

parent_of(tom,bob).

parent_of(tom,liz).

parent_of(bob,ann).

parent_of(bob,pet).

parent_of(pet,jim).

male(peter).

male(tom).

male(jim).

male(bob).

female(ann).

female(liz).

female(pet).

father_of(X,Y):-Parent_of(x,y), male(X).

mother_of(X,Y):-Parent_of(x,y), female(X).

grandfather(X,Y):-parent_of(X,Z),parent_of(Z,Y),male(Z).
grandmother(X,Y):-parent_of(X,Z),parent_of(Z,Y),female(Z).

sister_of(X,Y):-parent_of(Z,X),,parent_of(Z,Y), female(X).

brother_of(X,Y):-parent_of(Z,X),,parent_of(Z,Y), male(X).

Query the following and see the whether you can get the required results

father_of(X,Y.

mother_of(X,Y.

grandfather(X,Y).

grandmother(X,Y).

sister_of(X,Y).
16
brother_of(X,Y).

17

You might also like