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

This section describes some useful tips, collected from users of Solver.

Exhaustive propagation

Constraint programming is more efficient when it effectively reduces the variable domains as early as possible.

In order to reduce the domains of constrained variables as quickly as possible, you have to carry out all propagation. You do so by propagating constraints in every direction. While predefined constraints respect this rule, you have to exercise care when writing a new constraint to insure that your user-defined constraints respect this rule as well.

Another way of looking at this issue is to see that constraints are not directed, but must be seen as equations in the widest sense of the term. The final results of the search for a solution must be the same, regardless of the order in which constraints are posted, even user-defined constraints.

Let's consider the constraint x + y = z. The domain of z must be modified when the domain of x or of y is modified. At the same time, you must not forget that the domain of x must be modified when the domain of y or of z is modified. A similar rule applies for y as well. This is what is meant by propagating in all directions.

More generally, when designing a new constraint class, you must not overlook any direction of propagation.

Using demons to propagate selectively

Let's look again at our IlcDiff example. Each time one of its variables is bound, isBound is checked against the two variables. You can avoid these redundant tests by using intermediate demons, that is, by posting different demons on the different variables. You might think of demons as goals without subgoals, that is, goals which return the null pointer.

You add two member functions to the class IlcDiffConstraint, each of them responsible for the propagation of the constraint on one variable when the other is bound. Here is the modification.

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;
    virtual IlcConstraintI* makeOpposite() const;
    virtual void metaPostDemon(IlcDemonI* expr);
    void xDemon(){
        _y.removeValue(_x.getValue());
    }
    void yDemon(){
        _x.removeValue(_y.getValue());
    }
};


Now you need to define intermediate demons on variables that call the new member functions. You do that in this way.

ILCCTDEMONO(IlcDiffCst_xDemon,IlcDiffConstraint,xDemon);
ILCCTDEMON0(IlcDiffCst_yDemon,IlcDiffConstraint,yDemon);

Finally, you change the post member function to take this modification into account, like this:

void IlcDiffConstraint::post(){
    _x.whenValue(IlcDiffCst_xDemon(getSolver(),this));
    _y.whenValue(IlcDiffCst_yDemon(getSolver(),this));
}

This new version of the IlcDiffConstraint is faster that the previous one since the isBound tests in propagate are avoided. The price to pay for this efficiency is the memory used by the two intermediate goals.