IBM ILOG Solver User's Manual > Extending the Library > Writing a Constraint: Allocating Frequencies > Writing your own constraint

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:

class MyConstraint : public IlcConstraintI {
     ... // parameters for the constraint
public :
    MyConstraint(IloSolver solver, ... args ...); // constructor
    ~MyConstraint(){} // destructor; usually empty
    void post();
    void propagate();
}


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.

class IlcDiffConstraint :public IlcConstraintI {
    IlcIntExp _x, _y;
  public:
    IlcDiffConstraint(IloSolver solver, IlcIntExp x, IlcIntExp y);
    ~IlcDiffConstraint(){}; // empty destructor

    virtual void propagate ();
    virtual void post();
    virtual IlcBool isViolated() const;
};

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:

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.)

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());
}