Constraints

In this section, we do not present constraints in their full generality but numerical constraints (the ones you are the most likely interested in).

A numerical constraint in IBEX is either a relation like \(f(x)<0\), \(f(x)\le0\), \(f(x)=0\), \(f(x)\ge0\) or \(f(x)>0\), where f is a function as introduced in the previous section. If f is vector-valued, then 0 must be a vector.

Surprisingly, constraints do not play an important role in IBEX. It sounds a little bit contraditory for a constraint programming library. The point is that IBEX is rather a contractor programming library meaning that we build, apply and compose contractors rather than constraints directly.

As a programer, you may actually face two different situations.

Either you indeed want to use a constraint as a contractor in which case you build a Ctc object with this constraint (the actual class depending on the algorithm you chose, as explained in the tutorial –by default, it is Forward-Backward–). Either you need to do something else, say, like calculating the Jacobian matrix of the function f. In this case, you just need to get a reference to this function and call jacobian. In fact, all the information inherent to a constraint (except the comparison operator of course) is contained in the underlying function so that there is little specific code related to the constraint itself.

For these reasons, the only operations you actually do with a constraint is either to read its field or wrap it into a contractor.

Class and Fields

The class for representing a numerical constraint is NumConstraint. The first field in this class is a reference to the function:

Function& f;

The second field is the comparison operator:

CmpOp op;

CmpOp is just an enum (integer) with the following values:

Op

def

LT

\(<\)

LEQ

\(\le\)

EQ

\(=\)

GEQ

\(\ge\)

GT

\(>\)

Creating constraints (in C++)

To create a numerical constraint, you can build the function f first and call the constructor of NumConstraint as in the following example.

Variable x;
Function f(x,x+1);
NumConstraint c(f,LEQ); // the constraint x+1<=0

But you can also write directly:

Variable x;
NumConstraint c(x,x+1<=0);

which gives the same result. The only difference is that, in the second case, the object c.f is “owned” (and destroyed) by the constraint whereas in the first case, c.f is only a reference to f.

Note that the constant 0 is automatically interpreted as a vector (resp. matrix), if the left-hand side expression is a vector (resp. matrix). However, it does not work for other constants: you have to build the constant with the proper dimension, e.g.,

Variable x(2);
NumConstraint c(x,x=IntervalVector(2,1)); // the constraint x=(1,1)
cout << "c=" << c << endl;

The display is:

c=(_x_0-([1,1] ; [1,1]))=0

In case of several variables, the constructor of NumConstraint works as for functions. Up to 6 variables can be passed as arguments:

Variable a,b,c,d,e,f,g;
NumConstraint c(a,b,c,d,e,f,g,a+b+c+d+e+f+g<=1);

And if more variables are necessary, you need to build an Array<const ExprSymbol> first, like here.

Note: There is currently the important restriction that inequalities can only be formed with real-valued (called “scalar”) functions. It could be possible, in theory, to write \(f(x)\le0\) with f vector-valued by interpreting the operator componentwise but this is not supporter by Ibex.