Prolog/Constraint Logic Programming

We've seen that in Prolog, a variable can be either bound (have a value, possibly another variable) or free (have no value). Constraint logic programming (CLP) extends the notion of a logical variable by allowing variables to have a domain rather than a specific value. Variables can also be constrained, which means that their value must abide by certain rules specified by the programmer. Constraint logic programming makes it possible to solve complex combinatorial problems with a minimum amount of code.

Introduction: the dif/2 constraint
Often, Prolog programming revolves around constraints on the values of variables, embodied in the notion of unification. For example, the goal  can be said to constrain the variable   to take on the value. Unification is of course more general than this, since  constrains two variables to take on the same value, while   constrains   in yet a third way: it may take on any of an infinite set of values that includes   and , but the value it takes on must match the "template".

Standard unification is limited, though, in that it can only put a "positive" constraint on a variable. If we want to state that  may be bound to any value other than those matching , then we can use unification together with negation, but we have to be very careful. As we've seen in an earlier chapter, we can sometimes use negation as failure to express inequality, but this is not always reliable:

This non-monotonic property of  forces us to reorder goals to get variables bound at just the right time, making code harder to read, write and understand.


 * Exercise. Write a predicate that succeeds when its input is a list containing only unique values. E.g.  should succeed, but   should fail because   appears twice. How does your predicate deal with  ? How about  ?

Enter the  predicate. This predicate, available in SWI and SICStus (but not strictly standard Prolog) allows us to constrain two variables to be different. The following SWI-Prolog session shows its operation:

Contrast this with the result of the other inequality predicates  and.


 * Exercise. Implement the  predicate again, but this time use  . What is Prolog's response to the query  ? How many constraints does it report?


 * Exercise. Write a predicate  that constrains its argument to not be of the form , i.e. not a one-argument term with functor   Hint: use the   ("univ") operator.

Finite-domain constraints
We now turn to a much more powerful constraint handling system: constraint logic programming on finite domains, also known as CLP(fd). To use this, we must load a library. In SWI-Prolog, that's:

Do you remember how arithmetic in Prolog fails when used with insufficiently instantiated variables? Try this for a starter:

Even though there is logically one possible answer to this query (X=3), Prolog cannot infer it. We can get the right answer by rearranging the conjuncts of the query:

CLP(fd) is a lot smarter than Prolog when it comes to arithmetic. Try this (note: ECLiPSe users should replace in by ::):

This succeeds with the only correct answer, X=3. The predicate #>/2, when given two variables, posts the constraint that it left argument should be greater than its right argument. CLP(fd) holds this constraint in its constraint store until it knows enough about the variables' possible values to infer further information from it. The moment that Y gets the value 2, CLP(fd) infers that X can only have the value 3.

What happens when we bind Y to 1 instead?

Rather than start backtracking over the possible values of <tt>X</tt>, CLP(fd) constrains <tt>X</tt> to a smaller domain and stops. To have CLP(fd) search for possible assignments to variables, we must tell it do so explicitly. The simplest way to do that is with the predicate <tt>label/1</tt>:

What we've seen so far isn't very interesting, because there was only one constraint variable. Let's have a look at a problem with eight variables.

Send more money
The send more money puzzle is the quintessential example of a constraint problem. It amount to assigning different digits 0 through 9 to the variables <tt>[S,E,N,D,M,O,R,Y]</tt> such that the sum

SEND + MORE = MONEY

is solved; <tt>S</tt> and <tt>M</tt> should both be greater than zero. Instead of typing to the Prolog prompt, let's make a proper Prolog module.

<tt>ins/2</tt> is the same as <tt>in/2</tt>, except that it sets the domains of several variables at the same time. (ECLiPSe: use <tt>::</tt>, SICStus: use <tt>domain(Vars,0,9)</tt>). <tt>all_different</tt> (which may be called <tt>all_distinct</tt> in some implementations) is logically equivalent to posting disequality constraints (<tt>#\=</tt>) on all pairs of variables in <tt>Vars</tt>, but is shorter and possibly more efficient. Note that we can express the sum as one constraint over all eight variables at the same time. Let's try this out:

Even without labeling variables, CLP(fd) has inferred the values of three variables, has simplified the sum and put tighter bounds on the remaining five variables. Ofcourse, we want values for all our variables:

This should run instantly. Exercise: implement the puzzle in Prolog without CLP(fd), and notice how long it takes to run.