alldifferent
constraint can be
posted on that subset, thus supporting more powerful propagation.
This can be achieved by finding cliques in the graph whose nodes
are variables and edges are disequality constraints.
:- lib(ech). :- constraints neq/2. neq(X,Y) ==> sort([X,Y],List), clique(List), neq(Y,X). |
alldifferent
constraint is
posted, and the CHRs seek to extend this clique to
include another variable:
:- constraints clique/1. clique(List) ==> alldifferent(List). clique(List),neq(X,Y) ==> in_clique(Y,List), not in_clique(X,List) | sort([X|List],Clique), extend_clique(X,List,Clique). in_clique(Var,List) :- member(El,List), El==Var, !. |
X
and each other variable in the original
clique. This is done by recursing down the list of remaining
variables.
When there are no more variables left, a new clique has been found.
neq(X,Y) |
clique(Clique) |
p(a,f(Y),Y) <=> q(Y)
is really a shorthand for the guarded
rule:
The guard is “satisfied” if, logically, it is entailed by the constraints posted already.p(A,B,C) <=> A=a, B=f(Y), C=Y | q(Y)
:- constraints p/2. p(X,Y) <=> ic:(X $> Y) | q(X,Y). |
Although the guard is clearly satisfied, the CHR implementation cannot detect this and?- ic:(X $> Y), p(X,Y).
p(X,Y)
does not fire.
If the programmer needs the entailment of inequalities to be detected,
it is necessary to express inequalities as CHR constraints, which
propagate ic constraints as illustrated in the example
prec(S1,D,S2)
above.p(X,0)
does fire in the following example:
The implementation of this entailment test in ECLiPSe is to impose the guard as a constraint, and fail (the entailment test) as soon as any variable becomes more constrained. A variable becomes more constrained if:?- ic:(X $> 1), p(X,0).
CHRs suspend on the variables in the rule head. On waking the CHR tests if its guard is entailed by the current constraint store. The entailment test is efficient but incomplete, and therefore rules may fail to fire as early as they could in theory.
Figure 15.6: CHR Implementation