| IBM ILOG Solver User's Manual > Extending the Library > Writing a Constraint: Allocating Frequencies > Writing your own constraint |
Writing your own constraint |
INDEX
PREVIOUS
NEXT
|
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 |