IBM ILOG Solver User's Manual > Extending the Library > Writing a Constraint: Allocating Frequencies > Writing your own constraint |
Writing your own constraint |
INDEX
![]() |
Obviously, a constraint is an object in Solver. More precisely, a constraint in Solver is an instance of a class with two pure virtual functions, propagate
and post
, and a third virtual function, isViolated
. The virtual function propagate
implements the invariant of the constraint; post
defines on which events propagate
executes; and isViolated
returns IlcTrue
if the constraint cannot be satisfied. The virtual function isViolated
will be called if the constraint is to be used as a metaconstraint.
As indicated, Solver lets you define new classes of constraints. You do so by defining a subclass of IlcConstraintI
. (See the section "Implementation classes and handles" for an explanation of the I
at the end of the name.) The data members of this subclass include, among others, the constrained variables on which the constraint is posted.
Consequently, the definition of a new class of constraint looks something like this:
To clarify how to define a new class of constraint, let's look at the constraint x != y
. The parameters of this constraint are obviously the expressions x
and y
themselves.
The role of the constructor is to initialize the data members, like this:
IlcDiffConstraint::IlcDiffConstraint(IloSolver solver, IlcIntExp x, IlcIntExp y) : IlcConstraintI(solver), _x(x), _y(y) {} |
The invariant of x != y
is easy to determine, as indicated earlier:
x
is bound to a value, the domain of y
does not contain this value;
y
is bound to a value, the domain of x
does not contain this value.
The virtual member function propagate
implements the reduction of the domains. Using what you've already written, you would get this:
void IlcDiffConstraint::propagate () { if (_x.isBound()) _y.removeValue(_x.getValue()); if (_y.isBound()) _x.removeValue(_y.getValue()); } |
The second virtual member function to write is post
. It connects the constraint to its arguments by specifying the events that set it off. For integer expressions, there are three kinds of attachment, one kind for each type of event. For the class IlcIntExp
, these member functions are as follows: (The class IlcConstraint
derives from the class IlcDemon
.)
whenValue(IlcDemon ct)
attaches the demon ct
to the value event of the expression under consideration. Every time the expression takes a unique value, the execute
function for ct
is called.
whenRange(IlcDemon ct)
attaches the demon ct
to the range event of the expression under consideration. Every time the domain of the expression gets a new boundary, the execute
function for ct
is called.
whenDomain(IlcDemon ct)
attaches the demon ct
to the domain event. Every time the domain of the expression is modified, the execute
function for ct
is called.
For a constraint, the execute
member function calls propagate
.
In our example of the constraint x != y
, you want to propagate the constraint every time x
or y
is bound. Here's the code for doing that:
void IlcDiffConstraint::post(){ _x.whenValue(this); _y.whenValue(this); } |
If you want to use our constraint as a metaconstraint, you may want to define under what conditions IlcDiffConstraint
is definitely not satisfied. In our example, x != y
is violated if, and only if, variables x
and y
are bound to the same value. Hence, the definition of isViolated
:
IlcBool IlcDiffConstraint::isViolated() const { return (_x.isBound() && _y.isBound() && _x.getValue()==_y.getValue()); } |
© Copyright IBM Corp. 1987, 2009. Legal terms. | PREVIOUS NEXT |