Image goes here
Logic Programming
CptS 355 - Programming Language Design
Washington State University

Logic Programming

History of logic programming

Logic programming is based on rules of logical inference going back to Aristotle. In 1965 the resolution algorithm was discovered that provided a convenient implementation of logical inference letting computers construct proofs in the sense of Aristotle, Euclid, Frege, and Russell and Whitehead. The resolution idea quickly led to the creation of the Prolog language for logic programming.

The goal of this lecture is to become familiar with Prolog in order to write simple programs. There is much more to Prolog than we will cover.

Terminology

constant

A constant is an attribute value. By convention all constants start with lower-case letters. Some example constants are: joe, cp2001, sue, cp2003, etc.

variable

A variable is like a variable in a mathematical equation, it represents some constant that has yet to be determined. For example in the mathematical equation.
  X = Y + 3
The variable X is some unknown value, however, we know it is exactly 3 greater than the variable Y, which is also an unknown value. If we later determine that
  Y = 10
we then know that
  X = 13
Variables in Prolog are more like variables in mathematics than are normal programming variables in other languages. By convention variables always start with an upper case letter, e.g., Name, Code, Phone, X, Y, Z, etc.

fact

A fact is the same thing as a tuple in a relation. It is a true assertion about the enterprise being modelled. A ground fact has the following general form.
  predicatename(list of constants).
where predicatename is the name of a predicate. The fact is called ground because no variables appear in the fact. Ground facts and predicates are like tuples in relations. The name of the predicate is effectively just the name of a relation, and the list of constants comprise the values of a tuple within that relation.

rules

A rule is a way to derive new facts, that is, a rule is (part of) a query. All rules have the following general form.
 head :- body
where the head is a single predicate and the body is a comma-separated list of predicates. A general predicate is of the form.
  predicatename(list of constants, variables, or predicates)
and ground predicates are a special case. A rule has the following meaning in logic: the ':-' is an implication symbol (logical IF), and the ',' corresponds to logical AND. So let's assume we have the rule.
   Q :- P, R
This rules means IF P AND R THEN Q, or in other words, if P is true then we can conclude that Q is true, or in other words
  P => Q
Modus Ponens is a proof rule in logic that let's us prove new facts. It is the only such proof rule in Prolog. Modus Ponens has the following general form. Given that
  • 'P => Q' is a true statement and
  • 'P' is true
  • then we know that 'Q' is true.
Or in other words, in Prolog, if we have some facts that support the body of a rule, we can conclude that the head is true.

Unification

Unification is the process of making one predicate the same as another. The end result of unification is that either the two predicates fail to unify, or they unify with perhaps some binding of constants to variables, or variables to variables. Two predicates are said to unify if
  • they have the same name,
  • they have the same number of attributes, and
  • each of their attributes unifies, left to right, using the following reasoning.
    • If both attributes are constants, they must be the same constant.
    • If one attribute is a constant, and the other a variable, then the variable is bound to the constant.
    • If both attributes are variables, they are bound to each other.
Let's try to unify some predicates. Do the following two predicates unify?
  1. sameSubject(joe, sue, Code)
  2. enrolledIn(joe, cp2001)
No they have different names, and a differing number of attributes. How about these?
  1. enrolledIn(joe, cp2001)
  2. enrolledIn(sue, cp2001)
No the first attributes in each are different constants. How about these?
  1. enrolledIn(joe, cp2001)
  2. enrolledIn(joe, cp2003)
No the second attributes in each are different constants. How about these?
  1. enrolledIn(Name, cp2001)
  2. enrolledIn(sue, cp2001)
Yes, with the caveat that the variable Name = sue (Name is bound to sue). How about these?
  1. enrolledIn(Name, cp2001)
  2. enrolledIn(sue, Code)
Yes, with the caveat that Name = sue and Code = cp2001. How about these?
  1. enrolledIn(sue, Code1)
  2. enrolledIn(sue, Code2)
Yes, with the caveat that Code1 = Code2. How about these?
  1. enrolledIn(X, X)
  2. enrolledIn(sue, Y)
Yes, with the caveat that sue = X = Y.

Queries

Prolog queries are evaluated in a top-down manner (from rule heads to rule bodies). This technique is called backward chaining. In backward chaining the head is unified, and then we try to prove each term in the body. An example will make the process clearer. Assume that we have the following set of facts.
    student(sue, 335-0904).
    student(joe, 335-4621).
    enrolledIn(joe, cpts355).
    enrolledIn(sue, cpts452).
    enrolledIn(joe, cpts460).
Further assume that we have one rule.
    full(Name, Code, Phone) :- student(Name, Phone), enrolledIn(Name, Code).
Finally, suppose our query is to find out the phone number(s) and names of those enrolled in cpts355.
    ? - full(X, cpts355, Y)
To evaluate this query, we will try first try to unify it with the head of some rule. Well, we have only one rule, so it is pretty easy.
    full(Name, Code, Phone)
                ^  
                |     
      full(X, cpts355, Y)
It produces the bindings X = Name, joe = Code, and Y = Phone. Now let's try to prove the first term in the rule.
    student(sue, 355-0904)
unifies with
    student(X, Y)
producing the bindings X = sue, Y = 355-0904. We now try to prove enrolledIn(sue, cpts355). Nothing unifies with that since sue is not enrolled in cpts355. So our attempt to prove the body of the rule fails. We try again with the next student fact.
    student(joe, 355-4621)
unifies with
    student(X, Y)
producing the bindings X = joe, Y = 355-4621. We next try to prove enrolledIn(joe, cpts355). It unifies with the first enrollment fact, so we have proved full(X, cpts355, Y) with the bindings X = joe and Y = 355-4621. In a very real sense, the body of a rule can be viewed as a pattern. A combination of facts `matches' or `unifies' with the pattern only under certain assignments of values to the variables. These assignments are made as new fact are derived.
    full(Name, Code, Phone) :- student(Name, Phone), enrolledIn(Name, Code).
         |      |     |                 ^     ^                  ^     ^
        \|/    \|/   \|/                |     |                  |     |
     full(X, cpts355, Y)      student(joe, 355-4621), enrolledIn(joe, cpts355).

Factorial example

Look at factorial function in C:
/* assume x is non-negative */
int factorial (int x) {
  return ( x ? x*factorial(x-1) : 1 )
}
The C factorial program will compute factorial(x) given x. But remember that mathematicaly a function may be represented by its graph, and for factorial this would be
factorial = {(0,1), (1,1), (2,2), (3,6), (4,24)...}
How could the function be represented in logic programming?
factorial(0,1).
factorial(X,Y) :- X>0, Y=Z*X, W=X-1, factorial(W,Z).
With this definition of factorial, we can make a query like factorial(X,73572) to attempt to find X such that X! = 73572. (Note that there obviously is no such X). While this is convenient to program, it may take a long time to execute!

Resources

Wikipedia has decent (not great) articles on both unification and Prolog. The Prolog article has links to many implementations that you could experiment with. gprolog seems good enough for elementary use. One thing that stumped me for awhile was how to interactively enter facts and rules since the interpreter prompts for a query -- ?-. Enter the query [user]. After that you can enter facts and rules. When finished, enter an end-of-file (ctrl-D or ctrl-Z). That will take you back to the query prompt.
(c) 2003 Curtis Dyreson, (c) 2004-2006 Carl H. Hauser           E-mail questions or comments to Prof. Carl Hauser