User-defined, or ‘conceptual’ constraints can easily be defined as conjunctions of primitive constraints. For example, let us consider a set of products and the specification that allows them to be colocated in a warehouse. This should be done in such a way as to propagate possible changes in the domains as soon as this becomes possible.
Let us assume we have a symmetric relation that defines which product can be colocated with another and that products are distinguished by numeric product identifiers:
colocate(100, 101). colocate(100, 102). colocate(101, 100). colocate(102, 100). colocate(103, 104). colocate(104, 103). |
Suppose we define a constraint colocate_product_pair(X, Y)
such
that any change of the possible values of X or Y is propagated to the
other variable. There are many ways in which this pairing
can be defined in ECLiPSe. They are different solutions with different
properties, but they yield the same results.
We can encode directly the relations between elements in the domains of the two variables:
colocate_product_pair(A, B) :- cpp(A, B), cpp(B, A). cpp(A, B) :- [A,B] :: [100, 101, 102, 103, 104], A #= 100 => B :: [101, 102], A #= 101 => B #= 100, A #= 102 => B #= 100, A #= 103 => B #= 104, A #= 104 => B #= 103. |
This method is quite simple and does not need any special analysis; on the other hand it potentially creates a huge number of auxiliary constraints and variables.
By far the simplest mechanism, that avoids this potential creation of large numbers of auxiliary constraints and variables, is to load the Generalised Propagation library (propia) and use arc-consistency (ac) propagation, viz:
?- colocate(X,Y) infers ac
In this case we use the element/3
predicate,
that states in a list of integers that the element at
an index is equal to a value. Every time the index or the value is updated,
the constraint is activated and the domain of the other variable is updated
accordingly.
relates(X, Xs, Y, Ys) :- element(I, Xs, X), element(I, Ys, Y). |
We define a generic predicate, relates/4
, that associates the
corresponding elements at a specific index of two lists, with one
another. The variable I is an index into the lists,
Xs and Ys, to yield the elements at this index,
in variables X and Y.
colocate_product_pair(A, B) :- relates(A, [100, 100, 101, 102, 103, 104], B, [101, 102, 100, 100, 104, 103]). |
The colocate_product_pair
predicate simply calls relates/4
passing a list containing the product identifiers in the first argument
of colocate/2
as Xs and a list containing product identifiers
from the second argument of colocate/2
as Ys.
Behind the scenes, this is exactly the implementation used for arc-consistency propagation by the Generalised Propagation library.
Because of the specific and efficient algorithm implementing the
element/3
constraint, it is usually faster than the first
approach, using reified constraints.