To show the basic ideas, we will simply reimplement a constraint that
already exists in the ic solver, the inequality constraint.
We want a constraint ge/2 that takes two ic variables (or numbers)
and constrains the first to be greater or equal to the second.
The behaviour should be to maintain bounds-consistency:
If we have a goal ge(X,Y), where the domain of X is X{1..5} and
the domain of Y is Y{3..7}, we would like the domains to be updated such
that the upper bound of Y gets reduced to 5, and the lower bound of X
gets increased to 3. The following code achieves this:
ge(X, Y) :-
get_bounds(X, _, XH),
get_bounds(Y, YL, _),
( var(X),var(Y) ->
suspend(ge(X,Y), 0, [X->ic:max, Y->ic:min])
;
true
),
X #>= YL, % impose new bounds
Y #=< XH.
We have used a single primitive from the low-level interface of the
ic library: get_bounds/3, which extracts the current
domain bounds from a variable. Further, we have used the information
that the library implements trigger conditions called min
and max, which cause a goal to wake up when the lower/upper
bound on an ic variable changes.
Note that we suspend a new instance of the ge(X,Y) goal before
we impose the new bounds on the variables. This is important when the
constraint is to be used together with other constraints of higher
priority: imposing a bound may immediately wake and execute such
a higher-priority constraint. The higher-priority constraint may then
in turn change one of the bounds that ought to wake ge/2 again.
This only works if ge/2 has already been (re-)suspended at that time.