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.