?- X in 0..1, Y in 2..4, Z #= max(X,Y).
clpz:(X in 0..1), clpz:(Z#=max(X,Y)), clpz:(Z in 2..4), clpz:(Z#>=Y), clpz:(Y in 2..4). % unexpected.
?- X in 0..1, Y in 2..4, Z #= max(Y,X).
Y = Z, clpz:(X in 0..1), clpz:(Y in 2..4).
This allows subsequently invoked constraints to take the entire
filtering results into account, instead of being invoked when the
obtained information is not yet entirely used.
The SICStus-style attributed variables mechanism of Scryer Prolog
automatically prevents very subtle interaction problems that can arise
in systems that do not give all variables that are involved in a
unification an opportunity to schedule their propagators.
An example of such a subtle interaction is:
?- tuples_in([[A,C,B]], [[3,1,3],[4,2,4]]),
global_cardinality([A,B,D], [3-1,4-2]),
A = 4.
A = 4 causes pgcc_check/1 and pgcc/2 to be queued in the fast and slow
queue, respectively. In the fast queue, there is also rel_tuple/2,
which is worked off after gcc_check/1 and simultaneously instantiates
both C and B (to 2 and 4, respectively). Instantiation of C schedules
do_queue//0 from verify_attributes/3. Note that C does not participate
in the global_cardinality/2 constraint.
Critically, B also gets an opportunity to schedule its propagators in
this case, so another gcc_check/1 is run before gcc_global/2!
This allows subsequently invoked constraints to take the entire
filtering results into account, instead of being invoked when the
obtained information is not yet entirely used.
It speeds up programs such as the one in:
https://github.com/triska/clpz/issues/26
If reification constraints (such as reified equality) are triggered
here, then they may wish to disable this propagator and remove
attributes from auxiliary variables. If the pexp/3 propagation is
interrupted for that purpose, then the attributes will be
unintentionally reattached by the following fd_put/3 calls in this
propagator. We must ensure that this propagator completely finishes,
so we queue the triggered propagators for later processing.
geq/2 implements propagator activation outside the queue, and thus
should not be used in propagators in the way it was used here.
pexp/3 by itself may not seem particularly important. However, it can
arise by metamorphosis from Var*Var. Example:
?- A#<==> -1#=C*C, C in 0..1.
A = 0, clpz:(C in 0..1).
This addresses #2089.
This addresses all remaining cases from #2083, excepting (//)/2:
?- #\ 1#=(X*X)/0.
clpz:(X in inf..sup).
?- #\ 1#=(X+X)/0.
clpz:(X in inf..sup).
Still remaining:
?- #\ 0#=(Y// -1)/0.
clpz:(-1*Y#=_A).
The (+)/1 prefix in get_atts/2 at line 4219 by itself already causes a
greater than 15% slowdown for the benchmark shown in #1730:
?- N #= 2^14,
time(((between(1, N, _),
X #\= Y,
false)
; true)).
The performance impact is not a good reason to remove the optional
(+)/1 prefix! Performance issues should be addressed at the root, in
this case get_atts/2 (#1962). We should never manually work around
performance issues in built-in predicates.
In contrast, readability is a good argument, and I find the calls
slightly easier to read without the optional (+)/1 prefix.
The prefix is now consistently omitted when possible.