Implementing a lot of constraints with a lot of variables

Hello !

I'm implementing a code where a lot of constraints are defined thanks to functions using a lot of arguments.

My constraints use functions. The constraint is, for exemple, P(variables, data)>=0, where the dimensions of variable and data are large. Each time I implement a constraint, in fact,  I must implement two constraints because the measures are intervals - one constraint for the upper bound, another for the lower one.

So far, my code looks like that :
 


// The array that will contain all the contractors
vector<Ctc*> ctc;

//------------------------------------------------//

// Definition of all the variables, N being potentially large

Variable v[N];
    
// The ExprSymbol array

Array<const ExprSymbol> vars(N);

for (int i=0 ; i<N ; i++)
    vars.set_ref(i,v[i]);

//------------------------------------------------//

// The evaluation vector to give to the function in the constraint (ie containing the data)
const ExprNode* eval[2*N];
    
// Putting the variable vector v in the evaluation vector
for (int i=0 ; i<N ; i++){
    eval[i]=&vars[i];
}

//------------------------------------------------//

for (int eq = 0 ; eq < NbEq1 ; eq++){

    // Putting data

    for (int i=0 ; i<N ; i++)
        eval[N+i]=&ExprConstant::new_scalar(data[i]);

    //-----------//

    // Constraint upper bound

    Function* fu = new Function(vars,-P(eval));   // The function must verify f>=0
    NumConstraint* rel=new NumConstraint(*fu,GEQ);
    ctc.push_back(new CtcFwdBwd(*rel));

    // Constraint lower bound - a data value has been changed

    Function* fl = new Function(vars,P(eval));     // The function must verify f>=0
    NumConstraint* rel2=new NumConstraint(*fl,GEQ);
    ctc.push_back(new CtcFwdBwd(*rel2));

}

//------------------------------------------------//

// The same for other equations

for (int eq = 0 ; eq < NbEq2 ; eq++){

    // Putting data

    for (int i=0 ; i<N ; i++)
        eval[N+i]=&ExprConstant::new_scalar(data2[i]);

    //-----------//

    // Constraint upper bound

    Function* fu = new Function(vars,-Q(eval));    // The function must verify f>=0
    NumConstraint* rel=new NumConstraint(*fu,GEQ);
    ctc.push_back(new CtcFwdBwd(*rel));

    // Constraint lower bound - a data value has been changed

    Function* fl = new Function(vars,Q(eval));     // The function must verify f>=0
    NumConstraint* rel2=new NumConstraint(*fl,GEQ);
    ctc.push_back(new CtcFwdBwd(*rel2));

}

// The intersection of all the contracors

CtcCompo ctct(ctc);
    
// The final contractor is repeated

CtcFixPoint fp(ctct,step);

// Contraction

fp.contract(box);





 

My problem is that the contractors do not work, even for very simple constraints -such as a variable belongs to a certain interval-, when they are defined this way.

Must I declare new variable structures -vars and eval- for each NumConstraint ? Is there any problem in the definition of eval ?

Last, before realising the composition object, I implement some functions such as Function f(vars,v[0]); but there is a bug : it returns me "Symbol"_x_46" is not an argument of the function" where _x_46 is the first element of vars. Where is the problem ?

Dear Etienne,

The "Variable" class is very likely the source of your two problems.

This class was introduced only for simple usage and quicly creates a lot of confusion in more advanced examples.

Here is the reason: Each function must have its own set of symbols. They cannot be shared. To avoid re-creating symbols again and again for every function, the "Variable" class can be used. A "Variable" object automatically generates a new symbol each time it is referred to for creating a new Function. But this is not compatible with the constructor of Function that directly takes an array of symbols (the one you use).

Each time you create a function with an array of symbols, you have to re-build this array. Of course, to fill this array, you can resort to the Variable class, but this intermediate mechanism is now superfulous. To be clear, here are two equivalent pieces of code that create a function with an arbitrary number of arguments.

The first variant is:

int N=2; // could be any value

// Creates variables
Variable v[N];

// Creates the array of symbols for the function
Array<const ExprSymbol> vars(N);

// Each symbol is set to a variable
for (int i=0 ; i<N ; i++) vars.set_ref(i,v[i]);

// The function f
Function f(vars,vars[0]-vars[1]);

cout << f << endl;

The second variant is:

int N=2; // could be any value

// The array of symbols for the function
Array<const ExprSymbol> vars(N);

// create each symbol
for (int i=0 ; i<N ; i++) vars.set_ref(i,ExprSymbol::new_());

// The function f
Function f(vars,vars[0]-vars[1]);

cout << f << endl;

 

I prefer the second variant. Now, here is a piece of code that works and mimics what your are trying to do.

It generates contractors x<=0, ...x<=10 where x is 1 variable but could be N:

int main() {

    int N=1; // could be any value

    // The function P(x,y) (x will be the variables, y the data)
    // with, potentially, a lot of components in x and y (N big)
    Array<const ExprSymbol> xy(2*N);
    xy.set_ref(0,ExprSymbol::new_("x")); // string "x" is optional
    xy.set_ref(1,ExprSymbol::new_("y"));
    Function P(xy,xy[0]-xy[1]);

    // The array that will contain all the contractors
    vector<Ctc*> ctc;

    // Loop that create the constraints
    // P(x,0)<=0, ..., P(x,9)<=0,
    // and their associated contractors

    for (int i=0; i<10; i++) {

        // Create variables for x -> P(x,i)
        Array<const ExprSymbol> vars(N);
        vars.set_ref(0,ExprSymbol::new_("x")); // if N>1, create a loop "for (int j=0; j<N; j++) ..."

        // Create the arguments of P (concatenation of variables and data)
        // =========================================
        // Before 2.1.6
        //    const ExprNode* args[2];
        //
        //    args[0]=&vars[0];
        //    args[N]=&ExprConstant::new_scalar(i);
        // =========================================

        // =========================================
        // In 2.1.6 and subsequent
        Array<const ExprNode> args(2*N);
        args.set_ref(0,vars[0]);
        args.set_ref(N,ExprConstant::new_scalar(i));
        // =========================================

        Function* f = new Function(vars,P(args));
        NumConstraint* rel = new NumConstraint(*f,GEQ); // <=> x>=i

        ctc.push_back(new CtcFwdBwd(*rel));
    }

    for (int i=0; i<10; i++) {
        IntervalVector box(1);
        cout << "before contraction: " << box << endl;
        ctc[i]->contract(box);
        cout << "after contraction: " << box << endl;
    }

}

The display is as expected:

before contraction: ([-inf, inf])
after contraction: ([0, inf])
before contraction: ([-inf, inf])
after contraction: ([1, inf])
before contraction: ([-inf, inf])
after contraction: ([2, inf])
before contraction: ([-inf, inf])
after contraction: ([3, inf])
before contraction: ([-inf, inf])
after contraction: ([4, inf])
before contraction: ([-inf, inf])
after contraction: ([5, inf])
before contraction: ([-inf, inf])
after contraction: ([6, inf])
before contraction: ([-inf, inf])
after contraction: ([7, inf])
before contraction: ([-inf, inf])
after contraction: ([8, inf])
before contraction: ([-inf, inf])
after contraction: ([9, inf])
 

Regards,

Gilles

In my last example (and in yours I guess), it is possible to avoid creating 10 times the variables and 10 times the contractors x->P(x,i) by creating a single constraint F(x)<=0 where F is the vector-valued function x -> (P(x,0), ..., P(x,9)). The inequality "<=0" is interpreted componentwise.

This is done thanks to the ExprVector class. Here is the code:

int N=1; // could be any value

// The function P(x,y) (x will be the variables, y the data)
// with, potentially, a lot of components in x and y (N big)
Array<const ExprSymbol> xy(2*N);
xy.set_ref(0,ExprSymbol::new_("x")); // string "x" is optional
xy.set_ref(1,ExprSymbol::new_("y"));
Function P(xy,xy[0]-xy[1]);

// Create variables for x -> (P(x,0),...,P(x,9))
Array<const ExprSymbol> vars(N);
vars.set_ref(0,ExprSymbol::new_("x")); // if N>1, create a loop "for (int j=0; j<N; j++) ..."

// To store the 10 subexpressions (P(x,0),...,P(x,9))
Array<const ExprNode> f_img(10);

for (int i=0; i<10; i++) {

    // Create the arguments of P (concatenation of variables and data
    Array<const ExprNode> args(2*N);     // valid for release 2.1.6 and subsequent
    args.set_ref(0,vars[0]);
    args.set_ref(N,ExprConstant::new_scalar(i));

    f_img.set_ref(i,P(args));
}

Function* f = new Function(vars,ExprVector::new_(f_img,true));
NumConstraint* rel = new NumConstraint(*f,GEQ); // <=> x>=0

CtcFwdBwd c(*rel);

IntervalVector box(1);
cout << "before contraction: " << box << endl;
c.contract(box);
cout << "after contraction: " << box << endl;

Regards,

Gilles