IBM ILOG Solver User's Manual > Extending the Library > Writing a Constraint: Allocating Frequencies > Advanced issues: Propagating constraints after modifying variables |
Advanced issues: Propagating constraints after modifying variables |
INDEX
![]() |
Suppose that you have a constraint C involving the variables x, y, z, and t. Suppose that x is modified, then C is propagated. Now if the modification of x leads to a modification of y, z, and t, these variables are propagated and each of those variables will propagate C again, even if these variables have not been modified by constraints other than C.
Imagine that you want to avoid these repeated propagations because you happen to know that the propagate
member function of C is costly. Also imagine that you want to propagate C once for all the modifications of the variables involved in C but not for the modifications of each variable individually.
There is a mechanism in Solver that meets these aims. It is possible to have constraints that are not propagated within demons.
Such a constraint is not propagated after each modification of each variable; rather, it is propagated after all its variables have been propagated.
In fact, in Solver there are two mechanisms for propagating the modifications of variables.
propagate
function of some constraint known as a global constraint is called.
If, after the termination of a propagate
member function, a variable has been modified, you immediately return to the first level. This means that the process at the first level has greatest priority.
In the second level, a global constraint (say, ct
) is pushed by a call to the push
member function, like this ct->push();
.
This member function says to Solver that ct
is a global constraint and must be propagated by the second level mechanism. This member function must be called within a demon.
Consider the following example simplified for illustration: you would like to define the constraint x + y = s without considering modifications of s.
The code of that constraint could be something like this:
Now you add a demon for each variable x
and y
, and that demon is triggered when the boundary of x
or y
is modified (that is, when a range event occurs). Let's say this demon is called MySumDemon
, and it takes the global constraint as a parameter. Here is the code for its post
member function:
void MySum::post(){ _x.whenRange(MySumDemon(getSolver(),this)); _y.whenRange(MySumDemon(getSolver(),this)); } |
Now here is the code of the demon. Instead of calling a particular function of ct
that you define, you call the push
member function, like this:
ILCCTPUSHDEMON(MySumDemon, MySum){ ct->push(); } |
How does this tactic differ from the conventional propagation mechanism? In this case, the demon is called, but the propagate
member function of MySum
is not called immediately. Rather, ct
is pushed. When there are no more demons to trigger, the constraints that have been pushed are called. Furthermore, in this way, a constraint is propagated only once even if you push it several times.
If you want to refine the propagation mechanism, for example, if you want to treat the variables that have been modified in a special way, you have to manage that treatment by using internal data structures within the constraint. For example, suppose that if x is modified, you want to call your own function propagateX()
and if y has been modified, you want to call your own propagateY()
. To do so, you add reversible Boolean data members to MySum
, like this:
IlcRevBool _xIsModified IlcRevBool _yIsModified |
Now you define special demons for x
and for y
, like this:
Now the propagate
member function looks like this:
void MySum::propagate(){ if (_xIsModified){ propagateX(); _xIsModified=IlcFalse; } if (_yIsModified){ propagateY(); _yIsModified=IlcFalse; } } |
Thus you have created a global constraint that is propagated only once and propagated only after the propagation of variables in that constraint.
© Copyright IBM Corp. 1987, 2009. Legal terms. | PREVIOUS NEXT |