1 //
2 //  model.cpp
3 //  Gravity
4 //
5 //  Created by Hijazi, Hassan.
6 //
7 //
8 //
9 #include <gravity/model.h>
10 #include <gravity/solver.h>
11 #include <math.h> //for setting the rounding direction
12 
13 using namespace std;
14 namespace gravity {
15 
16 
var_compare(const pair<string,shared_ptr<param_>> & v1,const pair<string,shared_ptr<param_>> & v2)17     const bool var_compare(const pair<string,shared_ptr<param_>>& v1, const pair<string,shared_ptr<param_>>& v2) {
18         return v1.second->get_nb_rows() > v2.second->get_nb_rows();
19     }
20 
21     /** Lift and linearize the nonlinear constraint c, return the linearized form and add linking constraints to the model.
22      @param[in] c: constraint to linearize
23      @param[in] partition_model: formulation used for partitionning the nonconvex parts of the constraint
24      @return the linearized constraint
25      @note This function will add constraints linking the lifted variables to the original ones, if a variable's partition is greater than 1, it will also add the disjunctive constraints corresponding to the partitionning of the variables.
26      **/
27     template <typename type>
28     template<class T, typename enable_if<is_same<T, Cpx>::value>::type*>
lift(Constraint<type> & c,string model_type)29     Constraint<type> Model<type>::lift(Constraint<type>& c, string model_type){
30         if(c.is_constant() || c.is_linear()){
31             return c;
32         }
33         if(c.is_nonlinear() || c.is_polynomial()){
34             throw invalid_argument("lift can only be called on quadratic constraints for now");
35         }
36         /* Lambda models are taken from Padberg's paper as they are described in type II and type III */
37         if((model_type != "on/off") && (model_type != "lambda_II") && (model_type != "lambda_III")){
38             throw invalid_argument("model_type can only be one of the following: 'on/off', 'lambda_II', 'lambda_III' ");
39         }
40         Constraint<Cpx> lifted(c._name+"_lifted");
41         if (!c.get_cst()->is_zero()) {
42             if (c.get_cst()->is_number()) {
43                 auto f_cst = static_pointer_cast<constant<Cpx>>(c.get_cst());
44                 lifted.add_cst(*f_cst);
45             }
46             else if (c.get_cst()->is_param()) {
47                 auto f_cst = static_pointer_cast<param<Cpx>>(c.get_cst());
48                 lifted.add_cst(*f_cst);
49             }
50             else {
51                 auto f_cst = static_pointer_cast<func<Cpx>>(c.get_cst());
52                 lifted.add_cst(*f_cst);
53             }
54             if (lifted._cst->is_function()) {
55                 lifted.embed(*static_pointer_cast<func<Cpx>>(lifted._cst));
56             }
57         }
58         for (auto &pair:*c._lterms) {
59             auto term = pair.second;
60             if (term._coef->is_function()) {
61                 auto coef = *static_pointer_cast<func<Cpx>>(term._coef);
62                 term._coef = func<Cpx>(coef).copy();
63             }
64             else if(term._coef->is_param()) {
65                 auto coef = *static_pointer_cast<param<Cpx>>(term._coef);
66                 term._coef = param<Cpx>(coef).copy();
67             }
68             else if(term._coef->is_number()) {
69                 auto coef = *static_pointer_cast<constant<Cpx>>(term._coef);
70                 term._coef = constant<Cpx>(coef).copy();//TODO if T2==type no need to cast
71             }
72             lifted.insert(term);
73         }
74         bool lift_sign; /* create lift_sign for correct lower/upper bounding of the variables */
75         for (auto &pair:*c._qterms) {
76             auto term = pair.second;
77             lterm lt;
78             lt._sign = term._sign;
79             if (term._coef->is_function()) {
80                 auto coef = *static_pointer_cast<func<Cpx>>(term._coef);
81                 lt._coef = func<Cpx>(coef).copy();
82             }
83             else if(term._coef->is_param()) {
84                 auto coef = *static_pointer_cast<param<Cpx>>(term._coef);
85                 lt._coef = param<Cpx>(coef).copy();
86             }
87             else if(term._coef->is_number()) {
88                 auto coef = *static_pointer_cast<constant<Cpx>>(term._coef);
89                 lt._coef = constant<Cpx>(coef).copy();
90                 lift_sign = (term._sign ^ coef.is_negative()); //TODO: update prod_sign in other cases of coef type. Don't know how to do!
91             }
92 
93             if (c.func<Cpx>::is_concave()) //reverse the sign if the constraint is concave
94             {
95                 DebugOn("Changing the sign of the lifted variable." << endl);
96                 lift_sign = !lift_sign;
97             }
98             else{
99                 DebugOn("Keeping the sign of the lifted variable same." << endl);
100             }
101 
102             //arrange the variables so that if they have the same base name, use them ordered in name
103             auto o1 = *static_pointer_cast<var<Cpx>>(term._p->first);
104             auto o2 = *static_pointer_cast<var<Cpx>>(term._p->second);
105             if((o1 != o2) && (o1.get_name(true,true) == o2.get_name(true,true)) && (o1._name > o2._name) ){
106                 o2 = *static_pointer_cast<var<Cpx>>(term._p->first);
107                 o1 = *static_pointer_cast<var<Cpx>>(term._p->second);
108                 DebugOff("O1 name "<< o1._name << endl);
109                 DebugOff("O2 name "<< o2._name << endl);
110             }
111 
112             string name;
113             indices ids;
114             if(o1==o2){
115                 name = "Lift("+o1.get_name(true,true)+"^2)";
116                 ids = *o1._indices;
117             }
118             else {
119                 name = "Lift("+o1.get_name(true,true)+"|"+o2.get_name(true,true)+")";
120                 ids = combine(*o1._indices,*o2._indices);
121             }
122             auto unique_ids = ids.get_unique_keys(); /* In case of an indexed variable, keep the unique keys only */
123             auto o1_ids = *o1._indices;
124             auto o2_ids = *o2._indices;
125             if(unique_ids.size()!=ids.size()){/* If some keys are repeated, remove them from the refs of o1 and o2 */
126                 auto keep_refs = ids.get_unique_refs();
127                 o1_ids.filter_refs(keep_refs);
128                 o2_ids.filter_refs(keep_refs);
129             }
130             param<Cpx> lb("lb"), ub("ub");
131             lb.in(unique_ids);ub.in(unique_ids);
132             auto it = _vars_name.find(name);
133             auto name1 = o1.get_name(true,true);
134             auto name2 = o2.get_name(true,true);
135             if(it==_vars_name.end()){
136                 /* create the lifted variable with proper lower and upper bounds */
137                 var<Cpx> vlift(name, lb, ub);
138                 vlift._lift = true;
139                 add(vlift.in(unique_ids));
140                 lt._p = make_shared<var<Cpx>>(vlift.in(ids));
141             }
142             else {
143                 auto vlift = static_pointer_cast<var<Cpx>>(it->second);
144                 auto added = vlift->add_bounds(lb,ub);
145                 lt._p = make_shared<var<Cpx>>(vlift->in(ids));
146                 if(!added.empty()){
147                     assert(o1._indices->size()==o2._indices->size());
148                     if(added.size()!=o1._indices->size()){/* If some keys are repeated, remove them from the refs of o1 and o2 */
149                         auto keep_refs = ids.get_diff_refs(added);
150                         o1_ids.filter_refs(keep_refs);
151                         o2_ids.filter_refs(keep_refs);
152                     }
153                     reindex_vars();
154                     // If some keys are repeated in individual indices, remove them from the refs of o1 and o2
155                     auto o1_ids_uq = o1_ids;
156                     auto o2_ids_uq = o2_ids;
157                     auto keep_refs1 = o1_ids_uq.get_unique_refs();
158                     auto keep_refs2 = o2_ids_uq.get_unique_refs();
159                     o1_ids_uq.filter_refs(keep_refs1);
160                     o2_ids_uq.filter_refs(keep_refs2);
161                     reindex_vars();
162                 }
163             }
164             lifted.insert(lt);
165         }
166         for (auto &pair:*c._pterms) {
167             auto term = pair.second;
168             lterm lt;
169             lt._sign = term._sign;
170             if (term._coef->is_function()) {
171                 auto coef = *static_pointer_cast<func<Cpx>>(term._coef);
172                 lt._coef = func<Cpx>(coef).copy();
173             }
174             else if(term._coef->is_param()) {
175                 auto coef = *static_pointer_cast<param<Cpx>>(term._coef);
176                 lt._coef = param<Cpx>(coef).copy();
177             }
178             else if(term._coef->is_number()) {
179                 auto coef = *static_pointer_cast<constant<Cpx>>(term._coef);
180                 lt._coef = constant<Cpx>(coef).copy();
181             }
182             func<Cpx> prod = 1;
183             string prod_name = "Lift(";
184             auto list = pair.second._l;
185             for (auto &ppi: *list) {
186                 auto p = ppi.first;
187                 auto orig_var = *static_pointer_cast<var<Cpx>>(p);
188                 if(ppi.second>1){
189                     prod_name += orig_var.get_name(true,true)+"("+orig_var._indices->get_name()+")^"+to_string(ppi.second);
190                     //TODO Lift univarite power function
191                 }
192                 else{
193                     prod_name += orig_var.get_name(true,true)+"("+orig_var._indices->get_name()+")";
194                 }
195                 prod *= pow(orig_var,ppi.second);
196             }
197             prod_name += ")";
198 
199             auto ids = *c._indices;
200             param<Cpx> lb("lb"), ub("ub");
201             lb.in(ids);ub.in(ids);
202             lb.set_val(prod._range->first);
203             ub.set_val(prod._range->second);
204             var<Cpx> vlift(prod_name, lb, ub);
205             auto it = _vars_name.find(prod_name);
206             if(it==_vars_name.end()){
207                 vlift._lift=true;
208                 add(vlift.in(ids));
209                 lt._p = make_shared<var<Cpx>>(vlift);
210             }
211             else {
212                 vlift = *static_pointer_cast<var<Cpx>>(it->second);
213                 lt._p = make_shared<var<Cpx>>(vlift);
214             }
215             lifted.insert(lt);
216         }
217         lifted._range = c._range;
218         lifted._all_convexity = linear_;
219         lifted._all_sign = c._all_sign;
220         lifted._ftype = lin_;
221         lifted._ctype = c._ctype;
222         lifted._indices = c._indices;
223         lifted._dim[0] = c._dim[0];
224         lifted._dim[1] = c._dim[1];
225         return lifted;
226     }
227 
228     template <typename type>
229     template<typename T,typename std::enable_if<is_arithmetic<T>::value>::type*>
lift(Constraint<type> & c,string model_type)230     Constraint<type> Model<type>::lift(Constraint<type>& c, string model_type){
231         if(c.is_constant() || c.is_linear()){
232             return c;
233         }
234         if(c.is_nonlinear() || c.is_polynomial()){
235             throw invalid_argument("lift can only be called on quadratic constraints for now");
236         }
237         /* Lambda models are taken from Padberg's paper as they are described in type II and type III */
238         if((model_type != "on/off") && (model_type != "lambda_II") && (model_type != "lambda_III")){
239             throw invalid_argument("model_type can only be one of the following: 'on/off', 'lambda_II', 'lambda_III' ");
240         }
241         Constraint<type> lifted(c._name+"_lifted");
242         if (!c.get_cst()->is_zero()) {
243             if (c.get_cst()->is_number()) {
244                 auto f_cst = static_pointer_cast<constant<type>>(c.get_cst());
245                 lifted.add_cst(*f_cst);
246             }
247             else if (c.get_cst()->is_param()) {
248                 auto f_cst = static_pointer_cast<param<type>>(c.get_cst());
249                 lifted.add_cst(*f_cst);
250             }
251             else {
252                 auto f_cst = static_pointer_cast<func<type>>(c.get_cst());
253                 lifted.add_cst(*f_cst);
254                 lifted.embed(*static_pointer_cast<func<type>>(lifted._cst));
255             }
256         }
257         for (auto &pair:*c._lterms) {
258             auto term = pair.second;
259             if (term._coef->is_function()) {
260                 auto coef = *static_pointer_cast<func<type>>(term._coef);
261                 term._coef = func<type>(coef).copy();
262             }
263             else if(term._coef->is_param()) {
264                 auto coef = *static_pointer_cast<param<type>>(term._coef);
265                 term._coef = param<type>(coef).copy();
266             }
267             else if(term._coef->is_number()) {
268                 auto coef = *static_pointer_cast<constant<type>>(term._coef);
269                 term._coef = constant<type>(coef).copy();//TODO if T2==type no need to cast
270             }
271             lifted.insert(term);
272         }
273         bool lift_sign; /* create lift_sign for correct lower/upper bounding of the variables */
274         for (auto &pair:*c._qterms) {
275             auto term = pair.second;
276             lterm lt;
277             lt._sign = term._sign;
278             if (term._coef->is_function()) {
279                 auto coef = *static_pointer_cast<func<type>>(term._coef);
280                 lt._coef = func<type>(coef).copy();
281             }
282             else if(term._coef->is_param()) {
283                 auto coef = *static_pointer_cast<param<type>>(term._coef);
284                 lt._coef = param<type>(coef).copy();
285             }
286             else if(term._coef->is_number()) {
287                 auto coef = *static_pointer_cast<constant<type>>(term._coef);
288                 lt._coef = constant<type>(coef).copy();
289                 lift_sign = (term._sign ^ coef.is_negative()); //TODO: update prod_sign in other cases of coef type. Don't know how to do!
290             }
291 
292             if (c.func<type>::is_concave()) //reverse the sign if the constraint is concave
293             {
294                 DebugOff("Changing the sign of the lifted variable." << endl);
295                 lift_sign = !lift_sign;
296             }
297             else{
298                 DebugOff("Keeping the sign of the lifted variable same." << endl);
299             }
300 
301             //arrange the variables so that if they have the same base name, use them ordered in name
302             auto o1_ptr = static_pointer_cast<var<type>>(term._p->first);
303             auto o2_ptr = static_pointer_cast<var<type>>(term._p->second);
304             auto o1 = *o1_ptr;
305             auto o2 = *o2_ptr;
306             if((o1 != o2) && (o1._indices->_keys->at(0) > o2._indices->_keys->at(0)) ){
307                 //        if((o1 != o2) && (o1.get_name(false,false) > o2.get_name(false,false)) ){
308                 o2_ptr = static_pointer_cast<var<type>>(term._p->first);
309                 o1_ptr = static_pointer_cast<var<type>>(term._p->second);
310                 o1 = *o1_ptr;
311                 o2 = *o2_ptr;
312                 DebugOff("O1 name "<< o1._name << endl);
313                 DebugOff("O2 name "<< o2._name << endl);
314             }
315 
316             string name;
317             indices ids;
318             if(o1==o2){
319                 name = "Lift("+o1.get_name(true,true)+"^2)";
320                 ids = *o1._indices;
321                 ids.set_name(o1._name);
322             }
323             else {
324                 name = "Lift("+o1.get_name(true,true)+"|"+o2.get_name(true,true)+")";
325                 ids = combine(*o1._indices,*o2._indices);
326                 ids.set_name(o1._name+"|"+o2._name);
327             }
328             auto o1_ids = *o1._indices;
329             auto o2_ids = *o2._indices;
330             auto flat_ids = ids;
331             if(ids.is_matrix_indexed()){/* Flatten matrix indexed sets */
332                 flat_ids.flatten();
333                 o1_ids.flatten();
334                 o2_ids.flatten();
335             }
336             auto unique_ids = flat_ids.get_unique_keys(); /* In case of an indexed variable, keep the unique keys only */
337             if(unique_ids.size()!=flat_ids.size()){/* If some keys are repeated, remove them from the refs of o1 and o2 */
338                 auto keep_refs = flat_ids.get_unique_refs();
339                 o1_ids.filter_refs(keep_refs);
340                 o2_ids.filter_refs(keep_refs);
341             }
342 
343             // collect the number of partitions of each variable
344             int num_partns1 = *o1._num_partns;
345             int num_partns2 = *o2._num_partns;
346 
347             func<double> lb, ub;
348 
349             //calculate the tightest valid bounds
350             if(o1==o2) //if variables are same, calculate the bounds more efficiently
351             {
352                 auto it = _vars_name.find(name);
353                 if(it!=_vars_name.end()){
354                     auto vlift = static_pointer_cast<var<type>>(it->second);
355                     vlift->_lb->merge_vars(*vlift->_ub);
356                     auto new_ids = unique_ids.get_diff_refs(*vlift->_indices);
357                     unique_ids.filter_refs(new_ids);
358                     o1_ids.filter_refs(new_ids);
359                     o2_ids.filter_refs(new_ids);
360                     auto o1_lb = vlift->get_square_lb();
361                     auto o1_ub = vlift->get_square_ub();
362                     o1_lb->_indices->add_refs(o1_ids);
363                     o1_ub->_indices->add_refs(o1_ids);
364                     vlift->_original_vars[0]->_indices->add_refs(o1_ids);
365                     vlift->_original_vars[0]->_lb->index_in(*vlift->_original_vars[0]->_indices);
366                     vlift->_original_vars[0]->_ub->index_in(*vlift->_original_vars[0]->_indices);
367                     vlift->_original_vars[1]->_indices->add_refs(o2_ids);
368                     vlift->_original_vars[1]->_lb->index_in(*vlift->_original_vars[1]->_indices);
369                     vlift->_original_vars[1]->_ub->index_in(*vlift->_original_vars[1]->_indices);
370 
371                 }
372                 func<double> prod_b1 = (o1.get_lb()*o1.get_lb()).in(unique_ids);
373                 func<double> prod_b2 = (o1.get_lb()*o1.get_ub()).in(unique_ids);
374                 func<double> prod_b3 = (o1.get_ub()*o1.get_ub()).in(unique_ids);
375 
376                 lb = gravity::max(gravity::min(gravity::min(prod_b1,prod_b2), prod_b3).in(unique_ids), func<type>());
377                 ub = gravity::max(gravity::max(prod_b1,prod_b2).in(unique_ids),prod_b3);
378 
379 
380                 //            lb = max(min(min(o1.get_lb()*o1.get_lb(),o1.get_lb()*o1.get_ub()), o1.get_ub()*o1.get_ub()), 0);
381                 //            ub = max(max(o1.get_lb()*o1.get_lb(),o1.get_lb()*o1.get_ub()), o1.get_ub()*o1.get_ub());
382                 //            for (int i=0; i<unique_ids.size(); i++) {
383                 //                //calculate all the possibilities and assign the worst case
384                 //                size_t id1;
385                 //                if(o1_ids._ids == nullptr){
386                 //                    id1 = i;
387                 //                }
388                 //                else id1 = o1_ids._ids->at(0).at(i);
389                 //                auto key1 = o1_ids._keys->at(id1);
390                 //
391                 //                auto prod_b1 = o1.get_lb(key1)*o1.get_lb(key1);
392                 //                auto prod_b2 = o1.get_lb(key1)*o1.get_ub(key1);
393                 //                auto prod_b3 = o1.get_ub(key1)*o1.get_ub(key1);
394                 //
395                 //
396                 //
397                 //                lb.set_val(key1, std::max(std::min(std::min(prod_b1,prod_b2), prod_b3), (type)0 ));
398                 //                ub.set_val(key1, std::max(std::max(prod_b1,prod_b2),prod_b3));
399                 //            }
400             }
401             else /* if variables are different */
402             {
403                 auto it = _vars_name.find(name);
404                 if(it!=_vars_name.end()){
405                     auto vlift = static_pointer_cast<var<type>>(it->second);
406                     vlift->_lb->merge_vars(*vlift->_ub);
407                     auto new_ids = unique_ids.get_diff_refs(*vlift->_indices);
408                     unique_ids.filter_refs(new_ids);
409                     o1_ids.filter_refs(new_ids);
410                     o2_ids.filter_refs(new_ids);
411                     auto o1_lb = vlift->get_bilinear_lb1();
412                     auto o1_ub = vlift->get_bilinear_ub1();
413                     o1_lb->_indices->add_refs(o1_ids);
414                     o1_ub->_indices->add_refs(o1_ids);
415                     auto o2_lb = vlift->get_bilinear_lb2();
416                     auto o2_ub = vlift->get_bilinear_ub2();
417                     o2_lb->_indices->add_refs(o2_ids);
418                     o2_ub->_indices->add_refs(o2_ids);
419                     vlift->_original_vars[0]->_indices->add_refs(o1_ids);
420                     vlift->_original_vars[1]->_indices->add_refs(o2_ids);
421                     vlift->_original_vars[0]->_lb->index_in(*vlift->_original_vars[0]->_indices);
422                     vlift->_original_vars[0]->_ub->index_in(*vlift->_original_vars[0]->_indices);
423                     vlift->_original_vars[1]->_lb->index_in(*vlift->_original_vars[1]->_indices);
424                     vlift->_original_vars[1]->_ub->index_in(*vlift->_original_vars[1]->_indices);
425                 }
426 
427                 auto lb1 = o1.get_lb().in(o1_ids);
428                 auto ub1 = o1.get_ub().in(o1_ids);
429                 auto lb2 = o2.get_lb().in(o2_ids);
430                 auto ub2 = o2.get_ub().in(o2_ids);
431                 func<double> prod_b1 = (lb1*lb2).in(unique_ids);
432                 func<double> prod_b2 = (lb1*ub2).in(unique_ids);
433                 func<double> prod_b3 = (ub1*lb2).in(unique_ids);
434                 func<double> prod_b4 = (ub1*ub2).in(unique_ids);
435 
436                 lb = gravity::min(gravity::min(prod_b1,prod_b2).in(unique_ids),gravity::min(prod_b3,prod_b4).in(unique_ids));
437                 ub = gravity::max(gravity::max(prod_b1,prod_b2).in(unique_ids),gravity::max(prod_b3,prod_b4).in(unique_ids));
438                 //            for (int i=0; i<unique_ids.size(); i++) {
439                 //                //calculate all the possibilities and assign the worst case
440                 //                size_t id1;
441                 //                size_t id2;
442                 //                if(o1_ids._ids == nullptr){
443                 //                    id1 = i;
444                 //                }
445                 //                else id1 = o1_ids._ids->at(0).at(i);
446                 //                if(o2_ids._ids == nullptr){
447                 //                    id2 = i;
448                 //                }
449                 //                else id2 = o2_ids._ids->at(0).at(i);
450                 //                auto key1 = o1_ids._keys->at(id1);
451                 //                auto key2 = o2_ids._keys->at(id2);
452                 //
453                 //                auto prod_b1 = o1.get_lb(key1)*o2.get_lb(key2);
454                 //                auto prod_b2 = o1.get_lb(key1)*o2.get_ub(key2);
455                 //                auto prod_b3 = o1.get_ub(key1)*o2.get_lb(key2);
456                 //                auto prod_b4 = o1.get_ub(key1)*o2.get_ub(key2);
457                 //
458                 //                lb.set_val(key1+","+key2, std::min(std::min(prod_b1,prod_b2),std::min(prod_b3,prod_b4)));
459                 //                ub.set_val(key1+","+key2, std::max(std::max(prod_b1,prod_b2),std::max(prod_b3,prod_b4)));
460                 //            }
461             }
462             //        lb.update_var_indices(un)
463             lb.index_in(unique_ids);
464             ub.index_in(unique_ids);
465             lb.eval_all();
466             ub.eval_all();
467             //        auto common_refs = o1._indices->get_common_refs(unique_ids);
468             //        lb.update_rows(common_refs);
469             //        ub.update_rows(common_refs);
470             auto it = _vars_name.find(name);
471 
472             auto name1 = o1.get_name(true,true);
473             auto name2 = o2.get_name(true,true);
474 
475             if(it==_vars_name.end()){
476 
477 
478 
479                 //create the lifted variable with proper lower and upper bounds
480                 var<type> vlift(name, lb, ub);
481                 vlift._lift = true;
482                 vlift._original_vars.push_back(make_shared<var<type>>(*o1_ptr));
483                 vlift._original_vars.push_back(make_shared<var<type>>(*o2_ptr));
484                 add(vlift.in(unique_ids));
485                 lt._p = make_shared<var<type>>(vlift.in(ids));
486 
487                 //check the sign of the lift and the correspoinding bounding functions
488                 if(c.check_soc() && c.is_eq()){
489                     if(lift_sign){
490                         vlift._lift_ub = true;
491                         vlift._lift_lb = false;
492                     }
493                     else{
494                         vlift._lift_ub = false;
495                         vlift._lift_lb = true;
496                     }
497                 }
498                 else{
499                     vlift._lift_ub = true;
500                     vlift._lift_lb = true;
501                 }
502 
503 
504                 if((num_partns1 > 1) || (num_partns2 > 1)) {
505 #ifdef PARTITION
506                     // If some keys are repeated in individual indices, remove them from the refs of o1 and o2
507                     auto o1_ids_uq = o1_ids;
508                     auto o2_ids_uq = o2_ids;
509                     auto keep_refs1 = o1_ids_uq.get_unique_refs();
510                     auto keep_refs2 = o2_ids_uq.get_unique_refs();
511                     o1_ids_uq.filter_refs(keep_refs1);
512                     o2_ids_uq.filter_refs(keep_refs2);
513                     reindex_vars();
514                     if (o1 == o2) //if the variables are same add 1d partition
515                     {
516 
517                         DebugOn("<<<<<<<<<< THIS IS NOT SEEN LIFT -> SINGLE <<<<<<<<<<<" << endl);
518                         //create the binary variables for the partitions
519                         var<int> on(name1+"_binary",0,1);
520 
521                         //create the proper indices and add the binary variables to the model
522                         indices partns("partns");
523                         for (int i = 0; i < num_partns1 ; ++i)
524                         {
525                             partns.add(name1+ "{" +to_string(i+1) + "}");
526                         }
527                         auto inst_partition = indices(unique_ids,partns);
528                         add(on.in(inst_partition));
529 
530                         //collect the number of entries in each of the index set
531                         auto nb_entries_v1 = o1_ids.get_nb_entries();
532                         auto nb_entries = unique_ids.get_nb_entries();
533                         auto total_entries = inst_partition.get_nb_entries();
534 
535                         //add the binary assignment constraint
536                         Constraint<> onSum(pair.first + "_binarySum");
537                         onSum += sum(on.in_matrix(nb_entries,total_entries-nb_entries));
538                         add(onSum.in(unique_ids) == 1);
539 
540                         //if the model type is selected as on/off, call on_off formulation for activation of individual constraints
541                         if(model_type == "on/off"){
542                             add_on_off_McCormick_refined(pair.first, vlift.in(unique_ids), o1.in(o1_ids), o2.in(o2_ids), on);
543                         }
544 
545                         else{ //means it is one of the lambda formulations
546 
547                             //difference is this has one more partition index
548                             indices partns_lambda("partns_lambda");
549                             for (int i = 0; i < num_partns1+1 ; ++i)
550                             {
551                                 partns_lambda.add(name1+ "{" +to_string(i+1) + "}");
552                             }
553                             auto inst_partition_lambda = indices(unique_ids,partns_lambda);
554 
555                             // Convex combination variables
556                             var<> lambda(name1+"_lambda",pos_);
557                             add(lambda.in(inst_partition_lambda));
558 
559                             /** Parameters */
560                             // Bounds on variable v1 & v2
561                             param<> bounds(name1+"_bounds");
562                             bounds.in(inst_partition_lambda);
563 
564                             // Function values on the extreme points
565                             param<> EP(name1+name2+"_grid_values");
566                             EP.in(inst_partition_lambda);
567 
568                             size_t nb_ins = vlift.in(unique_ids).get_nb_inst();
569                             auto o1_global_lb = o1.get_lb();
570                             auto increment = (o1.get_ub() - o1_global_lb)/num_partns1;
571 
572                             // fill bounds and function values
573                             for (int i=0 ; i<num_partns1+1; ++i) {
574                                 auto bound_partn = o1_global_lb + increment*i;
575                                 bound_partn.eval_all();
576                                 for (size_t inst = 0; inst< nb_ins; inst++){
577                                     auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
578                                     auto cur_var_idx = unique_ids._keys->at(cur_var_id);
579                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
580                                     bounds.set_val(cur_idx,bound_partn.eval(inst));
581                                     EP.set_val(cur_idx,(bound_partn.eval(inst)*bound_partn.eval(inst)));
582                                 }
583                             }
584 
585                             // Lambda coefficient matrix when linking with partition variables
586                             param<> lambda_coef(name1+"_lambda_linking_coefficients");
587 
588                             // Partition coefficient matrix when linking with lambda variables
589                             param<> on_coef(name1+"_partition_linking_coefficients");
590 
591                             // create constraint indices
592                             indices const_idx("const_idx");
593 
594                             if(model_type == "lambda_II"){
595 
596                                 //fill constraint indices
597                                 for (int i=0; i<num_partns1+1; ++i){
598                                     const_idx.add(to_string(i+1));
599                                 }
600 
601                                 // Lambda coefficient matrix when linking with partition variables
602                                 lambda_coef.in(indices(inst_partition_lambda, const_idx));
603 
604                                 // Partition coefficient matrix when linking with lambda variables
605                                 on_coef.in(indices(inst_partition, const_idx));
606 
607                                 // fill lambda_coef
608                                 for (size_t inst = 0; inst< nb_ins; inst++){
609                                     auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
610                                     auto cur_var_idx = unique_ids._keys->at(cur_var_id);
611                                     for (int i=0 ; i<num_partns1+1; ++i) {
612                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}," + to_string(i+1);
613                                         lambda_coef.set_val(cur_idx,1);
614                                     }
615                                 }
616 
617                                 // fill on_coef
618                                 for (size_t inst = 0; inst< nb_ins; inst++){
619                                     auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
620                                     auto cur_var_idx = unique_ids._keys->at(cur_var_id);
621                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"}," + to_string(1);
622                                     on_coef.set_val(cur_idx,1);
623                                     for (int i=1 ; i<num_partns1; ++i) {
624                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i)+"}," + to_string(i+1);
625                                         on_coef.set_val(cur_idx,1);
626                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}," + to_string(i+1);
627                                         on_coef.set_val(cur_idx,1);
628                                     }
629                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1)+"}," + to_string(num_partns1+1);
630                                     on_coef.set_val(cur_idx,1);
631                                 }
632                             }
633 
634                             else /*means model_type == "lambda_III" */{
635 
636                                 //fill constraint indices
637                                 for (int i=0; i<(num_partns1-2)*2+2; ++i){
638                                     const_idx.add(to_string(i+1));
639                                 }
640 
641                                 // Lambda coefficient matrix when linking with partition variables
642                                 lambda_coef.in(indices(inst_partition_lambda, const_idx));
643 
644                                 // Partition coefficient matrix when linking with lambda variables
645                                 on_coef.in(indices(inst_partition, const_idx));
646 
647                                 // fill lambda_coef
648                                 for (size_t inst = 0; inst< nb_ins; inst++){
649                                     auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
650                                     auto cur_var_idx = unique_ids._keys->at(cur_var_id);
651                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"}," + to_string(1);
652                                     lambda_coef.set_val(cur_idx,1);
653                                     for (int i=1 ; i<(num_partns1-2)*2+1; i=i+2) {
654                                         for (int j=(i-1)/2 + 2; j<num_partns1+1; ++j) {
655                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"}," + to_string(i+1) ;
656                                             lambda_coef.set_val(cur_idx,1);
657                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"}," + to_string(i+2);
658                                             lambda_coef.set_val(cur_idx,-1);
659                                         }
660                                     }
661                                     cur_idx =  cur_var_idx+","+name1+"{"+to_string(num_partns1+1)+"}," + to_string((num_partns1-2)*2+2);
662                                     lambda_coef.set_val(cur_idx,1);
663                                 }
664 
665                                 // fill on_coef
666                                 for (size_t inst = 0; inst< nb_ins; inst++){
667                                     auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
668                                     auto cur_var_idx = unique_ids._keys->at(cur_var_id);
669                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"}," + to_string(1);
670                                     on_coef.set_val(cur_idx,1);
671 
672                                     for (int i=1; i<num_partns1; ++i) {
673                                         cur_idx =  cur_var_idx+","+name1+"{"+to_string(i+1)+"}," + to_string(2);
674                                         on_coef.set_val(cur_idx, 1);
675                                     }
676 
677                                     for (int i=2 ; i<(num_partns1-2)*2+2; i=i+2) {
678                                         for (int j=i/2+1; j<num_partns1; ++j) {
679                                             cur_idx =  cur_var_idx +","+name1+"{"+to_string(j+1)+"}," +  to_string(i+1);
680                                             on_coef.set_val(cur_idx,-1);
681                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"}," + to_string(i+2) ;
682                                             on_coef.set_val(cur_idx,1);
683                                         }
684                                     }
685                                 }
686 
687                             }
688 
689 
690                             /** Constraints */
691                             if (vlift._lift_ub){
692                                 // Representation of the quadratic term with secant
693                                 Constraint<> quad_ub(pair.first+"_quad_ub");
694                                 quad_ub = EP.in_matrix(nb_entries,total_entries-nb_entries)*lambda.in_matrix(nb_entries,total_entries-nb_entries) - vlift.in(unique_ids);
695                                 add(quad_ub.in(unique_ids) >= 0); /*using it as the upper bound to be valid*/
696                             }
697                             if (vlift._lift_lb){
698                                 Constraint<> quad_lb(pair.first+"_quad_lb");
699                                 quad_lb = pow(o1.from_ith(0,unique_ids),2) - vlift.in(unique_ids);
700                                 quad_lb._relaxed = true;
701                                 add(quad_lb.in(unique_ids) <= 0); /*using it as the lower bound to be valid*/
702                             }
703 
704                             // Representation of o1 with convex combination
705                             Constraint<> o1_rep(pair.first+"_o1_rep");
706                             o1_rep = bounds.from_ith(0,inst_partition_lambda).in_matrix(nb_entries, 1) * lambda.in_matrix(nb_entries,total_entries-nb_entries) - o1.in(o1_ids);
707                             add(o1_rep.in(unique_ids) == 0);
708 
709                             // Linking partition variables with lambda for both lambda formulations
710                             if(model_type == "lambda_II"){
711                                 Constraint<> on_link_lambda(pair.first+"_on_link_lambda_II");
712                                 on_link_lambda = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef.in_matrix(nb_entries,total_entries-nb_entries) - on.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,on_coef.get_matrix_ids(nb_entries,total_entries-nb_entries)) * on_coef.in_matrix(nb_entries,total_entries-nb_entries);
713                                 add(on_link_lambda.in(indices(unique_ids,const_idx)) <= 0);
714 
715                             }
716                             else{ //means model_type == "lambda_III"
717                                 Constraint<> on_link_lambda(pair.first+"_on_link_lambda_III");
718                                 on_link_lambda = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef.in_matrix(nb_entries,total_entries-nb_entries) -
719                                 on.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,on_coef.get_matrix_ids(nb_entries,total_entries-nb_entries)) * on_coef.in_matrix(nb_entries,total_entries-nb_entries);
720                                 add(on_link_lambda.in(indices(unique_ids,const_idx)) <= 0);
721 
722                             }
723 
724 
725                             // sum over lambda
726                             Constraint<> lambdaSum(pair.first+"_lambdaSum");
727                             lambdaSum = sum(lambda.in_matrix(nb_entries,total_entries-nb_entries));
728                             add(lambdaSum.in(unique_ids) == 1);
729                         }
730 
731                     }
732                     else{ //else add 2d partition
733 
734                         auto binvar_ptr1 = _vars_name.find(name1+"_binary");
735                         auto binvar_ptr2 = _vars_name.find(name2+"_binary");
736 
737                         if(binvar_ptr1 !=_vars_name.end() && binvar_ptr2 !=_vars_name.end()){ //means v1 has been partitioned before
738 
739                             //if the variables are same core name (means they are same symbolic variable with different indexing)
740                             if(name1 == name2){
741                                 DebugOn("<<<<<<<<<< THIS IS NOT SEEN LIFT -> DOUBLE -> SEEN BOTH BINARIES -> SAME CORE VARS <<<<<<<<<<<" << endl);
742 
743                                 //create the proper index set for partitions
744                                 indices partns1("partns1");
745                                 for (int i = 0; i < num_partns1 ; ++i)
746                                 {
747                                     partns1.add(name1+ "{" +to_string(i+1) + "}");
748                                 }
749 
750                                 indices partns2("partns2");
751                                 for (int i = 0; i < num_partns2 ; ++i)
752                                 {
753                                     partns2.add(name2+ "{" +to_string(i+1) + "}");
754                                 }
755 
756                                 //cast the variable pointer for further use
757                                 auto binvar1 = static_pointer_cast<var<int>>(binvar_ptr1->second);
758 
759                                 //define the lower and upper bounds
760                                 param<int> lb1("lb1"), ub1("ub1");
761                                 lb1.in(union_ids(o1_ids_uq, o2_ids_uq),partns1);
762                                 ub1.in(union_ids(o1_ids_uq, o2_ids_uq),partns1);
763                                 lb1.set_val(0), ub1.set_val(1);
764                                 auto added1 = binvar1->add_bounds(lb1,ub1);
765                                 reindex_vars();
766 
767                                 //collect the number of entries in each index set
768                                 auto nb_entries_v1 = o1_ids.get_nb_entries();
769                                 auto nb_entries_v2 = o2_ids.get_nb_entries();
770                                 auto nb_entries = unique_ids.get_nb_entries();
771 
772                                 //if there are new indices for the previously defined variable add the corresponding constraint for partitions
773                                 if(!added1.empty()){
774                                     Constraint<> onSum1(o1._name+"_binarySum");
775                                     onSum1 = sum(binvar1->in(added1).in_matrix(nb_entries_v1,1));
776                                     auto vset1 = added1.from_ith(0,nb_entries_v1);
777                                     vset1.filter_refs(vset1.get_unique_refs());
778                                     add(onSum1.in(vset1) == 1);
779                                 }
780 
781                                 //if the on/off formulation is chosen for activating constraint
782                                 if(model_type == "on/off"){
783                                     var<int> on(name1+name2+"_binary",0,1);
784 
785                                     indices partns("partns");
786                                     partns = indices(partns1,partns2);
787                                     auto inst_partition = indices(unique_ids,partns);
788                                     add(on.in(inst_partition));
789                                     auto total_entries = inst_partition.get_nb_entries();
790 
791                                     Constraint<> onLink1(pair.first+"_binaryLink1");
792                                     onLink1 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) - on;
793                                     add(onLink1.in(inst_partition) >= 0);
794 
795                                     Constraint<> onLink2(pair.first+"_binaryLink2");
796                                     onLink2 = binvar1->in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - on;
797                                     add(onLink2.in(inst_partition) >= 0);
798 
799                                     Constraint<> onLink3(pair.first+"_binaryLink3");
800                                     onLink3 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) + binvar1->in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - on;
801                                     add(onLink3.in(inst_partition) <= 0);
802 
803                                     Constraint<> onSumComb(pair.first+"_binarySum");
804                                     onSumComb = sum(on.in_matrix(nb_entries,total_entries-nb_entries));
805                                     add(onSumComb.in(unique_ids) == 1);
806 
807                                     add_on_off_McCormick_refined(pair.first, vlift.in(unique_ids), o1.in(o1_ids), o2.in(o2_ids), on);
808                                 }
809 
810                                 else{ //means it is one of the lambda formulations
811 
812                                     //difference is this has one more partition index
813                                     indices partns1_lambda("partns1_lambda");
814                                     for (int i = 0; i < num_partns1+1; ++i)
815                                     {
816                                         partns1_lambda.add(name1+ "{" +to_string(i+1) + "}");
817                                     }
818 
819                                     indices partns2_lambda("partns2_lambda");
820                                     for (int i = 0; i < num_partns2+1; ++i)
821                                     {
822                                         partns2_lambda.add(name2+ "{" +to_string(i+1) + "}");
823                                     }
824 
825                                     indices partns_lambda("partns_lambda");
826                                     partns_lambda = indices(partns1_lambda,partns2_lambda);
827                                     auto inst_partition_lambda = indices(unique_ids,partns_lambda);
828                                     auto inst_partition_bounds1 = indices(unique_ids,partns1_lambda);
829                                     auto inst_partition_bounds2 = indices(unique_ids,partns2_lambda);
830 
831                                     // Convex combination variables
832                                     var<> lambda(name1+name2+"_lambda",pos_);
833                                     add(lambda.in(inst_partition_lambda));
834 
835                                     /** Parameters */
836                                     // Bounds on variable v1 & v2
837                                     param<> bounds1(name1+"_bounds1");
838                                     bounds1.in(inst_partition_bounds1);
839 
840                                     param<> bounds2(name2+"_bounds2");
841                                     bounds2.in(inst_partition_bounds2);
842 
843                                     // Function values on the extreme points
844                                     param<> EP(name1+name2+"_grid_values");
845                                     EP.in(inst_partition_lambda);
846                                     auto total_entries = inst_partition_lambda.get_nb_entries();
847 
848                                     size_t nb_ins = vlift.in(unique_ids).get_nb_inst();
849                                     auto o1_global_lb = o1.get_lb();
850                                     auto increment1 = (o1.get_ub() - o1_global_lb)/num_partns1;
851 
852                                     auto o2_global_lb = o2.get_lb();
853                                     auto increment2 = (o2.get_ub() - o2_global_lb)/num_partns2;
854 
855                                     // fill bounds and function values
856                                     for (int i=0 ; i<num_partns1+1; ++i) {
857                                         auto bound_partn1 = o1_global_lb + increment1*i;
858                                         bound_partn1.eval_all();
859                                         auto bound_partn2 = o2_global_lb + increment2*i;
860                                         bound_partn2.eval_all();
861                                         for (size_t inst = 0; inst< nb_ins; inst++){
862                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
863                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
864                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
865                                             bounds1.set_val(cur_idx,bound_partn1.eval(inst));
866                                             bounds2.set_val(cur_idx,bound_partn2.eval(inst));
867                                             for(int j=0; j<num_partns2+1; ++j){
868                                                 auto bound_partn2_temp = o2_global_lb + increment2*j;
869                                                 bound_partn2_temp.eval_all();
870                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"}";
871                                                 EP.set_val(cur_idx,(bound_partn1.eval(inst)*bound_partn2_temp.eval(inst)));
872                                             }
873                                         }
874                                     }
875 
876                                     // Lambda coefficient matrix when linking with partition variables
877                                     param<> lambda_coef1(name1+"_lambda_linking_coefficients1");
878                                     param<> lambda_coef2(name2+"_lambda_linking_coefficients2");
879 
880                                     // Partition coefficient matrix when linking with lambda variables
881                                     param<> on_coef1(name1+"_partition_linking_coefficients1");
882                                     param<> on_coef2(name2+"_partition_linking_coefficients2");
883 
884                                     // create constraint indices
885                                     indices const_idx1("const_idx1");
886                                     indices const_idx2("const_idx2");
887 
888                                     if(model_type == "lambda_II"){
889 
890                                         //fill constraint indices
891                                         for (int i=0; i<num_partns1+1; ++i){
892                                             const_idx1.add(to_string(i+1));
893                                         }
894 
895                                         //fill constraint indices
896                                         for (int i=0; i<num_partns2+1; ++i){
897                                             const_idx2.add(to_string(i+1));
898                                         }
899 
900                                         // Lambda coefficient matrix when linking with partition variables
901                                         lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
902                                         lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
903 
904                                         // Lambda coefficient matrix when linking with partition variables
905                                         on_coef1.in(indices(unique_ids, partns1, const_idx1));
906                                         on_coef2.in(indices(unique_ids, partns2, const_idx2));
907 
908                                         // fill lambda_coef1 and lambda_coef2
909                                         for (size_t inst = 0; inst< nb_ins; inst++){
910                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
911                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
912                                             for (int i=0 ; i<num_partns1+1; ++i) {
913                                                 for (int j=0 ; j<num_partns2+1; ++j) {
914                                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
915                                                     lambda_coef1.set_val(cur_idx,1);
916                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(j+1);
917                                                     lambda_coef2.set_val(cur_idx,1);
918                                                 }
919                                             }
920                                         }
921 
922                                         // fill on_coef1 and on_coef2
923                                         for (size_t inst = 0; inst< nb_ins; inst++){
924                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
925                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
926                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
927                                             on_coef1.set_val(cur_idx,1);
928                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
929                                             on_coef2.set_val(cur_idx,1);
930                                             for (int i=1 ; i<num_partns1; ++i) {
931                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i)+"},"+to_string(i+1);
932                                                 on_coef1.set_val(cur_idx,1);
933                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
934                                                 on_coef1.set_val(cur_idx,1);
935                                             }
936                                             for (int i=1 ; i<num_partns2; ++i) {
937                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(i)+"},"+to_string(i+1);
938                                                 on_coef2.set_val(cur_idx,1);
939                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(i+1);
940                                                 on_coef2.set_val(cur_idx,1);
941                                             }
942                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1)+"},"+to_string(num_partns1+1);
943                                             on_coef1.set_val(cur_idx,1);
944                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(num_partns2)+"},"+to_string(num_partns2+1);
945                                             on_coef2.set_val(cur_idx,1);
946                                         }
947                                     }
948 
949 
950                                     else /*means model_type == "lambda_III" */{
951 
952                                         //fill constraint indices
953                                         for (int i=0; i<(num_partns1-2)*2+2; ++i){
954                                             const_idx1.add(to_string(i+1));
955                                         }
956 
957                                         //fill constraint indices
958                                         for (int i=0; i<(num_partns2-2)*2+2; ++i){
959                                             const_idx2.add(to_string(i+1));
960                                         }
961 
962                                         // Lambda coefficient matrix when linking with partition variables
963                                         lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
964                                         lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
965 
966                                         // Partition coefficient matrix when linking with lambda variables
967                                         on_coef1.in(indices(unique_ids, partns1, const_idx1));
968                                         on_coef2.in(indices(unique_ids, partns2, const_idx2));
969 
970                                         // fill lambda_coef1 and lambda_coef2
971                                         for (size_t inst = 0; inst< nb_ins; inst++){
972                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
973                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
974                                             for (int j=0; j<num_partns2+1; ++j) {
975                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(1);
976                                                 lambda_coef1.set_val(cur_idx,1);
977                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string((num_partns1-2)*2+2);
978                                                 lambda_coef1.set_val(cur_idx,1);
979                                             }
980 
981                                             for (int i=1 ; i<(num_partns1-2)*2+1; i=i+2) {
982                                                 for (int j=(i-1)/2 + 2; j<num_partns1+1; ++j) {
983                                                     for(int k=0; k<num_partns2+1; ++k){
984                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+1);
985                                                         lambda_coef1.set_val(cur_idx,1);
986                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+2);
987                                                         lambda_coef1.set_val(cur_idx,-1);
988                                                     }
989                                                 }
990                                             }
991 
992                                             for (int i=0; i<num_partns1+1; ++i) {
993                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(1)+"},"+to_string(1);
994                                                 lambda_coef2.set_val(cur_idx,1);
995                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(num_partns2+1)+"},"+to_string((num_partns2-2)*2+2);
996                                                 lambda_coef2.set_val(cur_idx,1);
997                                             }
998 
999                                             for (int i=1 ; i<(num_partns2-2)*2+1; i=i+2) {
1000                                                 for (int j=(i-1)/2 + 2; j<num_partns2+1; ++j) {
1001                                                     for(int k=0; k<num_partns1+1; ++k){
1002                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
1003                                                         lambda_coef2.set_val(cur_idx,1);
1004                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
1005                                                         lambda_coef2.set_val(cur_idx,-1);
1006                                                     }
1007                                                 }
1008                                             }
1009                                         }
1010 
1011 
1012 
1013                                         // fill on_coef1 and on_coef2
1014                                         for (size_t inst = 0; inst< nb_ins; inst++){
1015                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
1016                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
1017                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
1018                                             on_coef1.set_val(cur_idx,1);
1019 
1020                                             for (int i=1; i<num_partns1; ++i) {
1021                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(2);
1022                                                 on_coef1.set_val(cur_idx, 1);
1023                                             }
1024 
1025                                             for (int i=2 ; i<(num_partns1-2)*2+2; i=i+2) {
1026                                                 for (int j=i/2+1; j<num_partns1; ++j) {
1027                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
1028                                                     on_coef1.set_val(cur_idx,-1);
1029                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
1030                                                     on_coef1.set_val(cur_idx,1);
1031                                                 }
1032                                             }
1033 
1034                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
1035                                             on_coef2.set_val(cur_idx,1);
1036                                             for (int i=1; i<num_partns2; ++i) {
1037                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(2);
1038                                                 on_coef2.set_val(cur_idx, 1);
1039                                             }
1040 
1041                                             for (int i=2 ; i<(num_partns2-2)*2+2; i=i+2) {
1042                                                 for (int j=i/2+1; j<num_partns2; ++j) {
1043                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
1044                                                     on_coef2.set_val(cur_idx,-1);
1045                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
1046                                                     on_coef2.set_val(cur_idx,1);
1047                                                 }
1048                                             }
1049                                         }
1050                                     }
1051 
1052 
1053                                     /** Constraints */
1054                                     // Representation of the bilinear term with convex combination
1055                                     Constraint<> bln_rep(pair.first+"_bln_rep");
1056                                     bln_rep = EP.in_matrix(nb_entries,total_entries-nb_entries)*lambda.in_matrix(nb_entries,total_entries-nb_entries) - vlift.in(unique_ids);
1057                                     add(bln_rep.in(unique_ids) == 0);
1058 
1059                                     // Representation of o1 with convex combination
1060                                     Constraint<> o1_rep(pair.first+"_o1_rep");
1061 
1062                                     o1_rep = bounds1.from_ith(0,inst_partition_lambda).in_matrix(nb_entries, 1) * lambda.in_matrix(nb_entries,total_entries-nb_entries) - o1.in(o1_ids);
1063 
1064                                     add(o1_rep.in(unique_ids) == 0);
1065 
1066                                     // Representation of o2 with convex combination
1067                                     Constraint<> o2_rep(pair.first+"_o2_rep");
1068 
1069                                     o2_rep = bounds2.in_ignore_ith(nb_entries, 1, inst_partition_lambda).in_matrix(nb_entries,1) * lambda.in_matrix(nb_entries,total_entries-nb_entries) - o2.in(o2_ids);
1070                                     add(o2_rep.in(unique_ids) == 0);
1071 
1072                                     // Linking partition variables1 with lambda
1073                                     if(model_type == "lambda_II"){
1074                                         Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_II");
1075 
1076                                         on_link_lambda1 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
1077                                         add(on_link_lambda1.in(indices(unique_ids,const_idx1)) <= 0);
1078 
1079                                         Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_II");
1080                                         on_link_lambda2 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
1081                                         add(on_link_lambda2.in(indices(unique_ids,const_idx2)) <= 0);
1082 
1083                                     }
1084                                     else{
1085                                         Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_III");
1086 
1087                                         on_link_lambda1 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
1088                                         add(on_link_lambda1.in(indices(unique_ids,const_idx1)) <= 0);
1089 
1090                                         Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_III");
1091                                         on_link_lambda2 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
1092                                         add(on_link_lambda2.in(indices(unique_ids,const_idx2)) <= 0);
1093                                     }
1094                                     // sum over lambda
1095                                     Constraint<> lambdaSum(pair.first+"_lambdaSum");
1096                                     lambdaSum = sum(lambda.in_matrix(nb_entries,total_entries-nb_entries));
1097                                     add(lambdaSum.in(unique_ids) == 1);
1098                                 }
1099                             }
1100 
1101                             else{
1102                                 DebugOn("<<<<<<<<<< THIS IS NOT SEEN LIFT -> DOUBLE -> SEEN BOTH BINARIES -> DIFF CORE VARS <<<<<<<<<<<" << endl);
1103 
1104                                 indices partns1("partns1");
1105                                 for (int i = 0; i < num_partns1 ; ++i)
1106                                 {
1107                                     partns1.add(name1+ "{" +to_string(i+1) + "}");
1108                                 }
1109                                 indices partns2("partns2");
1110                                 for (int i = 0; i < num_partns2 ; ++i)
1111                                 {
1112                                     partns2.add(name2+ "{" +to_string(i+1) + "}");
1113                                 }
1114 
1115                                 auto binvar1 = static_pointer_cast<var<int>>(binvar_ptr1->second);
1116                                 param<int> lb1("lb1"), ub1("ub1");
1117                                 lb1.in(o1_ids_uq,partns1);
1118                                 ub1.in(o1_ids_uq,partns1);
1119                                 lb1.set_val(0), ub1.set_val(1);
1120                                 auto added1 = binvar1->add_bounds(lb1,ub1);
1121                                 reindex_vars();
1122 
1123                                 auto binvar2 = static_pointer_cast<var<int>>(binvar_ptr2->second);
1124                                 param<int> lb2("lb2"), ub2("ub2");
1125                                 lb2.in(o2_ids_uq,partns2);
1126                                 ub2.in(o2_ids_uq,partns2);
1127                                 lb2.set_val(0), ub2.set_val(1);
1128                                 auto added2 = binvar2->add_bounds(lb2,ub2);
1129                                 reindex_vars();
1130 
1131                                 auto nb_entries_v1 = o1_ids.get_nb_entries();
1132                                 auto nb_entries_v2 = o2_ids.get_nb_entries();
1133                                 auto nb_entries = unique_ids.get_nb_entries();
1134 
1135                                 if(!added1.empty()){
1136                                     Constraint<> onSum1(o1._name+"_binarySum");
1137                                     onSum1 = sum(binvar1->in(added1).in_matrix(nb_entries_v1,1));
1138                                     auto vset1 = added1.from_ith(0,nb_entries_v1);
1139                                     vset1.filter_refs(vset1.get_unique_refs());
1140                                     add(onSum1.in(vset1) == 1);
1141                                 }
1142 
1143                                 if(!added2.empty()){
1144                                     Constraint<> onSum2(o2._name+"_binarySum");
1145                                     onSum2 = sum(binvar2->in(added2).in_matrix(nb_entries_v2,1));
1146                                     auto vset2 = added2.from_ith(0,nb_entries_v2);
1147                                     vset2.filter_refs(vset2.get_unique_refs());
1148                                     add(onSum2.in(vset2) == 1);
1149                                 }
1150 
1151                                 if(model_type == "on/off"){ //if on/off is chosen
1152 
1153                                     var<int> on(name1+name2+"_binary",0,1);
1154 
1155                                     indices partns("partns");
1156                                     partns = indices(partns1,partns2);
1157                                     auto inst_partition = indices(unique_ids,partns);
1158                                     add(on.in(inst_partition));
1159                                     auto total_entries = inst_partition.get_nb_entries();
1160 
1161                                     Constraint<> onLink1(pair.first+"_binaryLink1");
1162                                     onLink1 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) - on;
1163                                     add(onLink1.in(inst_partition) >= 0);
1164 
1165                                     Constraint<> onLink2(pair.first+"_binaryLink2");
1166                                     onLink2 = binvar2->in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - on;
1167                                     add(onLink2.in(inst_partition) >= 0);
1168 
1169                                     Constraint<> onLink3(pair.first+"_binaryLink3");
1170                                     onLink3 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) + binvar2->in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - on;
1171                                     add(onLink3.in(inst_partition) <= 0);
1172 
1173                                     Constraint<> onSumComb(pair.first+"_binarySum");
1174                                     onSumComb = sum(on.in_matrix(nb_entries,total_entries-nb_entries));
1175                                     add(onSumComb.in(unique_ids) == 1);
1176 
1177                                     add_on_off_McCormick_refined(pair.first, vlift.in(unique_ids), o1.in(o1_ids), o2.in(o2_ids), on);
1178                                 }
1179 
1180                                 else{ //means it is one of the lambda formulations
1181 
1182                                     //difference is this has one more partition index
1183                                     indices partns1_lambda("partns1_lambda");
1184                                     for (int i = 0; i < num_partns1+1; ++i)
1185                                     {
1186                                         partns1_lambda.add(name1+ "{" +to_string(i+1) + "}");
1187                                     }
1188 
1189                                     indices partns2_lambda("partns2_lambda");
1190                                     for (int i = 0; i < num_partns2+1; ++i)
1191                                     {
1192                                         partns2_lambda.add(name2+ "{" +to_string(i+1) + "}");
1193                                     }
1194 
1195                                     indices partns_lambda("partns_lambda");
1196                                     partns_lambda = indices(partns1_lambda,partns2_lambda);
1197                                     auto inst_partition_lambda = indices(unique_ids,partns_lambda);
1198                                     auto inst_partition_bounds1 = indices(unique_ids,partns1_lambda);
1199                                     auto inst_partition_bounds2 = indices(unique_ids,partns2_lambda);
1200 
1201                                     // Convex combination variables
1202                                     var<> lambda(name1+name2+"_lambda",pos_);
1203                                     add(lambda.in(inst_partition_lambda));
1204 
1205                                     /** Parameters */
1206                                     // Bounds on variable v1 & v2
1207                                     param<> bounds1(name1+"_bounds1");
1208                                     bounds1.in(inst_partition_bounds1);
1209 
1210                                     param<> bounds2(name2+"_bounds2");
1211                                     bounds2.in(inst_partition_bounds2);
1212 
1213                                     // Function values on the extreme points
1214                                     param<> EP(name1+name2+"_grid_values");
1215                                     EP.in(inst_partition_lambda);
1216                                     auto total_entries = inst_partition_lambda.get_nb_entries();
1217 
1218                                     size_t nb_ins = vlift.in(unique_ids).get_nb_inst();
1219                                     auto o1_global_lb = o1.get_lb();
1220                                     auto increment1 = (o1.get_ub() - o1_global_lb)/num_partns1;
1221 
1222                                     auto o2_global_lb = o2.get_lb();
1223                                     auto increment2 = (o2.get_ub() - o2_global_lb)/num_partns2;
1224 
1225                                     // fill bounds1 and function values
1226                                     for (int i=0 ; i<num_partns1+1; ++i) {
1227                                         auto bound_partn1 = o1_global_lb + increment1*i;
1228                                         bound_partn1.eval_all();
1229                                         for (size_t inst = 0; inst< nb_ins; inst++){
1230                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
1231                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
1232                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
1233                                             bounds1.set_val(cur_idx,bound_partn1.eval(inst));
1234                                             for(int j=0; j<num_partns2+1; ++j){
1235                                                 auto bound_partn2 = o2_global_lb + increment2*j;
1236                                                 bound_partn2.eval_all();
1237                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"}";
1238                                                 EP.set_val(cur_idx,(bound_partn1.eval(inst)*bound_partn2.eval(inst)));
1239                                             }
1240                                         }
1241                                     }
1242                                     // fill bounds2
1243                                     for (int i=0 ; i<num_partns2+1; ++i) {
1244                                         auto bound_partn2 = o2_global_lb + increment2*i;
1245                                         bound_partn2.eval_all();
1246                                         for (size_t inst = 0; inst< nb_ins; inst++){
1247                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
1248                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
1249                                             string cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"}";
1250                                             bounds2.set_val(cur_idx,bound_partn2.eval(inst));
1251                                         }
1252                                     }
1253 
1254                                     // Lambda coefficient matrix when linking with partition variables
1255                                     param<> lambda_coef1(name1+"_lambda_linking_coefficients1");
1256                                     param<> lambda_coef2(name2+"_lambda_linking_coefficients2");
1257 
1258                                     // Partition coefficient matrix when linking with lambda variables
1259                                     param<> on_coef1(name1+"_partition_linking_coefficients1");
1260                                     param<> on_coef2(name2+"_partition_linking_coefficients2");
1261 
1262                                     // create constraint indices
1263                                     indices const_idx1("const_idx1");
1264                                     indices const_idx2("const_idx2");
1265 
1266                                     if(model_type == "lambda_II"){
1267 
1268                                         //fill constraint indices
1269                                         for (int i=0; i<num_partns1+1; ++i){
1270                                             const_idx1.add(to_string(i+1));
1271                                         }
1272 
1273                                         //fill constraint indices
1274                                         for (int i=0; i<num_partns2+1; ++i){
1275                                             const_idx2.add(to_string(i+1));
1276                                         }
1277 
1278                                         // Lambda coefficient matrix when linking with partition variables
1279                                         if(num_partns1 > 1) lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
1280                                         if(num_partns2 > 1) lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
1281 
1282                                         // Lambda coefficient matrix when linking with partition variables
1283                                         if(num_partns1 > 1) on_coef1.in(indices(unique_ids, partns1, const_idx1));
1284                                         if(num_partns2 > 1) on_coef2.in(indices(unique_ids, partns2, const_idx2));
1285 
1286                                         // fill lambda_coef1 and lambda_coef2
1287                                         for (size_t inst = 0; inst< nb_ins; inst++){
1288                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
1289                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
1290                                             for (int i=0 ; i<num_partns1+1; ++i) {
1291                                                 for (int j=0 ; j<num_partns2+1; ++j) {
1292                                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
1293                                                     if(num_partns1 > 1) lambda_coef1.set_val(cur_idx,1);
1294                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(j+1);
1295                                                     if(num_partns2 > 1) lambda_coef2.set_val(cur_idx,1);
1296                                                 }
1297                                             }
1298                                         }
1299 
1300                                         // fill on_coef1 and on_coef2
1301                                         for (size_t inst = 0; inst< nb_ins; inst++){
1302                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
1303                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
1304                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
1305                                             if(num_partns1 > 1) on_coef1.set_val(cur_idx,1);
1306                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
1307                                             if(num_partns2 > 1) on_coef2.set_val(cur_idx,1);
1308                                             if(num_partns1 > 1){
1309                                                 for (int i=1 ; i<num_partns1; ++i) {
1310                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i)+"},"+to_string(i+1);
1311                                                     on_coef1.set_val(cur_idx,1);
1312                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
1313                                                     on_coef1.set_val(cur_idx,1);
1314                                                 }
1315                                             }
1316                                             if(num_partns2 > 1){
1317                                                 for (int i=1 ; i<num_partns2; ++i) {
1318                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(i)+"},"+to_string(i+1);
1319                                                     on_coef2.set_val(cur_idx,1);
1320                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(i+1);
1321                                                     on_coef2.set_val(cur_idx,1);
1322                                                 }
1323                                             }
1324                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1)+"},"+to_string(num_partns1+1);
1325                                             if(num_partns1 > 1) on_coef1.set_val(cur_idx,1);
1326                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(num_partns2)+"},"+to_string(num_partns2+1);
1327                                             if(num_partns2 > 1) on_coef2.set_val(cur_idx,1);
1328                                         }
1329                                     }
1330 
1331 
1332                                     else /*means model_type == "lambda_III" */{
1333 
1334                                         //fill constraint indices
1335                                         for (int i=0; i<(num_partns1-2)*2+2; ++i){
1336                                             const_idx1.add(to_string(i+1));
1337                                         }
1338 
1339                                         //fill constraint indices
1340                                         for (int i=0; i<(num_partns2-2)*2+2; ++i){
1341                                             const_idx2.add(to_string(i+1));
1342                                         }
1343 
1344                                         // Lambda coefficient matrix when linking with partition variables
1345                                         if(num_partns1 > 1) lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
1346                                         if(num_partns2 > 1) lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
1347 
1348                                         // Partition coefficient matrix when linking with lambda variables
1349                                         if(num_partns1 > 1) on_coef1.in(indices(unique_ids, partns1, const_idx1));
1350                                         if(num_partns2 > 1) on_coef2.in(indices(unique_ids, partns2, const_idx2));
1351 
1352                                         // fill lambda_coef1 and lambda_coef2
1353                                         for (size_t inst = 0; inst< nb_ins; inst++){
1354                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
1355                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
1356                                             if(num_partns1 > 1) {
1357                                                 for (int j=0; j<num_partns2+1; ++j) {
1358                                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(1);
1359                                                     lambda_coef1.set_val(cur_idx,1);
1360                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string((num_partns1-2)*2+2);
1361                                                     lambda_coef1.set_val(cur_idx,1);
1362                                                 }
1363 
1364                                                 for (int i=1 ; i<(num_partns1-2)*2+1; i=i+2) {
1365                                                     for (int j=(i-1)/2 + 2; j<num_partns1+1; ++j) {
1366                                                         for(int k=0; k<num_partns2+1; ++k){
1367                                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+1);
1368                                                             lambda_coef1.set_val(cur_idx,1);
1369                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+2);
1370                                                             lambda_coef1.set_val(cur_idx,-1);
1371                                                         }
1372                                                     }
1373                                                 }
1374                                             }
1375 
1376                                             if(num_partns2 > 1){
1377                                                 for (int i=0; i<num_partns1+1; ++i) {
1378                                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(1)+"},"+to_string(1);
1379                                                     lambda_coef2.set_val(cur_idx,1);
1380                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(num_partns2+1)+"},"+to_string((num_partns2-2)*2+2);
1381                                                     lambda_coef2.set_val(cur_idx,1);
1382                                                 }
1383 
1384                                                 for (int i=1 ; i<(num_partns2-2)*2+1; i=i+2) {
1385                                                     for (int j=(i-1)/2 + 2; j<num_partns2+1; ++j) {
1386                                                         for(int k=0; k<num_partns1+1; ++k){
1387                                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
1388                                                             lambda_coef2.set_val(cur_idx,1);
1389                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
1390                                                             lambda_coef2.set_val(cur_idx,-1);
1391                                                         }
1392                                                     }
1393                                                 }
1394                                             }
1395                                         }
1396 
1397 
1398 
1399                                         // fill on_coef1 and on_coef2
1400                                         for (size_t inst = 0; inst< nb_ins; inst++){
1401                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
1402                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
1403                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
1404                                             if(num_partns1 > 1) {
1405                                                 on_coef1.set_val(cur_idx,1);
1406 
1407 
1408                                                 for (int i=1; i<num_partns1; ++i) {
1409                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(2);
1410                                                     on_coef1.set_val(cur_idx, 1);
1411                                                 }
1412 
1413                                                 for (int i=2 ; i<(num_partns1-2)*2+2; i=i+2) {
1414                                                     for (int j=i/2+1; j<num_partns1; ++j) {
1415                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
1416                                                         on_coef1.set_val(cur_idx,-1);
1417                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
1418                                                         on_coef1.set_val(cur_idx,1);
1419                                                     }
1420                                                 }
1421                                             }
1422 
1423                                             if(num_partns2 > 1) {
1424                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
1425                                                 on_coef2.set_val(cur_idx,1);
1426                                                 for (int i=1; i<num_partns2; ++i) {
1427                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(2);
1428                                                     on_coef2.set_val(cur_idx, 1);
1429                                                 }
1430 
1431                                                 for (int i=2 ; i<(num_partns2-2)*2+2; i=i+2) {
1432                                                     for (int j=i/2+1; j<num_partns2; ++j) {
1433                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
1434                                                         on_coef2.set_val(cur_idx,-1);
1435                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
1436                                                         on_coef2.set_val(cur_idx,1);
1437                                                     }
1438                                                 }
1439                                             }
1440                                         }
1441                                     }
1442 
1443 
1444                                     /** Constraints */
1445                                     // Representation of the bilinear term with convex combination
1446                                     Constraint<> bln_rep(pair.first+"_bln_rep");
1447                                     bln_rep = EP.in_matrix(nb_entries,total_entries-nb_entries)*lambda.in_matrix(nb_entries,total_entries-nb_entries) - vlift.in(unique_ids);
1448                                     add(bln_rep.in(unique_ids) == 0);
1449 
1450                                     // Representation of o1 with convex combination
1451                                     Constraint<> o1_rep(pair.first+"_o1_rep");
1452                                     o1_rep = bounds1.from_ith(0,inst_partition_lambda).in_matrix(nb_entries, 1) * lambda.in_matrix(nb_entries,total_entries-nb_entries) - o1.in(o1_ids);
1453                                     add(o1_rep.in(unique_ids) == 0);
1454 
1455                                     // Representation of o2 with convex combination
1456                                     Constraint<> o2_rep(pair.first+"_o2_rep");
1457                                     o2_rep = bounds2.in_ignore_ith(nb_entries, 1, inst_partition_lambda).in_matrix(nb_entries,1) * lambda.in_matrix(nb_entries,total_entries-nb_entries) - o2.in(o2_ids);
1458                                     add(o2_rep.in(unique_ids) == 0);
1459 
1460                                     // Linking partition variables1 with lambda
1461                                     if(model_type == "lambda_II"){
1462                                         if(num_partns1 > 1) {
1463                                             Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_II");
1464                                             on_link_lambda1 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
1465                                             add(on_link_lambda1.in(indices(unique_ids,const_idx1)) <= 0);
1466                                         }
1467                                         if(num_partns2 > 1) {
1468                                             Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_II");
1469                                             on_link_lambda2 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - binvar2->in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
1470                                             add(on_link_lambda2.in(indices(unique_ids,const_idx2)) <= 0);
1471 
1472                                         }
1473                                     }
1474                                     else{
1475                                         if(num_partns1 > 1) {
1476                                             Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_III");
1477                                             on_link_lambda1 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
1478                                             add(on_link_lambda1.in(indices(unique_ids,const_idx1)) <= 0);
1479 
1480 
1481                                         }
1482                                         if(num_partns2 > 1) {
1483                                             Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_III");
1484                                             on_link_lambda2 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - binvar2->in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
1485                                             add(on_link_lambda2.in(indices(unique_ids,const_idx2)) <= 0);
1486                                         }
1487                                     }
1488                                     // sum over lambda
1489                                     Constraint<> lambdaSum(pair.first+"_lambdaSum");
1490                                     lambdaSum = sum(lambda.in_matrix(nb_entries,total_entries-nb_entries));
1491                                     add(lambdaSum.in(unique_ids) == 1);
1492                                 }
1493                             }
1494                         }
1495                         else if(binvar_ptr1 !=_vars_name.end() && binvar_ptr2 ==_vars_name.end()){ //if first variable v1 has been partitioned before
1496                             DebugOn("<<<<<<<<<< THIS IS NOT SEEN LIFT -> DOUBLE -> SEEN FIRST BINARY -> DIFF CORE VARS <<<<<<<<<<<" << endl);
1497 
1498                             indices partns1("partns1");
1499                             for (int i = 0; i < num_partns1 ; ++i)
1500                             {
1501                                 partns1.add(name1+ "{" +to_string(i+1) + "}");
1502                             }
1503 
1504                             var<int> on2(name2+"_binary",0,1);
1505                             indices partns2("partns2");
1506                             for (int i = 0; i < num_partns2 ; ++i)
1507                             {
1508                                 partns2.add(name2+ "{" + to_string(i+1) + "}");
1509                             }
1510                             add(on2.in(o2_ids_uq,partns2));
1511 
1512                             auto binvar1 = static_pointer_cast<var<int>>(binvar_ptr2->second);
1513                             param<int> lb1("lb1"), ub1("ub1");
1514                             lb1.in(o1_ids_uq,partns1);
1515                             ub1.in(o1_ids_uq,partns1);
1516                             lb1.set_val(0), ub1.set_val(1);
1517                             auto added1 = binvar1->add_bounds(lb1,ub1);
1518                             reindex_vars();
1519 
1520                             auto nb_entries_v1 = o1_ids.get_nb_entries();
1521                             auto nb_entries_v2 = o2_ids.get_nb_entries();
1522                             auto nb_entries = unique_ids.get_nb_entries();
1523 
1524                             if(!added1.empty()){
1525                                 Constraint<> onSum1(o1._name+"_binarySum");
1526                                 onSum1 = sum(binvar1->in(added1).in_matrix(nb_entries_v1,1));
1527                                 auto vset1 = added1.from_ith(0,nb_entries_v1);
1528                                 vset1.filter_refs(vset1.get_unique_refs());
1529                                 add(onSum1.in(vset1) == 1);
1530                             }
1531 
1532                             Constraint<> onSum2(o2._name+"_binarySum");
1533                             onSum2 = sum(on2.in_matrix(nb_entries_v2,1));
1534                             add(onSum2.in(o2_ids_uq) == 1);
1535 
1536                             if(model_type == "on/off"){//if on/off is chosen
1537                                 var<int> on(name1+name2+"_binary",0,1);
1538 
1539                                 indices partns("partns");
1540                                 partns = indices(partns1,partns2);
1541                                 auto inst_partition = indices(unique_ids,partns);
1542                                 add(on.in(inst_partition));
1543                                 auto total_entries = inst_partition.get_nb_entries();
1544 
1545                                 Constraint<> onLink1(pair.first+"_binaryLink1");
1546                                 onLink1 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) - on;
1547                                 add(onLink1.in(inst_partition) >= 0);
1548 
1549                                 Constraint<> onLink2(pair.first+"_binaryLink2");
1550                                 onLink2 = on2.in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - on;
1551                                 add(onLink2.in(inst_partition) >= 0);
1552 
1553                                 Constraint<> onLink3(pair.first+"_binaryLink3");
1554                                 onLink3 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) + on2.in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - on;
1555                                 add(onLink3.in(inst_partition) <= 0);
1556 
1557                                 Constraint<> onSumComb(pair.first+"_binarySum");
1558                                 onSumComb = sum(on.in_matrix(nb_entries,total_entries-nb_entries));
1559                                 add(onSumComb.in(unique_ids) == 1);
1560 
1561                                 add_on_off_McCormick_refined(pair.first, vlift.in(unique_ids), o1.in(o1_ids), o2.in(o2_ids), on);
1562                             }
1563 
1564 
1565                             else{ //means it is one of the lambda formulations
1566 
1567                                 //difference is this has one more partition index
1568                                 indices partns1_lambda("partns1_lambda");
1569                                 for (int i = 0; i < num_partns1+1; ++i)
1570                                 {
1571                                     partns1_lambda.add(name1+ "{" +to_string(i+1) + "}");
1572                                 }
1573 
1574                                 indices partns2_lambda("partns2_lambda");
1575                                 for (int i = 0; i < num_partns2+1; ++i)
1576                                 {
1577                                     partns2_lambda.add(name2+ "{" +to_string(i+1) + "}");
1578                                 }
1579 
1580                                 indices partns_lambda("partns_lambda");
1581                                 partns_lambda = indices(partns1_lambda,partns2_lambda);
1582                                 auto inst_partition_lambda = indices(unique_ids,partns_lambda);
1583                                 auto inst_partition_bounds1 = indices(unique_ids,partns1_lambda);
1584                                 auto inst_partition_bounds2 = indices(unique_ids,partns2_lambda);
1585 
1586                                 // Convex combination variables
1587                                 var<> lambda(name1+name2+"_lambda",pos_);
1588                                 add(lambda.in(inst_partition_lambda));
1589 
1590                                 /** Parameters */
1591                                 // Bounds on variable v1 & v2
1592                                 param<> bounds1(name1+"_bounds1");
1593                                 bounds1.in(inst_partition_bounds1);
1594 
1595                                 param<> bounds2(name2+"_bounds2");
1596                                 bounds2.in(inst_partition_bounds2);
1597 
1598                                 // Function values on the extreme points
1599                                 param<> EP(name1+name2+"_grid_values");
1600                                 EP.in(inst_partition_lambda);
1601                                 auto total_entries = inst_partition_lambda.get_nb_entries();
1602 
1603                                 size_t nb_ins = vlift.in(unique_ids).get_nb_inst();
1604                                 auto o1_global_lb = o1.get_lb();
1605                                 auto increment1 = (o1.get_ub() - o1_global_lb)/num_partns1;
1606 
1607                                 auto o2_global_lb = o2.get_lb();
1608                                 auto increment2 = (o2.get_ub() - o2_global_lb)/num_partns2;
1609 
1610                                 // fill bounds1 and function values
1611                                 for (int i=0 ; i<num_partns1+1; ++i) {
1612                                     auto bound_partn1 = o1_global_lb + increment1*i;
1613                                     bound_partn1.eval_all();
1614                                     for (size_t inst = 0; inst< nb_ins; inst++){
1615                                         auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
1616                                         auto cur_var_idx = unique_ids._keys->at(cur_var_id);
1617                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
1618                                         bounds1.set_val(cur_idx,bound_partn1.eval(inst));
1619                                         for(int j=0; j<num_partns2+1; ++j){
1620                                             auto bound_partn2 = o2_global_lb + increment2*j;
1621                                             bound_partn2.eval_all();
1622                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"}";
1623                                             EP.set_val(cur_idx,(bound_partn1.eval(inst)*bound_partn2.eval(inst)));
1624                                         }
1625                                     }
1626                                 }
1627                                 // fill bounds2
1628                                 for (int i=0 ; i<num_partns2+1; ++i) {
1629                                     auto bound_partn2 = o2_global_lb + increment2*i;
1630                                     bound_partn2.eval_all();
1631                                     for (size_t inst = 0; inst< nb_ins; inst++){
1632                                         auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
1633                                         auto cur_var_idx = unique_ids._keys->at(cur_var_id);
1634                                         string cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"}";
1635                                         bounds2.set_val(cur_idx,bound_partn2.eval(inst));
1636                                     }
1637                                 }
1638 
1639                                 // Lambda coefficient matrix when linking with partition variables
1640                                 param<> lambda_coef1(name1+"_lambda_linking_coefficients1");
1641                                 param<> lambda_coef2(name2+"_lambda_linking_coefficients2");
1642 
1643                                 // Partition coefficient matrix when linking with lambda variables
1644                                 param<> on_coef1(name1+"_partition_linking_coefficients1");
1645                                 param<> on_coef2(name2+"_partition_linking_coefficients2");
1646 
1647                                 // create constraint indices
1648                                 indices const_idx1("const_idx1");
1649                                 indices const_idx2("const_idx2");
1650 
1651                                 if(model_type == "lambda_II"){
1652 
1653                                     //fill constraint indices
1654                                     for (int i=0; i<num_partns1+1; ++i){
1655                                         const_idx1.add(to_string(i+1));
1656                                     }
1657 
1658                                     //fill constraint indices
1659                                     for (int i=0; i<num_partns2+1; ++i){
1660                                         const_idx2.add(to_string(i+1));
1661                                     }
1662 
1663                                     // Lambda coefficient matrix when linking with partition variables
1664                                     if(num_partns1 > 1) lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
1665                                     if(num_partns2 > 1) lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
1666 
1667                                     // Lambda coefficient matrix when linking with partition variables
1668                                     if(num_partns1 > 1)  on_coef1.in(indices(unique_ids, partns1, const_idx1));
1669                                     if(num_partns2 > 1)  on_coef2.in(indices(unique_ids, partns2, const_idx2));
1670 
1671                                     // fill lambda_coef1 and lambda_coef2
1672                                     for (size_t inst = 0; inst< nb_ins; inst++){
1673                                         auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
1674                                         auto cur_var_idx = unique_ids._keys->at(cur_var_id);
1675                                         for (int i=0 ; i<num_partns1+1; ++i) {
1676                                             for (int j=0 ; j<num_partns2+1; ++j) {
1677                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
1678                                                 if(num_partns1 > 1)  lambda_coef1.set_val(cur_idx,1);
1679                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(j+1);
1680                                                 if(num_partns2 > 1)  lambda_coef2.set_val(cur_idx,1);
1681                                             }
1682                                         }
1683                                     }
1684 
1685                                     // fill on_coef1 and on_coef2
1686                                     for (size_t inst = 0; inst< nb_ins; inst++){
1687                                         auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
1688                                         auto cur_var_idx = unique_ids._keys->at(cur_var_id);
1689                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
1690                                         if(num_partns1 > 1)  on_coef1.set_val(cur_idx,1);
1691                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
1692                                         if(num_partns2 > 1)  on_coef2.set_val(cur_idx,1);
1693 
1694                                         if(num_partns1 > 1) {
1695                                             for (int i=1 ; i<num_partns1; ++i) {
1696                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i)+"},"+to_string(i+1);
1697                                                 on_coef1.set_val(cur_idx,1);
1698                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
1699                                                 on_coef1.set_val(cur_idx,1);
1700                                             }
1701                                         }
1702                                         if(num_partns2 > 1) {
1703                                             for (int i=1 ; i<num_partns2; ++i) {
1704                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(i)+"},"+to_string(i+1);
1705                                                 on_coef2.set_val(cur_idx,1);
1706                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(i+1);
1707                                                 on_coef2.set_val(cur_idx,1);
1708                                             }
1709                                         }
1710                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1)+"},"+to_string(num_partns1+1);
1711                                         if(num_partns1 > 1)  on_coef1.set_val(cur_idx,1);
1712                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(num_partns2)+"},"+to_string(num_partns2+1);
1713                                         if(num_partns1 > 2)  on_coef2.set_val(cur_idx,1);
1714                                     }
1715                                 }
1716 
1717 
1718                                 else /*means model_type == "lambda_III" */{
1719 
1720                                     //fill constraint indices
1721                                     for (int i=0; i<(num_partns1-2)*2+2; ++i){
1722                                         const_idx1.add(to_string(i+1));
1723                                     }
1724 
1725                                     //fill constraint indices
1726                                     for (int i=0; i<(num_partns2-2)*2+2; ++i){
1727                                         const_idx2.add(to_string(i+1));
1728                                     }
1729 
1730                                     // Lambda coefficient matrix when linking with partition variables
1731                                     if(num_partns1 > 1)  lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
1732                                     if(num_partns2 > 1)  lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
1733 
1734                                     // Partition coefficient matrix when linking with lambda variables
1735                                     if(num_partns1 > 1)  on_coef1.in(indices(unique_ids, partns1, const_idx1));
1736                                     if(num_partns2 > 1)  on_coef2.in(indices(unique_ids, partns2, const_idx2));
1737 
1738                                     // fill lambda_coef1 and lambda_coef2
1739                                     for (size_t inst = 0; inst< nb_ins; inst++){
1740                                         auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
1741                                         auto cur_var_idx = unique_ids._keys->at(cur_var_id);
1742                                         if(num_partns1 > 1){
1743                                             for (int j=0; j<num_partns2+1; ++j) {
1744                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(1);
1745                                                 lambda_coef1.set_val(cur_idx,1);
1746                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string((num_partns1-2)*2+2);
1747                                                 lambda_coef1.set_val(cur_idx,1);
1748                                             }
1749 
1750                                             for (int i=1 ; i<(num_partns1-2)*2+1; i=i+2) {
1751                                                 for (int j=(i-1)/2 + 2; j<num_partns1+1; ++j) {
1752                                                     for(int k=0; k<num_partns2+1; ++k){
1753                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+1);
1754                                                         lambda_coef1.set_val(cur_idx,1);
1755                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+2);
1756                                                         lambda_coef1.set_val(cur_idx,-1);
1757                                                     }
1758                                                 }
1759                                             }
1760                                         }
1761                                         if(num_partns2 > 1) {
1762                                             for (int i=0; i<num_partns1+1; ++i) {
1763                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(1)+"},"+to_string(1);
1764                                                 lambda_coef2.set_val(cur_idx,1);
1765                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(num_partns2+1)+"},"+to_string((num_partns2-2)*2+2);
1766                                                 lambda_coef2.set_val(cur_idx,1);
1767                                             }
1768 
1769                                             for (int i=1 ; i<(num_partns2-2)*2+1; i=i+2) {
1770                                                 for (int j=(i-1)/2 + 2; j<num_partns2+1; ++j) {
1771                                                     for(int k=0; k<num_partns1+1; ++k){
1772                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
1773                                                         lambda_coef2.set_val(cur_idx,1);
1774                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
1775                                                         lambda_coef2.set_val(cur_idx,-1);
1776                                                     }
1777                                                 }
1778                                             }
1779                                         }
1780                                     }
1781 
1782 
1783 
1784                                     // fill on_coef1 and on_coef2
1785                                     for (size_t inst = 0; inst< nb_ins; inst++){
1786                                         auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
1787                                         auto cur_var_idx = unique_ids._keys->at(cur_var_id);
1788                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
1789                                         if(num_partns1 > 1) {
1790                                             on_coef1.set_val(cur_idx,1);
1791 
1792                                             for (int i=1; i<num_partns1; ++i) {
1793                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(2);
1794                                                 on_coef1.set_val(cur_idx, 1);
1795                                             }
1796 
1797                                             for (int i=2 ; i<(num_partns1-2)*2+2; i=i+2) {
1798                                                 for (int j=i/2+1; j<num_partns1; ++j) {
1799                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
1800                                                     on_coef1.set_val(cur_idx,-1);
1801                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
1802                                                     on_coef1.set_val(cur_idx,1);
1803                                                 }
1804                                             }
1805                                         }
1806 
1807                                         if(num_partns2 > 1) {
1808                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
1809                                             on_coef2.set_val(cur_idx,1);
1810                                             for (int i=1; i<num_partns2; ++i) {
1811                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(2);
1812                                                 on_coef2.set_val(cur_idx, 1);
1813                                             }
1814 
1815                                             for (int i=2 ; i<(num_partns2-2)*2+2; i=i+2) {
1816                                                 for (int j=i/2+1; j<num_partns2; ++j) {
1817                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
1818                                                     on_coef2.set_val(cur_idx,-1);
1819                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
1820                                                     on_coef2.set_val(cur_idx,1);
1821                                                 }
1822                                             }
1823                                         }
1824                                     }
1825                                 }
1826 
1827 
1828                                 /** Constraints */
1829                                 // Representation of the bilinear term with convex combination
1830                                 Constraint<> bln_rep(pair.first+"_bln_rep");
1831                                 bln_rep = EP.in_matrix(nb_entries,total_entries-nb_entries)*lambda.in_matrix(nb_entries,total_entries-nb_entries) - vlift.in(unique_ids);
1832                                 add(bln_rep.in(unique_ids) == 0);
1833 
1834                                 // Representation of o1 with convex combination
1835                                 Constraint<> o1_rep(pair.first+"_o1_rep");
1836 
1837                                 o1_rep = bounds1.from_ith(0,inst_partition_lambda).in_matrix(nb_entries, 1) * lambda.in_matrix(nb_entries,total_entries-nb_entries) - o1.in(o1_ids);
1838                                 add(o1_rep.in(unique_ids) == 0);
1839 
1840                                 // Representation of o2 with convex combination
1841                                 Constraint<> o2_rep(pair.first+"_o2_rep");
1842 
1843                                 o2_rep = bounds2.in_ignore_ith(nb_entries, 1, inst_partition_lambda).in_matrix(nb_entries,1) * lambda.in_matrix(nb_entries,total_entries-nb_entries) - o2.in(o2_ids);
1844                                 add(o2_rep.in(unique_ids) == 0);
1845 
1846                                 // Linking partition variables1 with lambda
1847                                 if(model_type == "lambda_II"){
1848                                     if(num_partns1 > 1) {
1849                                         Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_II");
1850                                         on_link_lambda1 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
1851                                         add(on_link_lambda1.in(indices(unique_ids,const_idx1)) <= 0);
1852                                     }
1853                                     if(num_partns2 > 1) {
1854                                         Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_II");
1855                                         on_link_lambda2 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - on2.in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
1856                                         add(on_link_lambda2.in(indices(unique_ids,const_idx2)) <= 0);
1857 
1858                                     }
1859                                 }
1860                                 else{
1861                                     if(num_partns1 > 1) {
1862                                         Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_III");
1863                                         on_link_lambda1 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
1864                                         add(on_link_lambda1.in(indices(unique_ids,const_idx1)) <= 0);
1865                                     }
1866                                     if(num_partns2 > 1) {
1867                                         Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_III");
1868                                         on_link_lambda2 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - on2.in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
1869                                         add(on_link_lambda2.in(indices(unique_ids,const_idx2)) <= 0);
1870                                     }
1871                                 }
1872                                 // sum over lambda
1873                                 Constraint<> lambdaSum(pair.first+"_lambdaSum");
1874                                 lambdaSum = sum(lambda.in_matrix(nb_entries,total_entries-nb_entries));
1875                                 add(lambdaSum.in(unique_ids) == 1);
1876                             }
1877 
1878                         }
1879                         else if(binvar_ptr1 ==_vars_name.end() && binvar_ptr2 !=_vars_name.end()){ //means v2 has been partitioned before)
1880                             DebugOn("<<<<<<<<<< THIS IS NOT SEEN LIFT -> DOUBLE -> SEEN SECOND BINARY -> DIFF CORE VARS <<<<<<<<<<<" << endl);
1881 
1882                             var<int> on1(name1+"_binary",0,1);
1883                             indices partns1("partns1");
1884                             for (int i = 0; i < num_partns1 ; ++i)
1885                             {
1886                                 partns1.add(name1+ "{" +to_string(i+1) + "}");
1887                             }
1888                             add(on1.in(o1_ids_uq,partns1));
1889 
1890                             indices partns2("partns2");
1891                             for (int i = 0; i < num_partns2 ; ++i)
1892                             {
1893                                 partns2.add(name2+ "{" + to_string(i+1) + "}");
1894                             }
1895 
1896                             auto binvar2 = static_pointer_cast<var<int>>(binvar_ptr2->second);
1897                             param<int> lb2("lb2"), ub2("ub2");
1898                             lb2.in(o2_ids_uq,partns2);
1899                             ub2.in(o2_ids_uq,partns2);
1900                             lb2.set_val(0), ub2.set_val(1);
1901                             auto added2 = binvar2->add_bounds(lb2,ub2);
1902                             reindex_vars();
1903 
1904                             auto nb_entries_v1 = o1_ids.get_nb_entries();
1905                             auto nb_entries_v2 = o2_ids.get_nb_entries();
1906                             auto nb_entries = unique_ids.get_nb_entries();
1907 
1908                             if(!added2.empty()){
1909                                 Constraint<> onSum2(o2._name+"_binarySum");
1910                                 onSum2 = sum(binvar2->in(added2).in_matrix(nb_entries_v2,1));
1911                                 auto vset2 = added2.from_ith(0,nb_entries_v2);
1912                                 vset2.filter_refs(vset2.get_unique_refs());
1913                                 add(onSum2.in(vset2) == 1);
1914                             }
1915 
1916                             Constraint<> onSum1(o1._name+"_binarySum");
1917                             onSum1 = sum(on1.in_matrix(nb_entries_v1,1));
1918                             add(onSum1.in(o1_ids_uq) == 1);
1919 
1920                             if(model_type == "on/off"){//if on/off is chosen
1921                                 var<int> on(name1+name2+"_binary",0,1);
1922 
1923                                 indices partns("partns");
1924                                 partns = indices(partns1,partns2);
1925                                 auto inst_partition = indices(unique_ids,partns);
1926                                 add(on.in(inst_partition));
1927                                 auto total_entries = inst_partition.get_nb_entries();
1928 
1929                                 Constraint<> onLink1(pair.first+"_binaryLink1");
1930                                 onLink1 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) - on;
1931                                 add(onLink1.in(inst_partition) >= 0);
1932 
1933                                 Constraint<> onLink2(pair.first+"_binaryLink2");
1934                                 onLink2 = binvar2->in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - on;
1935                                 add(onLink2.in(inst_partition) >= 0);
1936 
1937                                 Constraint<> onLink3(pair.first+"_binaryLink3");
1938                                 onLink3 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) + binvar2->in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - on;
1939                                 add(onLink3.in(inst_partition) <= 0);
1940 
1941                                 Constraint<> onSumComb(pair.first+"_binarySum");
1942                                 onSumComb = sum(on.in_matrix(nb_entries,total_entries-nb_entries));
1943                                 add(onSumComb.in(unique_ids) == 1);
1944 
1945                                 add_on_off_McCormick_refined(pair.first, vlift.in(unique_ids), o1.in(o1_ids), o2.in(o2_ids), on);
1946                             }
1947 
1948 
1949                             else{ //means it is one of the lambda formulations
1950 
1951                                 //difference is this has one more partition index
1952                                 indices partns1_lambda("partns1_lambda");
1953                                 for (int i = 0; i < num_partns1+1; ++i)
1954                                 {
1955                                     partns1_lambda.add(name1+ "{" +to_string(i+1) + "}");
1956                                 }
1957 
1958                                 indices partns2_lambda("partns2_lambda");
1959                                 for (int i = 0; i < num_partns2+1; ++i)
1960                                 {
1961                                     partns2_lambda.add(name2+ "{" +to_string(i+1) + "}");
1962                                 }
1963 
1964                                 indices partns_lambda("partns_lambda");
1965                                 partns_lambda = indices(partns1_lambda,partns2_lambda);
1966                                 auto inst_partition_lambda = indices(unique_ids,partns_lambda);
1967                                 auto inst_partition_bounds1 = indices(unique_ids,partns1_lambda);
1968                                 auto inst_partition_bounds2 = indices(unique_ids,partns2_lambda);
1969 
1970                                 // Convex combination variables
1971                                 var<> lambda(name1+name2+"_lambda",pos_);
1972                                 add(lambda.in(inst_partition_lambda));
1973 
1974                                 /** Parameters */
1975                                 // Bounds on variable v1 & v2
1976                                 param<> bounds1(name1+"_bounds1");
1977                                 bounds1.in(inst_partition_bounds1);
1978 
1979                                 param<> bounds2(name2+"_bounds2");
1980                                 bounds2.in(inst_partition_bounds2);
1981 
1982                                 // Function values on the extreme points
1983                                 param<> EP(name1+name2+"_grid_values");
1984                                 EP.in(inst_partition_lambda);
1985                                 auto total_entries = inst_partition_lambda.get_nb_entries();
1986 
1987                                 size_t nb_ins = vlift.in(unique_ids).get_nb_inst();
1988                                 auto o1_global_lb = o1.get_lb();
1989                                 auto increment1 = (o1.get_ub() - o1_global_lb)/num_partns1;
1990 
1991                                 auto o2_global_lb = o2.get_lb();
1992                                 auto increment2 = (o2.get_ub() - o2_global_lb)/num_partns2;
1993 
1994                                 // fill bounds1 and function values
1995                                 for (int i=0 ; i<num_partns1+1; ++i) {
1996                                     auto bound_partn1 = o1_global_lb + increment1*i;
1997                                     bound_partn1.eval_all();
1998                                     for (size_t inst = 0; inst< nb_ins; inst++){
1999                                         auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2000                                         auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2001                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
2002                                         bounds1.set_val(cur_idx,bound_partn1.eval(inst));
2003                                         for(int j=0; j<num_partns2+1; ++j){
2004                                             auto bound_partn2 = o2_global_lb + increment2*j;
2005                                             bound_partn2.eval_all();
2006                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"}";
2007                                             EP.set_val(cur_idx,(bound_partn1.eval(inst)*bound_partn2.eval(inst)));
2008                                         }
2009                                     }
2010                                 }
2011                                 // fill bounds2
2012                                 for (int i=0 ; i<num_partns2+1; ++i) {
2013                                     auto bound_partn2 = o2_global_lb + increment2*i;
2014                                     bound_partn2.eval_all();
2015                                     for (size_t inst = 0; inst< nb_ins; inst++){
2016                                         auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2017                                         auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2018                                         string cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"}";
2019                                         bounds2.set_val(cur_idx,bound_partn2.eval(inst));
2020                                     }
2021                                 }
2022 
2023                                 // Lambda coefficient matrix when linking with partition variables
2024                                 param<> lambda_coef1(name1+"_lambda_linking_coefficients1");
2025                                 param<> lambda_coef2(name2+"_lambda_linking_coefficients2");
2026 
2027                                 // Partition coefficient matrix when linking with lambda variables
2028                                 param<> on_coef1(name1+"_partition_linking_coefficients1");
2029                                 param<> on_coef2(name2+"_partition_linking_coefficients2");
2030 
2031                                 // create constraint indices
2032                                 indices const_idx1("const_idx1");
2033                                 indices const_idx2("const_idx2");
2034 
2035                                 if(model_type == "lambda_II"){
2036 
2037                                     //fill constraint indices
2038                                     for (int i=0; i<num_partns1+1; ++i){
2039                                         const_idx1.add(to_string(i+1));
2040                                     }
2041 
2042                                     //fill constraint indices
2043                                     for (int i=0; i<num_partns2+1; ++i){
2044                                         const_idx2.add(to_string(i+1));
2045                                     }
2046 
2047                                     // Lambda coefficient matrix when linking with partition variables
2048                                     if(num_partns1 > 1) lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
2049                                     if(num_partns2 > 1) lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
2050 
2051                                     // Lambda coefficient matrix when linking with partition variables
2052                                     if(num_partns1 > 1)  on_coef1.in(indices(unique_ids, partns1, const_idx1));
2053                                     if(num_partns2 > 1)  on_coef2.in(indices(unique_ids, partns2, const_idx2));
2054 
2055                                     // fill lambda_coef1 and lambda_coef2
2056                                     for (size_t inst = 0; inst< nb_ins; inst++){
2057                                         auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2058                                         auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2059                                         for (int i=0 ; i<num_partns1+1; ++i) {
2060                                             for (int j=0 ; j<num_partns2+1; ++j) {
2061                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
2062                                                 if(num_partns1 > 1)  lambda_coef1.set_val(cur_idx,1);
2063                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(j+1);
2064                                                 if(num_partns2 > 1)  lambda_coef2.set_val(cur_idx,1);
2065                                             }
2066                                         }
2067                                     }
2068 
2069                                     // fill on_coef1 and on_coef2
2070                                     for (size_t inst = 0; inst< nb_ins; inst++){
2071                                         auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2072                                         auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2073                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
2074                                         if(num_partns1 > 1)  on_coef1.set_val(cur_idx,1);
2075                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
2076                                         if(num_partns2 > 1)  on_coef2.set_val(cur_idx,1);
2077 
2078                                         if(num_partns1 > 1) {
2079                                             for (int i=1 ; i<num_partns1; ++i) {
2080                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i)+"},"+to_string(i+1);
2081                                                 on_coef1.set_val(cur_idx,1);
2082                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
2083                                                 on_coef1.set_val(cur_idx,1);
2084                                             }
2085                                         }
2086                                         if(num_partns2 > 1) {
2087                                             for (int i=1 ; i<num_partns2; ++i) {
2088                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(i)+"},"+to_string(i+1);
2089                                                 on_coef2.set_val(cur_idx,1);
2090                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(i+1);
2091                                                 on_coef2.set_val(cur_idx,1);
2092                                             }
2093                                         }
2094                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1)+"},"+to_string(num_partns1+1);
2095                                         if(num_partns1 > 1)  on_coef1.set_val(cur_idx,1);
2096                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(num_partns2)+"},"+to_string(num_partns2+1);
2097                                         if(num_partns1 > 2)  on_coef2.set_val(cur_idx,1);
2098                                     }
2099                                 }
2100 
2101 
2102                                 else /*means model_type == "lambda_III" */{
2103 
2104                                     //fill constraint indices
2105                                     for (int i=0; i<(num_partns1-2)*2+2; ++i){
2106                                         const_idx1.add(to_string(i+1));
2107                                     }
2108 
2109                                     //fill constraint indices
2110                                     for (int i=0; i<(num_partns2-2)*2+2; ++i){
2111                                         const_idx2.add(to_string(i+1));
2112                                     }
2113 
2114                                     // Lambda coefficient matrix when linking with partition variables
2115                                     if(num_partns1 > 1)  lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
2116                                     if(num_partns2 > 1)  lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
2117 
2118                                     // Partition coefficient matrix when linking with lambda variables
2119                                     if(num_partns1 > 1)  on_coef1.in(indices(unique_ids, partns1, const_idx1));
2120                                     if(num_partns2 > 1)  on_coef2.in(indices(unique_ids, partns2, const_idx2));
2121 
2122                                     // fill lambda_coef1 and lambda_coef2
2123                                     for (size_t inst = 0; inst< nb_ins; inst++){
2124                                         auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2125                                         auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2126                                         if(num_partns1 > 1){
2127                                             for (int j=0; j<num_partns2+1; ++j) {
2128                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(1);
2129                                                 lambda_coef1.set_val(cur_idx,1);
2130                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string((num_partns1-2)*2+2);
2131                                                 lambda_coef1.set_val(cur_idx,1);
2132                                             }
2133 
2134                                             for (int i=1 ; i<(num_partns1-2)*2+1; i=i+2) {
2135                                                 for (int j=(i-1)/2 + 2; j<num_partns1+1; ++j) {
2136                                                     for(int k=0; k<num_partns2+1; ++k){
2137                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+1);
2138                                                         lambda_coef1.set_val(cur_idx,1);
2139                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+2);
2140                                                         lambda_coef1.set_val(cur_idx,-1);
2141                                                     }
2142                                                 }
2143                                             }
2144                                         }
2145                                         if(num_partns2 > 1) {
2146                                             for (int i=0; i<num_partns1+1; ++i) {
2147                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(1)+"},"+to_string(1);
2148                                                 lambda_coef2.set_val(cur_idx,1);
2149                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(num_partns2+1)+"},"+to_string((num_partns2-2)*2+2);
2150                                                 lambda_coef2.set_val(cur_idx,1);
2151                                             }
2152 
2153                                             for (int i=1 ; i<(num_partns2-2)*2+1; i=i+2) {
2154                                                 for (int j=(i-1)/2 + 2; j<num_partns2+1; ++j) {
2155                                                     for(int k=0; k<num_partns1+1; ++k){
2156                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
2157                                                         lambda_coef2.set_val(cur_idx,1);
2158                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
2159                                                         lambda_coef2.set_val(cur_idx,-1);
2160                                                     }
2161                                                 }
2162                                             }
2163                                         }
2164                                     }
2165 
2166 
2167 
2168                                     // fill on_coef1 and on_coef2
2169                                     for (size_t inst = 0; inst< nb_ins; inst++){
2170                                         auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2171                                         auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2172                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
2173                                         if(num_partns1 > 1) {
2174                                             on_coef1.set_val(cur_idx,1);
2175 
2176                                             for (int i=1; i<num_partns1; ++i) {
2177                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(2);
2178                                                 on_coef1.set_val(cur_idx, 1);
2179                                             }
2180 
2181                                             for (int i=2 ; i<(num_partns1-2)*2+2; i=i+2) {
2182                                                 for (int j=i/2+1; j<num_partns1; ++j) {
2183                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
2184                                                     on_coef1.set_val(cur_idx,-1);
2185                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
2186                                                     on_coef1.set_val(cur_idx,1);
2187                                                 }
2188                                             }
2189                                         }
2190 
2191                                         if(num_partns2 > 1) {
2192                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
2193                                             on_coef2.set_val(cur_idx,1);
2194                                             for (int i=1; i<num_partns2; ++i) {
2195                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(2);
2196                                                 on_coef2.set_val(cur_idx, 1);
2197                                             }
2198 
2199                                             for (int i=2 ; i<(num_partns2-2)*2+2; i=i+2) {
2200                                                 for (int j=i/2+1; j<num_partns2; ++j) {
2201                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
2202                                                     on_coef2.set_val(cur_idx,-1);
2203                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
2204                                                     on_coef2.set_val(cur_idx,1);
2205                                                 }
2206                                             }
2207                                         }
2208                                     }
2209                                 }
2210 
2211 
2212                                 /** Constraints */
2213                                 // Representation of the bilinear term with convex combination
2214                                 Constraint<> bln_rep(pair.first+"_bln_rep");
2215                                 bln_rep = EP.in_matrix(nb_entries,total_entries-nb_entries)*lambda.in_matrix(nb_entries,total_entries-nb_entries) - vlift.in(unique_ids);
2216                                 add(bln_rep.in(unique_ids) == 0);
2217 
2218                                 // Representation of o1 with convex combination
2219                                 Constraint<> o1_rep(pair.first+"_o1_rep");
2220 
2221                                 o1_rep = bounds1.from_ith(0,inst_partition_lambda).in_matrix(nb_entries, 1) * lambda.in_matrix(nb_entries,total_entries-nb_entries) - o1.in(o1_ids);
2222                                 add(o1_rep.in(unique_ids) == 0);
2223 
2224                                 // Representation of o2 with convex combination
2225                                 Constraint<> o2_rep(pair.first+"_o2_rep");
2226 
2227                                 o2_rep = bounds2.in_ignore_ith(nb_entries, 1, inst_partition_lambda).in_matrix(nb_entries,1) * lambda.in_matrix(nb_entries,total_entries-nb_entries) - o2.in(o2_ids);
2228                                 add(o2_rep.in(unique_ids) == 0);
2229 
2230                                 // Linking partition variables1 with lambda
2231                                 if(model_type == "lambda_II"){
2232                                     if(num_partns1 > 1) {
2233                                         Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_II");
2234                                         on_link_lambda1 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
2235                                         add(on_link_lambda1.in(indices(unique_ids,const_idx1)) <= 0);
2236                                     }
2237                                     if(num_partns2 > 1) {
2238                                         Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_II");
2239                                         on_link_lambda2 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - binvar2->in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
2240                                         add(on_link_lambda2.in(indices(unique_ids,const_idx2)) <= 0);
2241 
2242                                     }
2243                                 }
2244                                 else{
2245                                     if(num_partns1 > 1) {
2246                                         Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_III");
2247                                         on_link_lambda1 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
2248                                         add(on_link_lambda1.in(indices(unique_ids,const_idx1)) <= 0);
2249                                     }
2250                                     if(num_partns2 > 1) {
2251                                         Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_III");
2252                                         on_link_lambda2 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - binvar2->in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
2253                                         add(on_link_lambda2.in(indices(unique_ids,const_idx2)) <= 0);
2254                                     }
2255                                 }
2256                                 // sum over lambda
2257                                 Constraint<> lambdaSum(pair.first+"_lambdaSum");
2258                                 lambdaSum = sum(lambda.in_matrix(nb_entries,total_entries-nb_entries));
2259                                 add(lambdaSum.in(unique_ids) == 1);
2260                             }
2261 
2262                         }
2263                         else{ //means both variables v1 and v2 haven't been partitioned
2264                             if(name1 == name2){
2265                                 DebugOn("<<<<<<<<<< THIS IS NOT SEEN LIFT -> DOUBLE -> NOT SEEN BINARIES -> SAME CORE VARS <<<<<<<<<<<" << endl);
2266 
2267                                 var<int> on1(name1+"_binary",0,1);
2268                                 indices partns1("partns1");
2269                                 for (int i = 0; i < num_partns1 ; ++i)
2270                                 {
2271                                     partns1.add(name1+ "{" +to_string(i+1) + "}");
2272                                 }
2273                                 add(on1.in(union_ids(o1_ids_uq, o2_ids_uq),partns1));
2274                                 indices partns2("partns2");
2275                                 for (int i = 0; i < num_partns2 ; ++i)
2276                                 {
2277                                     partns2.add(name2+ "{" +to_string(i+1) + "}");
2278                                 }
2279 
2280                                 auto nb_entries_v1 = o1_ids.get_nb_entries();
2281                                 auto nb_entries_v2 = o2_ids.get_nb_entries();
2282                                 auto nb_entries = unique_ids.get_nb_entries();
2283 
2284                                 Constraint<> onSum1(o1._name+"_binarySum");
2285                                 onSum1 = sum(on1.in_matrix(nb_entries_v1,1));
2286                                 add(onSum1.in(union_ids(o1_ids_uq,o2_ids_uq)) == 1);
2287 
2288                                 if(model_type == "on/off")
2289                                 {
2290                                     var<int> on(name1+name2+"_binary",0,1);
2291 
2292                                     indices partns("partns");
2293                                     partns = indices(partns1,partns2);
2294                                     auto inst_partition = indices(unique_ids,partns);
2295                                     add(on.in(inst_partition));
2296                                     auto total_entries = inst_partition.get_nb_entries();
2297 
2298                                     Constraint<> onLink1(pair.first+"_binaryLink1");
2299                                     onLink1 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) - on;
2300                                     add(onLink1.in(inst_partition) >= 0);
2301 
2302                                     Constraint<> onLink2(pair.first+"_binaryLink2");
2303                                     onLink2 = on1.in_ignore_ith(nb_entries_v1,1,inst_partition.ignore_ith(0,nb_entries_v1)) - on;
2304                                     add(onLink2.in(inst_partition) >= 0);
2305 
2306                                     Constraint<> onLink3(pair.first+"_binaryLink3");
2307                                     onLink3 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) + on1.in_ignore_ith(nb_entries_v1,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - on;
2308                                     add(onLink3.in(inst_partition) <= 0);
2309 
2310                                     Constraint<> onSumComb(pair.first+"_binarySum");
2311                                     onSumComb = sum(on.in_matrix(nb_entries,total_entries-nb_entries));
2312                                     add(onSumComb.in(unique_ids) == 1);
2313 
2314                                     add_on_off_McCormick_refined(pair.first, vlift.in(unique_ids), o1.in(o1_ids), o2.in(o2_ids), on);
2315                                 }
2316 
2317                                 else{ //means it is one of the lambda formulations
2318 
2319                                     //difference is this has one more partition index
2320                                     indices partns1_lambda("partns1_lambda");
2321                                     for (int i = 0; i < num_partns1+1; ++i)
2322                                     {
2323                                         partns1_lambda.add(name1+ "{" +to_string(i+1) + "}");
2324                                     }
2325 
2326                                     indices partns2_lambda("partns2_lambda");
2327                                     for (int i = 0; i < num_partns2+1; ++i)
2328                                     {
2329                                         partns2_lambda.add(name2+ "{" +to_string(i+1) + "}");
2330                                     }
2331 
2332                                     indices partns_lambda("partns_lambda");
2333                                     partns_lambda = indices(partns1_lambda,partns2_lambda);
2334                                     auto inst_partition_lambda = indices(unique_ids,partns_lambda);
2335                                     auto inst_partition_bounds1 = indices(unique_ids,partns1_lambda);
2336                                     auto inst_partition_bounds2 = indices(unique_ids,partns2_lambda);
2337 
2338                                     // Convex combination variables
2339                                     var<> lambda(name1+name2+"_lambda",pos_);
2340                                     add(lambda.in(inst_partition_lambda));
2341 
2342                                     /** Parameters */
2343                                     // Bounds on variable v1 & v2
2344                                     param<> bounds1(name1+"_bounds1");
2345                                     bounds1.in(inst_partition_bounds1);
2346 
2347                                     param<> bounds2(name2+"_bounds2");
2348                                     bounds2.in(inst_partition_bounds2);
2349 
2350                                     // Function values on the extreme points
2351                                     param<> EP(name1+name2+"_grid_values");
2352                                     EP.in(inst_partition_lambda);
2353                                     auto total_entries = inst_partition_lambda.get_nb_entries();
2354 
2355                                     size_t nb_ins = vlift.in(unique_ids).get_nb_inst();
2356 
2357                                     auto o1_global_lb = o1.get_lb();
2358                                     auto increment1 = (o1.get_ub() - o1_global_lb)/num_partns1;
2359 
2360                                     auto o2_global_lb = o2.get_lb();
2361                                     auto increment2 = (o2.get_ub() - o2_global_lb)/num_partns2;
2362 
2363                                     // fill bounds and function values
2364                                     for (int i=0 ; i<num_partns1+1; ++i) {
2365                                         auto bound_partn1 = o1_global_lb + increment1*i;
2366                                         bound_partn1.eval_all();
2367                                         auto bound_partn2 = o2_global_lb + increment2*i;
2368                                         bound_partn2.eval_all();
2369                                         for (size_t inst = 0; inst< nb_ins; inst++){
2370                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2371                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2372                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
2373                                             bounds1.set_val(cur_idx,bound_partn1.eval(inst));
2374                                             bounds2.set_val(cur_idx,bound_partn2.eval(inst));
2375                                             for(int j=0; j<num_partns2+1; ++j){
2376                                                 auto bound_partn2_temp = o2_global_lb + increment2*j;
2377                                                 bound_partn2_temp.eval_all();
2378                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"}";
2379                                                 EP.set_val(cur_idx,(bound_partn1.eval(inst)*bound_partn2_temp.eval(inst)));
2380                                             }
2381                                         }
2382                                     }
2383 
2384                                     // Lambda coefficient matrix when linking with partition variables
2385                                     param<> lambda_coef1(name1+"_lambda_linking_coefficients1");
2386                                     param<> lambda_coef2(name2+"_lambda_linking_coefficients2");
2387 
2388                                     // Partition coefficient matrix when linking with lambda variables
2389                                     param<> on_coef1(name1+"_partition_linking_coefficients1");
2390                                     param<> on_coef2(name2+"_partition_linking_coefficients2");
2391 
2392                                     // create constraint indices
2393                                     indices const_idx1("const_idx1");
2394                                     indices const_idx2("const_idx2");
2395 
2396                                     if(model_type == "lambda_II"){
2397 
2398                                         //fill constraint indices
2399                                         for (int i=0; i<num_partns1+1; ++i){
2400                                             const_idx1.add(to_string(i+1));
2401                                         }
2402 
2403                                         //fill constraint indices
2404                                         for (int i=0; i<num_partns2+1; ++i){
2405                                             const_idx2.add(to_string(i+1));
2406                                         }
2407 
2408                                         // Lambda coefficient matrix when linking with partition variables
2409                                         lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
2410                                         lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
2411 
2412                                         // Lambda coefficient matrix when linking with partition variables
2413                                         on_coef1.in(indices(unique_ids, partns1, const_idx1));
2414                                         on_coef2.in(indices(unique_ids, partns2, const_idx2));
2415 
2416                                         // fill lambda_coef1 and lambda_coef2
2417                                         for (size_t inst = 0; inst< nb_ins; inst++){
2418                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2419                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2420                                             for (int i=0 ; i<num_partns1+1; ++i) {
2421                                                 for (int j=0 ; j<num_partns2+1; ++j) {
2422                                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
2423                                                     lambda_coef1.set_val(cur_idx,1);
2424                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(j+1);
2425                                                     lambda_coef2.set_val(cur_idx,1);
2426                                                 }
2427                                             }
2428                                         }
2429 
2430                                         // fill on_coef1 and on_coef2
2431                                         for (size_t inst = 0; inst< nb_ins; inst++){
2432                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2433                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2434                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
2435                                             on_coef1.set_val(cur_idx,1);
2436                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
2437                                             on_coef2.set_val(cur_idx,1);
2438                                             for (int i=1 ; i<num_partns1; ++i) {
2439                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i)+"},"+to_string(i+1);
2440                                                 on_coef1.set_val(cur_idx,1);
2441                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
2442                                                 on_coef1.set_val(cur_idx,1);
2443                                             }
2444                                             for (int i=1 ; i<num_partns2; ++i) {
2445                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(i)+"},"+to_string(i+1);
2446                                                 on_coef2.set_val(cur_idx,1);
2447                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(i+1);
2448                                                 on_coef2.set_val(cur_idx,1);
2449                                             }
2450                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1)+"},"+to_string(num_partns1+1);
2451                                             on_coef1.set_val(cur_idx,1);
2452                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(num_partns2)+"},"+to_string(num_partns2+1);
2453                                             on_coef2.set_val(cur_idx,1);
2454                                         }
2455                                     }
2456 
2457 
2458                                     else /*means model_type == "lambda_III" */{
2459 
2460                                         //fill constraint indices
2461                                         for (int i=0; i<(num_partns1-2)*2+2; ++i){
2462                                             const_idx1.add(to_string(i+1));
2463                                         }
2464 
2465                                         //fill constraint indices
2466                                         for (int i=0; i<(num_partns2-2)*2+2; ++i){
2467                                             const_idx2.add(to_string(i+1));
2468                                         }
2469 
2470                                         // Lambda coefficient matrix when linking with partition variables
2471                                         lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
2472                                         lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
2473 
2474                                         // Partition coefficient matrix when linking with lambda variables
2475                                         on_coef1.in(indices(unique_ids, partns1, const_idx1));
2476                                         on_coef2.in(indices(unique_ids, partns2, const_idx2));
2477 
2478                                         // fill lambda_coef1 and lambda_coef2
2479                                         for (size_t inst = 0; inst< nb_ins; inst++){
2480                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2481                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2482                                             for (int j=0; j<num_partns2+1; ++j) {
2483                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(1);
2484                                                 lambda_coef1.set_val(cur_idx,1);
2485                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string((num_partns1-2)*2+2);
2486                                                 lambda_coef1.set_val(cur_idx,1);
2487                                             }
2488 
2489                                             for (int i=1 ; i<(num_partns1-2)*2+1; i=i+2) {
2490                                                 for (int j=(i-1)/2 + 2; j<num_partns1+1; ++j) {
2491                                                     for(int k=0; k<num_partns2+1; ++k){
2492                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+1);
2493                                                         lambda_coef1.set_val(cur_idx,1);
2494                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+2);
2495                                                         lambda_coef1.set_val(cur_idx,-1);
2496                                                     }
2497                                                 }
2498                                             }
2499 
2500                                             for (int i=0; i<num_partns1+1; ++i) {
2501                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(1)+"},"+to_string(1);
2502                                                 lambda_coef2.set_val(cur_idx,1);
2503                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(num_partns2+1)+"},"+to_string((num_partns2-2)*2+2);
2504                                                 lambda_coef2.set_val(cur_idx,1);
2505                                             }
2506 
2507                                             for (int i=1 ; i<(num_partns2-2)*2+1; i=i+2) {
2508                                                 for (int j=(i-1)/2 + 2; j<num_partns2+1; ++j) {
2509                                                     for(int k=0; k<num_partns1+1; ++k){
2510                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
2511                                                         lambda_coef2.set_val(cur_idx,1);
2512                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
2513                                                         lambda_coef2.set_val(cur_idx,-1);
2514                                                     }
2515                                                 }
2516                                             }
2517                                         }
2518 
2519 
2520 
2521                                         // fill on_coef1 and on_coef2
2522                                         for (size_t inst = 0; inst< nb_ins; inst++){
2523                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2524                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2525                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
2526                                             on_coef1.set_val(cur_idx,1);
2527 
2528                                             for (int i=1; i<num_partns1; ++i) {
2529                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(2);
2530                                                 on_coef1.set_val(cur_idx, 1);
2531                                             }
2532 
2533                                             for (int i=2 ; i<(num_partns1-2)*2+2; i=i+2) {
2534                                                 for (int j=i/2+1; j<num_partns1; ++j) {
2535                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
2536                                                     on_coef1.set_val(cur_idx,-1);
2537                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
2538                                                     on_coef1.set_val(cur_idx,1);
2539                                                 }
2540                                             }
2541 
2542                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
2543                                             on_coef2.set_val(cur_idx,1);
2544                                             for (int i=1; i<num_partns2; ++i) {
2545                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(2);
2546                                                 on_coef2.set_val(cur_idx, 1);
2547                                             }
2548 
2549                                             for (int i=2 ; i<(num_partns2-2)*2+2; i=i+2) {
2550                                                 for (int j=i/2+1; j<num_partns2; ++j) {
2551                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
2552                                                     on_coef2.set_val(cur_idx,-1);
2553                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
2554                                                     on_coef2.set_val(cur_idx,1);
2555                                                 }
2556                                             }
2557                                         }
2558                                     }
2559 
2560 
2561                                     /** Constraints */
2562                                     // Representation of the bilinear term with convex combination
2563                                     Constraint<> bln_rep(pair.first+"_bln_rep");
2564                                     bln_rep = EP.in_matrix(nb_entries,total_entries-nb_entries)*lambda.in_matrix(nb_entries,total_entries-nb_entries) - vlift.in(unique_ids);
2565                                     add(bln_rep.in(unique_ids) == 0);
2566 
2567                                     // Representation of o1 with convex combination
2568                                     Constraint<> o1_rep(pair.first+"_o1_rep");
2569                                     o1_rep = bounds1.from_ith(0,inst_partition_lambda).in_matrix(nb_entries, 1) * lambda.in_matrix(nb_entries,total_entries-nb_entries) - o1.in(o1_ids);
2570                                     add(o1_rep.in(unique_ids) == 0);
2571 
2572                                     // Representation of o2 with convex combination
2573                                     Constraint<> o2_rep(pair.first+"_o2_rep");
2574                                     o2_rep = bounds2.in_ignore_ith(nb_entries, 1, inst_partition_lambda).in_matrix(nb_entries,1) * lambda.in_matrix(nb_entries,total_entries-nb_entries) - o2.in(o2_ids);
2575                                     add(o2_rep.in(unique_ids) == 0);
2576 
2577                                     // Linking partition variables1 with lambda
2578                                     if(model_type == "lambda_II"){
2579                                         Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_II");
2580                                         on_link_lambda1 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
2581                                         add(on_link_lambda1.in(indices(unique_ids,const_idx1)) <= 0);
2582 
2583                                         Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_II");
2584                                         on_link_lambda2 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
2585                                         add(on_link_lambda2.in(indices(unique_ids,const_idx2)) <= 0);
2586                                     }
2587                                     else{
2588                                         Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_III");
2589                                         on_link_lambda1 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
2590                                         add(on_link_lambda1.in(indices(unique_ids,const_idx1)) <= 0);
2591 
2592                                         Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_III");
2593                                         on_link_lambda2 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
2594                                         add(on_link_lambda2.in(indices(unique_ids,const_idx2)) <= 0);
2595                                     }
2596                                     // sum over lambda
2597                                     Constraint<> lambdaSum(pair.first+"_lambdaSum");
2598                                     lambdaSum = sum(lambda.in_matrix(nb_entries,total_entries-nb_entries));
2599                                     add(lambdaSum.in(unique_ids) == 1);
2600                                 }
2601                             }
2602                             else{
2603                                 DebugOn("<<<<<<<<<< THIS IS NOT SEEN LIFT -> DOUBLE -> NOT SEEN BINARIES -> DIFF CORE VARS <<<<<<<<<<<" << endl);
2604 
2605                                 var<int> on1(name1+"_binary",0,1);
2606                                 indices partns1("partns1");
2607                                 for (int i = 0; i < num_partns1 ; ++i)
2608                                 {
2609                                     partns1.add(name1+ "{" + to_string(i+1) + "}");
2610                                 }
2611                                 add(on1.in(o1_ids_uq,partns1));
2612 
2613                                 var<int> on2(name2+"_binary",0,1);
2614                                 indices partns2("partns2");
2615                                 for (int i = 0; i < num_partns2 ; ++i)
2616                                 {
2617                                     partns2.add(name2+ "{" + to_string(i+1) + "}");
2618                                 }
2619                                 add(on2.in(o2_ids_uq,partns2));
2620 
2621                                 auto nb_entries_v1 = o1_ids.get_nb_entries();
2622                                 auto nb_entries_v2 = o2_ids.get_nb_entries();
2623                                 auto nb_entries = unique_ids.get_nb_entries();
2624 
2625                                 Constraint<> onSum1(o1._name+"_binarySum");
2626                                 onSum1 = sum(on1.in_matrix(nb_entries_v1,1));
2627                                 add(onSum1.in(o1_ids_uq) == 1);
2628 
2629                                 Constraint<> onSum2(o2._name+"_binarySum");
2630                                 onSum2 = sum(on2.in_matrix(nb_entries_v2,1));
2631                                 add(onSum2.in(o2_ids_uq) == 1);
2632 
2633                                 if(model_type == "on/off"){//if on/off is chosen
2634                                     var<int> on(name1+name2+"_binary",0,1);
2635 
2636                                     indices partns("partns");
2637                                     partns = indices(partns1,partns2);
2638                                     auto inst_partition = indices(unique_ids,partns);
2639                                     add(on.in(inst_partition));
2640                                     auto total_entries = inst_partition.get_nb_entries();
2641 
2642                                     Constraint<> onLink1(pair.first+"_binaryLink1");
2643                                     onLink1 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) - on;
2644                                     add(onLink1.in(inst_partition) >= 0);
2645 
2646                                     Constraint<> onLink2(pair.first+"_binaryLink2");
2647                                     onLink2 = on2.in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - on;
2648                                     add(onLink2.in(inst_partition) >= 0);
2649 
2650                                     Constraint<> onLink3(pair.first+"_binaryLink3");
2651                                     onLink3 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) + on2.in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - on;
2652                                     add(onLink3.in(inst_partition) <= 0);
2653 
2654                                     Constraint<> onSumComb(pair.first+"_binarySum");
2655                                     onSumComb = sum(on.in_matrix(nb_entries,total_entries-nb_entries));
2656                                     add(onSumComb.in(unique_ids) == 1);
2657 
2658                                     add_on_off_McCormick_refined(pair.first, vlift.in(unique_ids), o1.in(o1_ids), o2.in(o2_ids), on);
2659                                 }
2660 
2661                                 else{//means it is one of the lambda formulations
2662 
2663                                     //difference is this has one more partition index
2664                                     indices partns1_lambda("partns1_lambda");
2665                                     for (int i = 0; i < num_partns1+1; ++i)
2666                                     {
2667                                         partns1_lambda.add(name1+ "{" +to_string(i+1) + "}");
2668                                     }
2669 
2670                                     indices partns2_lambda("partns2_lambda");
2671                                     for (int i = 0; i < num_partns2+1; ++i)
2672                                     {
2673                                         partns2_lambda.add(name2+ "{" +to_string(i+1) + "}");
2674                                     }
2675 
2676                                     indices partns_lambda("partns_lambda");
2677                                     partns_lambda = indices(partns1_lambda,partns2_lambda);
2678                                     auto inst_partition_lambda = indices(unique_ids,partns_lambda);
2679                                     auto inst_partition_bounds1 = indices(unique_ids,partns1_lambda);
2680                                     auto inst_partition_bounds2 = indices(unique_ids,partns2_lambda);
2681 
2682                                     // Convex combination variables
2683                                     var<> lambda(name1+name2+"_lambda",pos_);
2684                                     add(lambda.in(inst_partition_lambda));
2685 
2686                                     /** Parameters */
2687                                     // Bounds on variable v1 & v2
2688                                     param<> bounds1(name1+"_bounds1");
2689                                     bounds1.in(inst_partition_bounds1);
2690 
2691                                     param<> bounds2(name2+"_bounds2");
2692                                     bounds2.in(inst_partition_bounds2);
2693 
2694                                     // Function values on the extreme points
2695                                     param<> EP(name1+name2+"_grid_values");
2696                                     EP.in(inst_partition_lambda);
2697                                     auto total_entries = inst_partition_lambda.get_nb_entries();
2698 
2699                                     size_t nb_ins = vlift.in(unique_ids).get_nb_inst();
2700                                     auto o1_global_lb = o1.get_lb();
2701                                     auto increment1 = (o1.get_ub() - o1_global_lb)/num_partns1;
2702 
2703                                     auto o2_global_lb = o2.get_lb();
2704                                     auto increment2 = (o2.get_ub() - o2_global_lb)/num_partns2;
2705 
2706                                     // fill bounds1 and function values
2707                                     for (int i=0 ; i<num_partns1+1; ++i) {
2708                                         auto bound_partn1 = o1_global_lb + increment1*i;
2709                                         bound_partn1.eval_all();
2710                                         for (size_t inst = 0; inst< nb_ins; inst++){
2711                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2712                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2713                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
2714                                             bounds1.set_val(cur_idx,bound_partn1.eval(inst));
2715                                             for(int j=0; j<num_partns2+1; ++j){
2716                                                 auto bound_partn2 = o2_global_lb + increment2*j;
2717                                                 bound_partn2.eval_all();
2718                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"}";
2719                                                 EP.set_val(cur_idx,(bound_partn1.eval(inst)*bound_partn2.eval(inst)));
2720                                             }
2721                                         }
2722                                     }
2723                                     // fill bounds2
2724                                     for (int i=0 ; i<num_partns2+1; ++i) {
2725                                         auto bound_partn2 = o2_global_lb + increment2*i;
2726                                         bound_partn2.eval_all();
2727                                         for (size_t inst = 0; inst< nb_ins; inst++){
2728                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2729                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2730                                             string cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"}";
2731                                             bounds2.set_val(cur_idx,bound_partn2.eval(inst));
2732                                         }
2733                                     }
2734 
2735                                     // Lambda coefficient matrix when linking with partition variables
2736                                     param<> lambda_coef1(name1+"_lambda_linking_coefficients1");
2737                                     param<> lambda_coef2(name2+"_lambda_linking_coefficients2");
2738 
2739                                     // Partition coefficient matrix when linking with lambda variables
2740                                     param<> on_coef1(name1+"_partition_linking_coefficients1");
2741                                     param<> on_coef2(name2+"_partition_linking_coefficients2");
2742 
2743                                     // create constraint indices
2744                                     indices const_idx1("const_idx1");
2745                                     indices const_idx2("const_idx2");
2746 
2747                                     if(model_type == "lambda_II"){
2748 
2749                                         //fill constraint indices
2750                                         for (int i=0; i<num_partns1+1; ++i){
2751                                             const_idx1.add(to_string(i+1));
2752                                         }
2753 
2754                                         //fill constraint indices
2755                                         for (int i=0; i<num_partns2+1; ++i){
2756                                             const_idx2.add(to_string(i+1));
2757                                         }
2758 
2759                                         // Lambda coefficient matrix when linking with partition variables
2760                                         if(num_partns1 > 1) lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
2761                                         if(num_partns2 > 1) lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
2762 
2763                                         // Lambda coefficient matrix when linking with partition variables
2764                                         if(num_partns1 > 1) on_coef1.in(indices(unique_ids, partns1, const_idx1));
2765                                         if(num_partns2 > 1) on_coef2.in(indices(unique_ids, partns2, const_idx2));
2766 
2767                                         // fill lambda_coef1 and lambda_coef2
2768                                         for (size_t inst = 0; inst< nb_ins; inst++){
2769                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2770                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2771                                             for (int i=0 ; i<num_partns1+1; ++i) {
2772                                                 for (int j=0 ; j<num_partns2+1; ++j) {
2773                                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
2774                                                     if(num_partns1 > 1) lambda_coef1.set_val(cur_idx,1);
2775                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(j+1);
2776                                                     if(num_partns2 > 1) lambda_coef2.set_val(cur_idx,1);
2777                                                 }
2778                                             }
2779                                         }
2780 
2781                                         // fill on_coef1 and on_coef2
2782                                         for (size_t inst = 0; inst< nb_ins; inst++){
2783                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2784                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2785                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
2786                                             if(num_partns1 > 1) on_coef1.set_val(cur_idx,1);
2787                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
2788                                             if(num_partns2 > 1) on_coef2.set_val(cur_idx,1);
2789                                             if(num_partns1 > 1) {
2790                                                 for (int i=1 ; i<num_partns1; ++i) {
2791                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i)+"},"+to_string(i+1);
2792                                                     on_coef1.set_val(cur_idx,1);
2793                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
2794                                                     on_coef1.set_val(cur_idx,1);
2795                                                 }
2796                                             }
2797                                             if(num_partns2 > 1) {
2798                                                 for (int i=1 ; i<num_partns2; ++i) {
2799                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(i)+"},"+to_string(i+1);
2800                                                     on_coef2.set_val(cur_idx,1);
2801                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(i+1);
2802                                                     on_coef2.set_val(cur_idx,1);
2803                                                 }
2804                                             }
2805                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1)+"},"+to_string(num_partns1+1);
2806                                             if(num_partns1 > 1) on_coef1.set_val(cur_idx,1);
2807                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(num_partns2)+"},"+to_string(num_partns2+1);
2808                                             if(num_partns2 > 1) on_coef2.set_val(cur_idx,1);
2809                                         }
2810                                     }
2811 
2812 
2813                                     else /*means model_type == "lambda_III" */{
2814 
2815                                         //fill constraint indices
2816                                         for (int i=0; i<(num_partns1-2)*2+2; ++i){
2817                                             const_idx1.add(to_string(i+1));
2818                                         }
2819 
2820                                         //fill constraint indices
2821                                         for (int i=0; i<(num_partns2-2)*2+2; ++i){
2822                                             const_idx2.add(to_string(i+1));
2823                                         }
2824 
2825                                         // Lambda coefficient matrix when linking with partition variables
2826                                         if(num_partns1 > 1) lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
2827                                         if(num_partns2 > 1) lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
2828 
2829                                         // Partition coefficient matrix when linking with lambda variables
2830                                         if(num_partns1 > 1) on_coef1.in(indices(unique_ids, partns1, const_idx1));
2831                                         if(num_partns2 > 1) on_coef2.in(indices(unique_ids, partns2, const_idx2));
2832 
2833                                         // fill lambda_coef1 and lambda_coef2
2834                                         for (size_t inst = 0; inst< nb_ins; inst++){
2835                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2836                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2837                                             if(num_partns1 > 1) {
2838                                                 for (int j=0; j<num_partns2+1; ++j) {
2839                                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(1);
2840                                                     lambda_coef1.set_val(cur_idx,1);
2841                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string((num_partns1-2)*2+2);
2842                                                     lambda_coef1.set_val(cur_idx,1);
2843                                                 }
2844 
2845                                                 for (int i=1 ; i<(num_partns1-2)*2+1; i=i+2) {
2846                                                     for (int j=(i-1)/2 + 2; j<num_partns1+1; ++j) {
2847                                                         for(int k=0; k<num_partns2+1; ++k){
2848                                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+1);
2849                                                             lambda_coef1.set_val(cur_idx,1);
2850                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+2);
2851                                                             lambda_coef1.set_val(cur_idx,-1);
2852                                                         }
2853                                                     }
2854                                                 }
2855                                             }
2856                                             if(num_partns2 > 1) {
2857                                                 for (int i=0; i<num_partns1+1; ++i) {
2858                                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(1)+"},"+to_string(1);
2859                                                     lambda_coef2.set_val(cur_idx,1);
2860                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(num_partns2+1)+"},"+to_string((num_partns2-2)*2+2);
2861                                                     lambda_coef2.set_val(cur_idx,1);
2862                                                 }
2863                                                 for (int i=1 ; i<(num_partns2-2)*2+1; i=i+2) {
2864                                                     for (int j=(i-1)/2 + 2; j<num_partns2+1; ++j) {
2865                                                         for(int k=0; k<num_partns1+1; ++k){
2866                                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
2867                                                             lambda_coef2.set_val(cur_idx,1);
2868                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
2869                                                             lambda_coef2.set_val(cur_idx,-1);
2870                                                         }
2871                                                     }
2872                                                 }
2873                                             }
2874                                         }
2875 
2876 
2877                                         // fill on_coef1 and on_coef2
2878                                         for (size_t inst = 0; inst< nb_ins; inst++){
2879                                             auto cur_var_id = vlift.in(unique_ids).get_id_inst(inst);
2880                                             auto cur_var_idx = unique_ids._keys->at(cur_var_id);
2881                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
2882                                             if(num_partns1 > 1) {
2883                                                 on_coef1.set_val(cur_idx,1);
2884 
2885                                                 for (int i=1; i<num_partns1; ++i) {
2886                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(2);
2887                                                     on_coef1.set_val(cur_idx, 1);
2888                                                 }
2889 
2890                                                 for (int i=2 ; i<(num_partns1-2)*2+2; i=i+2) {
2891                                                     for (int j=i/2+1; j<num_partns1; ++j) {
2892                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
2893                                                         on_coef1.set_val(cur_idx,-1);
2894                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
2895                                                         on_coef1.set_val(cur_idx,1);
2896                                                     }
2897                                                 }
2898                                             }
2899                                             if(num_partns2 > 1) {
2900                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
2901                                                 on_coef2.set_val(cur_idx,1);
2902                                                 for (int i=1; i<num_partns2; ++i) {
2903                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(2);
2904                                                     on_coef2.set_val(cur_idx, 1);
2905                                                 }
2906 
2907                                                 for (int i=2 ; i<(num_partns2-2)*2+2; i=i+2) {
2908                                                     for (int j=i/2+1; j<num_partns2; ++j) {
2909                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
2910                                                         on_coef2.set_val(cur_idx,-1);
2911                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
2912                                                         on_coef2.set_val(cur_idx,1);
2913                                                     }
2914                                                 }
2915                                             }
2916                                         }
2917                                     }
2918 
2919 
2920                                     /** Constraints */
2921                                     // Representation of the bilinear term with convex combination
2922                                     Constraint<> bln_rep(pair.first+"_bln_rep");
2923                                     bln_rep = EP.in_matrix(nb_entries,total_entries-nb_entries)*lambda.in_matrix(nb_entries,total_entries-nb_entries) - vlift.in(unique_ids);
2924                                     add(bln_rep.in(unique_ids) == 0);
2925 
2926                                     // Representation of o1 with convex combination
2927                                     Constraint<> o1_rep(pair.first+"_o1_rep");
2928                                     o1_rep = bounds1.from_ith(0,inst_partition_lambda).in_matrix(nb_entries, 1) * lambda.in_matrix(nb_entries,total_entries-nb_entries) - o1.in(o1_ids);
2929                                     add(o1_rep.in(unique_ids) == 0);
2930 
2931                                     // Representation of o2 with convex combination
2932                                     Constraint<> o2_rep(pair.first+"_o2_rep");
2933                                     o2_rep = bounds2.in_ignore_ith(nb_entries, 1, inst_partition_lambda).in_matrix(nb_entries,1) * lambda.in_matrix(nb_entries,total_entries-nb_entries) - o2.in(o2_ids);
2934                                     add(o2_rep.in(unique_ids) == 0);
2935 
2936                                     // Linking partition variables1 with lambda
2937                                     if(model_type == "lambda_II"){
2938                                         if(num_partns1 > 1) {
2939                                             Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_II");
2940                                             on_link_lambda1 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
2941                                             add(on_link_lambda1.in(indices(unique_ids,const_idx1)) <= 0);
2942                                         }
2943                                         if(num_partns2 > 1) {
2944                                             Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_II");
2945                                             on_link_lambda2 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - on2.in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
2946                                             add(on_link_lambda2.in(indices(unique_ids,const_idx2)) <= 0);
2947                                         }
2948                                     }
2949                                     else{
2950                                         if(num_partns1 > 1) {
2951                                             Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_III");
2952                                             on_link_lambda1 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
2953                                             add(on_link_lambda1.in(indices(unique_ids,const_idx1)) <= 0);
2954                                         }
2955                                         if(num_partns2 > 1) {
2956                                             Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_III");
2957                                             on_link_lambda2 = lambda.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - on2.in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
2958                                             add(on_link_lambda2.in(indices(unique_ids,const_idx2)) <= 0);
2959                                         }
2960                                     }
2961                                     // sum over lambda
2962                                     Constraint<> lambdaSum(pair.first+"_lambdaSum");
2963                                     lambdaSum = sum(lambda.in_matrix(nb_entries,total_entries-nb_entries));
2964                                     add(lambdaSum.in(unique_ids) == 1);
2965                                 }
2966                             }
2967                         }
2968                     }
2969 #endif
2970                 }
2971                 else {
2972                     add_McCormick(pair.first, vlift.in(unique_ids), o1.in(o1_ids), o2.in(o2_ids));
2973                 }
2974             }
2975             else {
2976                 auto vlift = static_pointer_cast<var<type>>(it->second);
2977                 param<T> lb_p("lb_p");
2978                 lb_p = lb;
2979                 param<T> ub_p("ub_p");
2980                 ub_p = ub;
2981                 auto added = vlift->add_bounds(lb_p,ub_p);
2982                 lt._p = make_shared<var<type>>(vlift->in(ids));
2983                 if(!added.empty()){
2984                     vlift->_lb->update_vars();
2985                     vlift->_ub->update_vars();
2986                     assert(o1._indices->size()==o2._indices->size());
2987                     if(added.size()!=o1_ids.size()){/* If some keys are repeated, remove them from the refs of o1 and o2 */
2988                         auto keep_refs = flat_ids.get_diff_refs(added);
2989                         o1_ids.filter_refs(keep_refs);
2990                         o2_ids.filter_refs(keep_refs);
2991                     }
2992                     reindex_vars();
2993                     // If some keys are repeated in individual indices, remove them from the refs of o1 and o2
2994                     auto o1_ids_uq = o1_ids;
2995                     auto o2_ids_uq = o2_ids;
2996                     auto keep_refs1 = o1_ids_uq.get_unique_refs();
2997                     auto keep_refs2 = o2_ids_uq.get_unique_refs();
2998                     o1_ids_uq.filter_refs(keep_refs1);
2999                     o2_ids_uq.filter_refs(keep_refs2);
3000                     reindex_vars();
3001 
3002                     //check the sign of the lift and the correspoinding boudning functions
3003                     if(c.check_soc() && c.is_eq()){
3004                         if(lift_sign){
3005                             vlift->_lift_ub = true;
3006                             vlift->_lift_lb = false;
3007                         }
3008                         else{
3009                             vlift->_lift_ub = false;
3010                             vlift->_lift_lb = true;
3011                         }
3012                     }
3013                     else{
3014                         vlift->_lift_ub = true;
3015                         vlift->_lift_lb = true;
3016                     }
3017                     if((num_partns1 > 1) || (num_partns2 > 1)) {
3018 #ifdef PARTITION
3019                         auto binvar_ptr1 = _vars_name.find(name1+"_binary");
3020                         auto binvar_ptr2 = _vars_name.find(name2+"_binary");
3021                         auto binvar_ptr3 = _vars_name.find(name1+name2+"_binary");
3022 
3023                         if (o1 == o2) //if the variables are same add 1d partition
3024                         {
3025                             if(binvar_ptr1 !=_vars_name.end()){ //means v1 has been partitioned before
3026                                 DebugOn("<<<<<<<<<< THIS IS SEEN LIFT -> SINGLE -> SEEN BINARY <<<<<<<<<<<" << endl);
3027 
3028                                 auto binvar1 = static_pointer_cast<var<int>>(binvar_ptr1->second);
3029 
3030                                 indices partns("partns");
3031                                 for (int i = 0; i < num_partns1 ; ++i)
3032                                 {
3033                                     partns.add(name1+  "{" + to_string(i+1) + "}");
3034                                 }
3035                                 auto inst_partition = indices(added,partns);
3036 
3037                                 param<int> lb1("lb1"), ub1("ub1");
3038                                 lb1.in(added,partns);
3039                                 ub1.in(added,partns);
3040                                 lb1.set_val(0), ub1.set_val(1);
3041                                 auto added1 = binvar1->add_bounds(lb1,ub1);
3042                                 reindex_vars();
3043 
3044                                 auto nb_entries_v1 = o1_ids.get_nb_entries();
3045                                 auto nb_entries = added.get_nb_entries();
3046                                 auto total_entries = inst_partition.get_nb_entries();
3047 
3048                                 Constraint<> onSumComb(pair.first+"_binarySum");
3049                                 onSumComb = sum((binvar1->in(added1)).in_matrix(nb_entries,total_entries-nb_entries));
3050                                 add(onSumComb.in(added) == 1);
3051 
3052                                 if(model_type == "on/off"){//if on/off is chosen
3053                                     add_on_off_McCormick_refined(pair.first, vlift->in(added), o1.in(o1_ids), o2.in(o2_ids), binvar1->in(added1));
3054                                 }
3055 
3056                                 else{ //means it is one of the lambda formulations
3057 
3058                                     //difference is this has one more partition index
3059                                     indices partns_lambda("partns_lambda");
3060                                     for (int i = 0; i < num_partns1+1 ; ++i)
3061                                     {
3062                                         partns_lambda.add(name1+ "{" +to_string(i+1) + "}");
3063                                     }
3064                                     auto inst_partition_lambda = indices(added,partns_lambda);
3065 
3066                                     // Convex combination variables
3067                                     auto lambda_ptr = _vars_name.find(name1+"_lambda");
3068                                     auto lambda = static_pointer_cast<var<double>>(lambda_ptr->second);
3069                                     param<double> lb_lambda("lb_lambda"), ub_lambda("ub_lambda");
3070                                     lb_lambda.in(added,partns_lambda);
3071                                     ub_lambda.in(added,partns_lambda);
3072                                     lb_lambda.set_val(0), ub_lambda.set_val(1);
3073                                     auto added_lambda = lambda->add_bounds(lb_lambda,ub_lambda);
3074                                     reindex_vars();
3075 
3076                                     /** Parameters */
3077                                     // Bounds on variable v1 & v2
3078                                     param<> bounds(name1+"_bounds");
3079                                     bounds.in(inst_partition_lambda);
3080 
3081                                     // Function values on the extreme points
3082                                     param<> EP(name1+name2+"_grid_values");
3083                                     EP.in(inst_partition_lambda);
3084 
3085                                     size_t nb_ins = vlift->in(added).get_nb_inst();
3086                                     auto o1_global_lb = o1.get_lb();
3087                                     auto increment = (o1.get_ub() - o1_global_lb)/num_partns1;
3088 
3089                                     // fill bounds and function values
3090                                     for (int i=0 ; i<num_partns1+1; ++i) {
3091                                         auto bound_partn = o1_global_lb + increment*i;
3092                                         bound_partn.eval_all();
3093                                         for (size_t inst = 0; inst< nb_ins; inst++){
3094                                             auto cur_var_id = vlift->get_id_inst(inst);
3095                                             auto cur_var_idx = added._keys->at(cur_var_id);
3096                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
3097                                             bounds.set_val(cur_idx,bound_partn.eval(inst));
3098                                             EP.set_val(cur_idx,(bound_partn.eval(inst)*bound_partn.eval(inst)));
3099                                         }
3100                                     }
3101 
3102                                     // Lambda coefficient matrix when linking with partition variables
3103                                     param<> lambda_coef(name1+"_lambda_linking_coefficients");
3104 
3105                                     // Partition coefficient matrix when linking with lambda variables
3106                                     param<> on_coef(name1+"_partition_linking_coefficients");
3107 
3108                                     // create constraint indices
3109                                     indices const_idx("const_idx");
3110 
3111                                     if(model_type == "lambda_II"){
3112 
3113                                         //fill constraint indices
3114                                         for (int i=0; i<num_partns1+1; ++i){
3115                                             const_idx.add(to_string(i+1));
3116                                         }
3117 
3118                                         // Lambda coefficient matrix when linking with partition variables
3119                                         lambda_coef.in(indices(inst_partition_lambda, const_idx));
3120 
3121                                         // Partition coefficient matrix when linking with lambda variables
3122                                         on_coef.in(indices(inst_partition, const_idx));
3123 
3124                                         // fill lambda_coef
3125                                         for (size_t inst = 0; inst< nb_ins; inst++){
3126                                             auto cur_var_id = vlift->get_id_inst(inst);
3127                                             auto cur_var_idx = added._keys->at(cur_var_id);
3128                                             for (int i=0 ; i<num_partns1+1; ++i) {
3129                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
3130                                                 lambda_coef.set_val(cur_idx,1);
3131                                             }
3132                                         }
3133 
3134                                         // fill on_coef
3135                                         for (size_t inst = 0; inst< nb_ins; inst++){
3136                                             auto cur_var_id = vlift->get_id_inst(inst);
3137                                             auto cur_var_idx = added._keys->at(cur_var_id);
3138                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
3139                                             on_coef.set_val(cur_idx,1);
3140                                             for (int i=1 ; i<num_partns1; ++i) {
3141                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i)+"},"+to_string(i+1);
3142                                                 on_coef.set_val(cur_idx,1);
3143                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
3144                                                 on_coef.set_val(cur_idx,1);
3145                                             }
3146                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1)+"},"+to_string(num_partns1+1);
3147                                             on_coef.set_val(cur_idx,1);
3148                                         }
3149                                     }
3150 
3151                                     else /*means model_type == "lambda_III" */{
3152 
3153                                         //fill constraint indices
3154                                         for (int i=0; i<(num_partns1-2)*2+2; ++i){
3155                                             const_idx.add(to_string(i+1));
3156                                         }
3157 
3158                                         // Lambda coefficient matrix when linking with partition variables
3159                                         lambda_coef.in(indices(inst_partition_lambda, const_idx));
3160 
3161                                         // Partition coefficient matrix when linking with lambda variables
3162                                         on_coef.in(indices(inst_partition, const_idx));
3163 
3164                                         // fill lambda_coef
3165                                         for (size_t inst = 0; inst< nb_ins; inst++){
3166                                             auto cur_var_id = vlift->get_id_inst(inst);
3167                                             auto cur_var_idx = added._keys->at(cur_var_id);
3168                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
3169                                             lambda_coef.set_val(cur_idx,1);
3170                                             for (int i=1 ; i<(num_partns1-2)*2+1; i=i+2) {
3171                                                 for (int j=(i-1)/2 + 2; j<num_partns1+1; ++j) {
3172                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
3173                                                     lambda_coef.set_val(cur_idx,1);
3174                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
3175                                                     lambda_coef.set_val(cur_idx,-1);
3176                                                 }
3177                                             }
3178                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1+1)+"},"+to_string((num_partns1-2)*2+2);
3179                                             lambda_coef.set_val(cur_idx,1);
3180                                         }
3181 
3182                                         // fill on_coef
3183                                         for (size_t inst = 0; inst< nb_ins; inst++){
3184                                             auto cur_var_id = vlift->get_id_inst(inst);
3185                                             auto cur_var_idx = added._keys->at(cur_var_id);
3186                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
3187                                             on_coef.set_val(cur_idx,1);
3188 
3189                                             for (int i=1; i<num_partns1; ++i) {
3190                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(2);
3191                                                 on_coef.set_val(cur_idx, 1);
3192                                             }
3193 
3194                                             for (int i=2 ; i<(num_partns1-2)*2+2; i=i+2) {
3195                                                 for (int j=i/2+1; j<num_partns1; ++j) {
3196                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
3197                                                     on_coef.set_val(cur_idx,-1);
3198                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
3199                                                     on_coef.set_val(cur_idx,1);
3200                                                 }
3201                                             }
3202                                         }
3203 
3204                                     }
3205 
3206 
3207                                     /** Constraints */
3208 
3209                                     if (vlift->_lift_ub){
3210                                         // Representation of the quadratic term with secant
3211                                         Constraint<> quad_ub(pair.first+"_quad_ub");
3212                                         quad_ub = EP.in_matrix(nb_entries,total_entries-nb_entries)*lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - vlift->in(added);
3213                                         add(quad_ub.in(added) >= 0); /*using it as the upper bound to be valid*/
3214                                     }
3215 
3216                                     if (vlift->_lift_lb){
3217                                         Constraint<> quad_lb(pair.first+"_quad_lb");
3218                                         quad_lb = o1.from_ith(0,added)*o2.from_ith(nb_entries_v1,added) - vlift->in(added);
3219                                         quad_lb._relaxed = true;
3220                                         add(quad_lb.in(added) <= 0); /*using it as the lower bound to be valid*/
3221                                     }
3222 
3223                                     // Representation of o1 with convex combination
3224                                     Constraint<> o1_rep(pair.first+"_o1_rep");
3225                                     o1_rep = bounds.from_ith(0,inst_partition_lambda).in_matrix(nb_entries, 1) * lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - o1.in(o1_ids);
3226                                     add(o1_rep.in(added) == 0);
3227 
3228                                     // Linking partition variables with lambda
3229                                     if(model_type == "lambda_II"){
3230                                         Constraint<> on_link_lambda(pair.first+"_on_link_lambda_II");
3231                                         on_link_lambda = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in(added1).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,on_coef.get_matrix_ids(nb_entries,total_entries-nb_entries)) * on_coef.in_matrix(nb_entries,total_entries-nb_entries);
3232                                         add(on_link_lambda.in(indices(added,const_idx)) <= 0);
3233                                     }
3234                                     else{
3235                                         Constraint<> on_link_lambda(pair.first+"_on_link_lambda_III");
3236                                         on_link_lambda = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in(added1).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,on_coef.get_matrix_ids(nb_entries,total_entries-nb_entries)) * on_coef.in_matrix(nb_entries,total_entries-nb_entries);
3237                                         add(on_link_lambda.in(indices(added,const_idx)) <= 0);
3238                                     }
3239 
3240 
3241                                     // sum over lambda
3242                                     Constraint<> lambdaSum(pair.first+"_lambdaSum");
3243                                     lambdaSum = sum(lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries));
3244                                     add(lambdaSum.in(added) == 1);
3245                                 }
3246                             }
3247                             else{ //means v1 has not been partitioned before
3248                                 DebugOn("<<<<<<<<<< THIS IS SEEN LIFT -> DOUBLE -> NOT SEEN BINARY <<<<<<<<<<<" << endl);
3249 
3250                                 var<int> on(name1+"_binary",0,1);
3251                                 indices partns("partns");
3252                                 for (int i = 0; i < num_partns1 ; ++i)
3253                                 {
3254                                     partns.add(name1+  "{" + to_string(i+1) + "}");
3255                                 }
3256                                 auto inst_partition = indices(added,partns);
3257                                 add(on.in(inst_partition));
3258 
3259                                 auto nb_entries_v1 = o1_ids.get_nb_entries();
3260                                 auto nb_entries = added.get_nb_entries();
3261                                 auto total_entries = inst_partition.get_nb_entries();
3262 
3263                                 Constraint<> onSumComb(pair.first+"_binarySum");
3264                                 onSumComb = sum(on.in_matrix(nb_entries,total_entries-nb_entries));
3265                                 add(onSumComb.in(added) == 1);
3266 
3267                                 if(model_type == "on/off"){//if on/off is chosen
3268                                     add_on_off_McCormick_refined(pair.first, vlift->in(added), o1.in(o1_ids), o2.in(o2_ids), on);
3269                                 }
3270 
3271                                 else{ //means it is one of the lambda formulations
3272 
3273                                     //difference is this has one more partition index
3274                                     indices partns_lambda("partns_lambda");
3275                                     for (int i = 0; i < num_partns1+1 ; ++i)
3276                                     {
3277                                         partns_lambda.add(name1+ "{" +to_string(i+1) + "}");
3278                                     }
3279                                     auto inst_partition_lambda = indices(added,partns_lambda);
3280 
3281                                     // Convex combination variables
3282                                     auto lambda_ptr = _vars_name.find(name1+"_lambda");
3283                                     auto lambda = static_pointer_cast<var<double>>(lambda_ptr->second);
3284                                     param<double> lb_lambda("lb_lambda"), ub_lambda("ub_lambda");
3285                                     lb_lambda.in(added,partns_lambda);
3286                                     ub_lambda.in(added,partns_lambda);
3287                                     lb_lambda.set_val(0), ub_lambda.set_val(1);
3288                                     auto added_lambda = lambda->add_bounds(lb_lambda,ub_lambda);
3289                                     reindex_vars();
3290 
3291                                     /** Parameters */
3292                                     // Bounds on variable v1 & v2
3293                                     param<> bounds(name1+"_bounds");
3294                                     bounds.in(inst_partition_lambda);
3295 
3296                                     // Function values on the extreme points
3297                                     param<> EP(name1+name2+"_grid_values");
3298                                     EP.in(inst_partition_lambda);
3299 
3300                                     size_t nb_ins = vlift->in(added).get_nb_inst();
3301                                     auto o1_global_lb = o1.get_lb();
3302                                     auto increment = (o1.get_ub() - o1_global_lb)/num_partns1;
3303 
3304                                     // fill bounds and function values
3305                                     for (int i=0 ; i<num_partns1+1; ++i) {
3306                                         auto bound_partn = o1_global_lb + increment*i;
3307                                         bound_partn.eval_all();
3308                                         for (size_t inst = 0; inst< nb_ins; inst++){
3309                                             auto cur_var_id = vlift->get_id_inst(inst);
3310                                             auto cur_var_idx = added._keys->at(cur_var_id);
3311                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
3312                                             bounds.set_val(cur_idx,bound_partn.eval(inst));
3313                                             EP.set_val(cur_idx,(bound_partn.eval(inst)*bound_partn.eval(inst)));
3314                                         }
3315                                     }
3316 
3317                                     // Lambda coefficient matrix when linking with partition variables
3318                                     param<> lambda_coef(name1+"_lambda_linking_coefficients");
3319 
3320                                     // Partition coefficient matrix when linking with lambda variables
3321                                     param<> on_coef(name1+"_partition_linking_coefficients");
3322 
3323                                     // create constraint indices
3324                                     indices const_idx("const_idx");
3325 
3326                                     if(model_type == "lambda_II"){
3327 
3328                                         //fill constraint indices
3329                                         for (int i=0; i<num_partns1+1; ++i){
3330                                             const_idx.add(to_string(i+1));
3331                                         }
3332 
3333                                         // Lambda coefficient matrix when linking with partition variables
3334                                         lambda_coef.in(indices(inst_partition_lambda, const_idx));
3335 
3336                                         // Partition coefficient matrix when linking with lambda variables
3337                                         on_coef.in(indices(inst_partition, const_idx));
3338 
3339                                         // fill lambda_coef
3340                                         for (size_t inst = 0; inst< nb_ins; inst++){
3341                                             auto cur_var_id = vlift->get_id_inst(inst);
3342                                             auto cur_var_idx = added._keys->at(cur_var_id);
3343                                             for (int i=0 ; i<num_partns1+1; ++i) {
3344                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
3345                                                 lambda_coef.set_val(cur_idx,1);
3346                                             }
3347                                         }
3348 
3349                                         // fill on_coef
3350                                         for (size_t inst = 0; inst< nb_ins; inst++){
3351                                             auto cur_var_id = vlift->get_id_inst(inst);
3352                                             auto cur_var_idx = added._keys->at(cur_var_id);
3353                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
3354                                             on_coef.set_val(cur_idx,1);
3355                                             for (int i=1 ; i<num_partns1; ++i) {
3356                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i)+"},"+to_string(i+1);
3357                                                 on_coef.set_val(cur_idx,1);
3358                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
3359                                                 on_coef.set_val(cur_idx,1);
3360                                             }
3361                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1)+"},"+to_string(num_partns1+1);
3362                                             on_coef.set_val(cur_idx,1);
3363                                         }
3364                                     }
3365 
3366                                     else /*means model_type == "lambda_III" */{
3367 
3368                                         //fill constraint indices
3369                                         for (int i=0; i<(num_partns1-2)*2+2; ++i){
3370                                             const_idx.add(to_string(i+1));
3371                                         }
3372 
3373                                         // Lambda coefficient matrix when linking with partition variables
3374                                         lambda_coef.in(indices(inst_partition_lambda, const_idx));
3375 
3376                                         // Partition coefficient matrix when linking with lambda variables
3377                                         on_coef.in(indices(inst_partition, const_idx));
3378 
3379                                         // fill lambda_coef
3380                                         for (size_t inst = 0; inst< nb_ins; inst++){
3381                                             auto cur_var_id = vlift->get_id_inst(inst);
3382                                             auto cur_var_idx = added._keys->at(cur_var_id);
3383                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
3384                                             lambda_coef.set_val(cur_idx,1);
3385                                             for (int i=1 ; i<(num_partns1-2)*2+1; i=i+2) {
3386                                                 for (int j=(i-1)/2 + 2; j<num_partns1+1; ++j) {
3387                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
3388                                                     lambda_coef.set_val(cur_idx,1);
3389                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
3390                                                     lambda_coef.set_val(cur_idx,-1);
3391                                                 }
3392                                             }
3393                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1+1)+"},"+to_string((num_partns1-2)*2+2);
3394                                             lambda_coef.set_val(cur_idx,1);
3395                                         }
3396 
3397                                         // fill on_coef
3398                                         for (size_t inst = 0; inst< nb_ins; inst++){
3399                                             auto cur_var_id = vlift->get_id_inst(inst);
3400                                             auto cur_var_idx = added._keys->at(cur_var_id);
3401                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
3402                                             on_coef.set_val(cur_idx,1);
3403 
3404                                             for (int i=1; i<num_partns1; ++i) {
3405                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(2);
3406                                                 on_coef.set_val(cur_idx, 1);
3407                                             }
3408 
3409                                             for (int i=2 ; i<(num_partns1-2)*2+2; i=i+2) {
3410                                                 for (int j=i/2+1; j<num_partns1; ++j) {
3411                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
3412                                                     on_coef.set_val(cur_idx,-1);
3413                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
3414                                                     on_coef.set_val(cur_idx,1);
3415                                                 }
3416                                             }
3417                                         }
3418 
3419                                     }
3420 
3421 
3422                                     /** Constraints */
3423 
3424                                     if (vlift->_lift_ub){
3425                                         // Representation of the quadratic term with secant
3426                                         Constraint<> quad_ub(pair.first+"_quad_ub");
3427                                         quad_ub = EP.in_matrix(nb_entries,total_entries-nb_entries)*lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - vlift->in(added);
3428                                         add(quad_ub.in(added) >= 0); /*using it as the upper bound to be valid*/
3429                                     }
3430 
3431                                     if (vlift->_lift_lb){
3432                                         Constraint<> quad_lb(pair.first+"_quad_lb");
3433                                         quad_lb = o1.from_ith(0,added)*o2.from_ith(nb_entries_v1,added) - vlift->in(added);
3434                                         quad_lb._relaxed = true;
3435                                         add(quad_lb.in(added) <= 0); /*using it as the lower bound to be valid*/
3436                                     }
3437 
3438                                     // Representation of o1 with convex combination
3439                                     Constraint<> o1_rep(pair.first+"_o1_rep");
3440                                     o1_rep = bounds.from_ith(0,inst_partition_lambda).in_matrix(nb_entries, 1) * lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - o1.in(o1_ids);
3441                                     add(o1_rep.in(added) == 0);
3442 
3443                                     // Linking partition variables with lambda
3444                                     if(model_type == "lambda_II"){
3445                                         Constraint<> on_link_lambda(pair.first+"_on_link_lambda_II");
3446                                         on_link_lambda = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef.in_matrix(nb_entries,total_entries-nb_entries) - on.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,on_coef.get_matrix_ids(nb_entries,total_entries-nb_entries)) * on_coef.in_matrix(nb_entries,total_entries-nb_entries);
3447                                         add(on_link_lambda.in(indices(added,const_idx)) <= 0);
3448                                     }
3449                                     else{
3450                                         Constraint<> on_link_lambda(pair.first+"_on_link_lambda_III");
3451                                         on_link_lambda = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef.in_matrix(nb_entries,total_entries-nb_entries) - on.in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,on_coef.get_matrix_ids(nb_entries,total_entries-nb_entries)) * on_coef.in_matrix(nb_entries,total_entries-nb_entries);
3452                                         add(on_link_lambda.in(indices(added,const_idx)) <= 0);
3453                                     }
3454 
3455 
3456                                     // sum over lambda
3457                                     Constraint<> lambdaSum(pair.first+"_lambdaSum");
3458                                     lambdaSum = sum(lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries));
3459                                     add(lambdaSum.in(added) == 1);
3460                                 }
3461                             }
3462                         }
3463                         else{ //else add 2d partition
3464                             if(binvar_ptr1 !=_vars_name.end() && binvar_ptr2 !=_vars_name.end()){ //means v1 and v2 has been partitioned before
3465                                 if(name1 == name2){
3466                                     DebugOn("<<<<<<<<<< THIS IS SEEN LIFT -> DOUBLE -> SEEN BOTH BINARIES -> SAME CORE VARS <<<<<<<<<<<" << endl);
3467 
3468                                     indices partns1("partns1");
3469                                     for (int i = 0; i < num_partns1 ; ++i)
3470                                     {
3471                                         partns1.add(name1+ "{" + to_string(i+1) + "}");
3472                                     }
3473 
3474                                     auto binvar1 = static_pointer_cast<var<int>>(binvar_ptr1->second);
3475                                     param<int> lb1("lb1"), ub1("ub1");
3476                                     lb1.in(union_ids(o1_ids_uq,o2_ids_uq),partns1);
3477                                     ub1.in(union_ids(o1_ids_uq,o2_ids_uq),partns1);
3478                                     lb1.set_val(0), ub1.set_val(1);
3479                                     auto added1 = binvar1->add_bounds(lb1,ub1);
3480                                     reindex_vars();
3481 
3482                                     indices partns2("partns2");
3483                                     for (int i = 0; i < num_partns2 ; ++i)
3484                                     {
3485                                         partns2.add(name2+ "{" + to_string(i+1) + "}");
3486                                     }
3487 
3488                                     auto nb_entries_v1 = o1_ids.get_nb_entries();
3489                                     auto nb_entries_v2 = o2_ids.get_nb_entries();
3490                                     auto nb_entries = added.get_nb_entries();
3491 
3492                                     if(!added1.empty()){
3493                                         Constraint<> onSum1(o1._name+"_binarySum");
3494                                         onSum1 = sum(binvar1->in(added1).in_matrix(nb_entries_v1,1));
3495                                         auto vset1 = added1.from_ith(0,nb_entries_v1);
3496                                         vset1.filter_refs(vset1.get_unique_refs());
3497                                         add(onSum1.in(vset1) == 1);
3498                                     }
3499 
3500                                     if(model_type == "on/off"){//if on/off is chosen
3501 
3502                                         indices partns("partns");
3503                                         partns = indices(partns1,partns2);
3504                                         auto inst_partition = indices(added,partns);
3505                                         auto total_entries = inst_partition.get_nb_entries();
3506 
3507                                         if(binvar_ptr3 !=_vars_name.end()){ //means the combined binary variable has been used before
3508                                             auto binvar3 = static_pointer_cast<var<int>>(binvar_ptr3->second);
3509                                             param<int> lb3("lb3"), ub3("ub3");
3510                                             lb3.in(added,partns);
3511                                             ub3.in(added,partns);
3512                                             lb3.set_val(0), ub3.set_val(1);
3513                                             auto added3 = binvar3->add_bounds(lb3,ub3);
3514                                             reindex_vars();
3515 
3516                                             Constraint<> onLink1(pair.first+"_binaryLink1");
3517                                             onLink1 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v1)) - binvar3->in(inst_partition);
3518                                             add(onLink1.in(inst_partition) >= 0);
3519 
3520                                             Constraint<> onLink2(pair.first+"_binaryLink2");
3521                                             onLink2 = binvar1->in_ignore_ith(nb_entries_v1,1,inst_partition.ignore_ith(0,nb_entries_v1)) - binvar3->in(inst_partition);
3522                                             add(onLink2.in(inst_partition) >= 0);
3523 
3524                                             Constraint<> onLink3(pair.first+"_binaryLink3");
3525                                             onLink3 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v1)) + binvar1->in_ignore_ith(nb_entries_v1,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - binvar3->in(inst_partition);
3526                                             add(onLink3.in(inst_partition) <= 0);
3527 
3528                                             Constraint<> onSumComb(pair.first+"_binarySum");
3529                                             onSumComb = sum((binvar3->in(added3)).in_matrix(nb_entries,total_entries-nb_entries));
3530                                             add(onSumComb.in(added) == 1);
3531 
3532                                             add_on_off_McCormick_refined(pair.first, vlift->in(added), o1.in(o1_ids), o2.in(o2_ids), binvar3->in(added3));
3533                                         }
3534                                         else{ // means the combined binary variable has not been used before
3535 
3536                                             var<int> on(name1+name2+"_binary",0,1);
3537                                             add(on.in(inst_partition));
3538 
3539                                             Constraint<> onLink1(pair.first+"_binaryLink1");
3540                                             onLink1 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v1)) - on;
3541                                             add(onLink1.in(inst_partition) >= 0);
3542 
3543                                             Constraint<> onLink2(pair.first+"_binaryLink2");
3544                                             onLink2 = binvar1->in_ignore_ith(nb_entries_v1,1,inst_partition.ignore_ith(0,nb_entries_v1)) - on;
3545                                             add(onLink2.in(inst_partition) >= 0);
3546 
3547                                             Constraint<> onLink3(pair.first+"_binaryLink3");
3548                                             onLink3 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v1)) + binvar1->in_ignore_ith(nb_entries_v1,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - on;
3549                                             add(onLink3.in(inst_partition) <= 0);
3550 
3551                                             Constraint<> onSumComb(pair.first+"_binarySum");
3552                                             onSumComb = sum(on.in_matrix(nb_entries,total_entries-nb_entries));
3553                                             add(onSumComb.in(added) == 1);
3554 
3555                                             add_on_off_McCormick_refined(pair.first, vlift->in(added), o1.in(o1_ids), o2.in(o2_ids), on);
3556                                         }
3557                                     }
3558 
3559                                     else{//means it is one of the lambda formulations
3560 
3561                                         //difference is this has one more partition index
3562                                         indices partns1_lambda("partns1_lambda");
3563                                         for (int i = 0; i < num_partns1+1; ++i)
3564                                         {
3565                                             partns1_lambda.add(name1+ "{" +to_string(i+1) + "}");
3566                                         }
3567 
3568                                         indices partns2_lambda("partns2_lambda");
3569                                         for (int i = 0; i < num_partns2+1; ++i)
3570                                         {
3571                                             partns2_lambda.add(name2+ "{" +to_string(i+1) + "}");
3572                                         }
3573 
3574                                         indices partns_lambda("partns_lambda");
3575                                         partns_lambda = indices(partns1_lambda,partns2_lambda);
3576                                         auto inst_partition_lambda = indices(added,partns_lambda);
3577                                         auto inst_partition_bounds1 = indices(added,partns1_lambda);
3578                                         auto inst_partition_bounds2 = indices(added,partns2_lambda);
3579 
3580                                         // Convex combination variables
3581                                         auto lambda_ptr = _vars_name.find(name1+name2+"_lambda");
3582                                         auto lambda = static_pointer_cast<var<double>>(lambda_ptr->second);
3583                                         param<double> lb_lambda("lb_lambda"), ub_lambda("ub_lambda");
3584                                         lb_lambda.in(added,partns_lambda);
3585                                         ub_lambda.in(added,partns_lambda);
3586                                         lb_lambda.set_val(0), ub_lambda.set_val(1);
3587                                         auto added_lambda = lambda->add_bounds(lb_lambda,ub_lambda);
3588                                         reindex_vars();
3589 
3590                                         /** Parameters */
3591                                         // Bounds on variable v1 & v2
3592                                         param<> bounds1(name1+"_bounds1");
3593                                         bounds1.in(inst_partition_bounds1);
3594 
3595                                         param<> bounds2(name2+"_bounds2");
3596                                         bounds2.in(inst_partition_bounds2);
3597 
3598                                         // Function values on the extreme points
3599                                         param<> EP(name1+name2+"_grid_values");
3600                                         EP.in(inst_partition_lambda);
3601                                         auto total_entries = inst_partition_lambda.get_nb_entries();
3602 
3603                                         size_t nb_ins = vlift->in(added).get_nb_inst();
3604                                         auto o1_global_lb = o1.get_lb();
3605                                         auto increment1 = (o1.get_ub() - o1_global_lb)/num_partns1;
3606 
3607                                         auto o2_global_lb = o2.get_lb();
3608                                         auto increment2 = (o2.get_ub() - o2_global_lb)/num_partns2;
3609 
3610                                         // fill bounds and function values
3611                                         for (int i=0 ; i<num_partns1+1; ++i) {
3612                                             auto bound_partn1 = o1_global_lb + increment1*i;
3613                                             bound_partn1.eval_all();
3614                                             auto bound_partn2 = o2_global_lb + increment2*i;
3615                                             bound_partn2.eval_all();
3616                                             for (size_t inst = 0; inst< nb_ins; inst++){
3617                                                 auto cur_var_id = vlift->get_id_inst(inst);
3618                                                 auto cur_var_idx = added._keys->at(cur_var_id);
3619                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
3620                                                 bounds1.set_val(cur_idx,bound_partn1.eval(inst));
3621                                                 bounds2.set_val(cur_idx,bound_partn2.eval(inst));
3622                                                 for(int j=0; j<num_partns2+1; ++j){
3623                                                     auto bound_partn2_temp = o2_global_lb + increment2*j;
3624                                                     bound_partn2_temp.eval_all();
3625                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"}";
3626                                                     EP.set_val(cur_idx,(bound_partn1.eval(inst)*bound_partn2_temp.eval(inst)));
3627                                                 }
3628                                             }
3629                                         }
3630 
3631                                         // Lambda coefficient matrix when linking with partition variables
3632                                         param<> lambda_coef1(name1+"_lambda_linking_coefficients1");
3633                                         param<> lambda_coef2(name2+"_lambda_linking_coefficients2");
3634 
3635                                         // Partition coefficient matrix when linking with lambda variables
3636                                         param<> on_coef1(name1+"_partition_linking_coefficients1");
3637                                         param<> on_coef2(name2+"_partition_linking_coefficients2");
3638 
3639                                         // create constraint indices
3640                                         indices const_idx1("const_idx1");
3641                                         indices const_idx2("const_idx2");
3642 
3643                                         if(model_type == "lambda_II"){
3644 
3645                                             //fill constraint indices
3646                                             for (int i=0; i<num_partns1+1; ++i){
3647                                                 const_idx1.add(to_string(i+1));
3648                                             }
3649 
3650                                             //fill constraint indices
3651                                             for (int i=0; i<num_partns2+1; ++i){
3652                                                 const_idx2.add(to_string(i+1));
3653                                             }
3654 
3655                                             // Lambda coefficient matrix when linking with partition variables
3656                                             lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
3657                                             lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
3658 
3659                                             // Lambda coefficient matrix when linking with partition variables
3660                                             on_coef1.in(indices(added, partns1, const_idx1));
3661                                             on_coef2.in(indices(added, partns2, const_idx2));
3662 
3663                                             // fill lambda_coef1 and lambda_coef2
3664                                             for (size_t inst = 0; inst< nb_ins; inst++){
3665                                                 auto cur_var_id = vlift->get_id_inst(inst);
3666                                                 auto cur_var_idx = added._keys->at(cur_var_id);
3667                                                 for (int i=0 ; i<num_partns1+1; ++i) {
3668                                                     for (int j=0 ; j<num_partns2+1; ++j) {
3669                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
3670                                                         lambda_coef1.set_val(cur_idx,1);
3671                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(j+1);
3672                                                         lambda_coef2.set_val(cur_idx,1);
3673                                                     }
3674                                                 }
3675                                             }
3676 
3677                                             // fill on_coef1 and on_coef2
3678                                             for (size_t inst = 0; inst< nb_ins; inst++){
3679                                                 auto cur_var_id = vlift->get_id_inst(inst);
3680                                                 auto cur_var_idx = added._keys->at(cur_var_id);
3681                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
3682                                                 on_coef1.set_val(cur_idx,1);
3683                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
3684                                                 on_coef2.set_val(cur_idx,1);
3685                                                 for (int i=1 ; i<num_partns1; ++i) {
3686                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i)+"},"+to_string(i+1);
3687                                                     on_coef1.set_val(cur_idx,1);
3688                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
3689                                                     on_coef1.set_val(cur_idx,1);
3690                                                 }
3691                                                 for (int i=1 ; i<num_partns2; ++i) {
3692                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(i)+"},"+to_string(i+1);
3693                                                     on_coef2.set_val(cur_idx,1);
3694                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(i+1);
3695                                                     on_coef2.set_val(cur_idx,1);
3696                                                 }
3697                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1)+"},"+to_string(num_partns1+1);
3698                                                 on_coef1.set_val(cur_idx,1);
3699                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(num_partns2)+"},"+to_string(num_partns2+1);
3700                                                 on_coef2.set_val(cur_idx,1);
3701                                             }
3702                                         }
3703 
3704 
3705                                         else /*means model_type == "lambda_III" */{
3706 
3707                                             //fill constraint indices
3708                                             for (int i=0; i<(num_partns1-2)*2+2; ++i){
3709                                                 const_idx1.add(to_string(i+1));
3710                                             }
3711 
3712                                             //fill constraint indices
3713                                             for (int i=0; i<(num_partns2-2)*2+2; ++i){
3714                                                 const_idx2.add(to_string(i+1));
3715                                             }
3716 
3717                                             // Lambda coefficient matrix when linking with partition variables
3718                                             lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
3719                                             lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
3720 
3721                                             // Partition coefficient matrix when linking with lambda variables
3722                                             on_coef1.in(indices(added, partns1, const_idx1));
3723                                             on_coef2.in(indices(added, partns2, const_idx2));
3724 
3725                                             // fill lambda_coef1 and lambda_coef2
3726                                             for (size_t inst = 0; inst< nb_ins; inst++){
3727                                                 auto cur_var_id = vlift->get_id_inst(inst);
3728                                                 auto cur_var_idx = added._keys->at(cur_var_id);
3729                                                 for (int j=0; j<num_partns2+1; ++j) {
3730                                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(1);
3731                                                     lambda_coef1.set_val(cur_idx,1);
3732                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string((num_partns1-2)*2+2);
3733                                                     lambda_coef1.set_val(cur_idx,1);
3734                                                 }
3735 
3736                                                 for (int i=1 ; i<(num_partns1-2)*2+1; i=i+2) {
3737                                                     for (int j=(i-1)/2 + 2; j<num_partns1+1; ++j) {
3738                                                         for(int k=0; k<num_partns2+1; ++k){
3739                                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+1);
3740                                                             lambda_coef1.set_val(cur_idx,1);
3741                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+2);
3742                                                             lambda_coef1.set_val(cur_idx,-1);
3743                                                         }
3744                                                     }
3745                                                 }
3746 
3747                                                 for (int i=0; i<num_partns1+1; ++i) {
3748                                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(1)+"},"+to_string(1);
3749                                                     lambda_coef2.set_val(cur_idx,1);
3750                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(num_partns2+1)+"},"+to_string((num_partns2-2)*2+2);
3751                                                     lambda_coef2.set_val(cur_idx,1);
3752                                                 }
3753 
3754                                                 for (int i=1 ; i<(num_partns2-2)*2+1; i=i+2) {
3755                                                     for (int j=(i-1)/2 + 2; j<num_partns2+1; ++j) {
3756                                                         for(int k=0; k<num_partns1+1; ++k){
3757                                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
3758                                                             lambda_coef2.set_val(cur_idx,1);
3759                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
3760                                                             lambda_coef2.set_val(cur_idx,-1);
3761                                                         }
3762                                                     }
3763                                                 }
3764                                             }
3765 
3766 
3767 
3768                                             // fill on_coef1 and on_coef2
3769                                             for (size_t inst = 0; inst< nb_ins; inst++){
3770                                                 auto cur_var_id = vlift->get_id_inst(inst);
3771                                                 auto cur_var_idx = added._keys->at(cur_var_id);
3772                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
3773                                                 on_coef1.set_val(cur_idx,1);
3774 
3775                                                 for (int i=1; i<num_partns1; ++i) {
3776                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(2);
3777                                                     on_coef1.set_val(cur_idx, 1);
3778                                                 }
3779 
3780                                                 for (int i=2 ; i<(num_partns1-2)*2+2; i=i+2) {
3781                                                     for (int j=i/2+1; j<num_partns1; ++j) {
3782                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
3783                                                         on_coef1.set_val(cur_idx,-1);
3784                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
3785                                                         on_coef1.set_val(cur_idx,1);
3786                                                     }
3787                                                 }
3788 
3789                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
3790                                                 on_coef2.set_val(cur_idx,1);
3791                                                 for (int i=1; i<num_partns2; ++i) {
3792                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(2);
3793                                                     on_coef2.set_val(cur_idx, 1);
3794                                                 }
3795 
3796                                                 for (int i=2 ; i<(num_partns2-2)*2+2; i=i+2) {
3797                                                     for (int j=i/2+1; j<num_partns2; ++j) {
3798                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
3799                                                         on_coef2.set_val(cur_idx,-1);
3800                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
3801                                                         on_coef2.set_val(cur_idx,1);
3802                                                     }
3803                                                 }
3804                                             }
3805                                         }
3806 
3807 
3808                                         /** Constraints */
3809                                         // Representation of the bilinear term with convex combination
3810                                         Constraint<> bln_rep(pair.first+"_bln_rep");
3811                                         bln_rep = EP.in_matrix(nb_entries,total_entries-nb_entries)*lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - vlift->in(added);
3812                                         add(bln_rep.in(added) == 0);
3813 
3814                                         // Representation of o1 with convex combination
3815                                         Constraint<> o1_rep(pair.first+"_o1_rep");
3816                                         o1_rep = bounds1.from_ith(0,inst_partition_lambda).in_matrix(nb_entries, 1) * lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - o1.in(o1_ids);
3817                                         add(o1_rep.in(added) == 0);
3818 
3819                                         // Representation of o2 with convex combination
3820                                         Constraint<> o2_rep(pair.first+"_o2_rep");
3821                                         o2_rep = bounds2.in_ignore_ith(nb_entries, 1, inst_partition_lambda).in_matrix(nb_entries,1) * lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - o2.in(o2_ids);
3822                                         add(o2_rep.in(added) == 0);
3823 
3824                                         // Linking partition variables1 with lambda
3825                                         if(model_type == "lambda_II"){
3826                                             Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_II");
3827                                             on_link_lambda1 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
3828                                             add(on_link_lambda1.in(indices(added,const_idx1)) <= 0);
3829 
3830                                             Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_II");
3831                                             on_link_lambda2 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
3832                                             add(on_link_lambda2.in(indices(added,const_idx2)) <= 0);
3833                                         }
3834                                         else{
3835                                             Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_III");
3836                                             on_link_lambda1 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
3837                                             add(on_link_lambda1.in(indices(added,const_idx1)) <= 0);
3838 
3839                                             Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_III");
3840                                             on_link_lambda2 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
3841                                             add(on_link_lambda2.in(indices(added,const_idx2)) <= 0);
3842                                         }
3843                                         // sum over lambda
3844                                         Constraint<> lambdaSum(pair.first+"_lambdaSum");
3845                                         lambdaSum = sum(lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries));
3846                                         add(lambdaSum.in(added) == 1);
3847                                     }
3848 
3849                                 }
3850                                 else{
3851                                     DebugOn("<<<<<<<<<< THIS IS SEEN LIFT -> DOUBLE -> SEEN BOTH BINARIES -> DIFF CORE VARS <<<<<<<<<<<" << endl);
3852 
3853                                     auto binvar1 = static_pointer_cast<var<int>>(binvar_ptr1->second);
3854                                     indices partns1("partns1");
3855                                     for (int i = 0; i < num_partns1 ; ++i)
3856                                     {
3857                                         partns1.add(name1+ "{" +to_string(i+1) + "}");
3858                                     }
3859                                     param<int> lb1("lb1"), ub1("ub1");
3860                                     lb1.in(o1_ids_uq,partns1);
3861                                     ub1.in(o1_ids_uq,partns1);
3862                                     lb1.set_val(0), ub1.set_val(1);
3863                                     auto added1 = binvar1->add_bounds(lb1,ub1);
3864                                     reindex_vars();
3865 
3866                                     auto binvar2 = static_pointer_cast<var<int>>(binvar_ptr2->second);
3867                                     indices partns2("partns2");
3868                                     for (int i = 0; i < num_partns2 ; ++i)
3869                                     {
3870                                         partns2.add(name2+ "{" + to_string(i+1) + "}");
3871                                     }
3872                                     param<int> lb2("lb2"), ub2("ub2");
3873                                     lb2.in(o2_ids_uq,partns2);
3874                                     ub2.in(o2_ids_uq,partns2);
3875                                     lb2.set_val(0), ub2.set_val(1);
3876                                     auto added2 = binvar2->add_bounds(lb2,ub2);
3877                                     reindex_vars();
3878 
3879                                     auto nb_entries_v1 = o1_ids.get_nb_entries();
3880                                     auto nb_entries_v2 = o2_ids.get_nb_entries();
3881                                     auto nb_entries = added.get_nb_entries();
3882 
3883                                     if(!added1.empty()){
3884                                         Constraint<> onSum1(o1._name+"_binarySum");
3885                                         onSum1 = sum(binvar1->in(added1).in_matrix(nb_entries_v1,1));
3886                                         auto vset1 = added1.from_ith(0,nb_entries_v1);
3887                                         vset1.filter_refs(vset1.get_unique_refs());
3888                                         add(onSum1.in(vset1) == 1);
3889                                     }
3890 
3891                                     if(!added2.empty()){
3892                                         Constraint<> onSum2(o2._name+"_binarySum");
3893                                         onSum2 = sum(binvar2->in(added2).in_matrix(nb_entries_v2,1));
3894                                         auto vset2 = added2.from_ith(0,nb_entries_v2);
3895                                         vset2.filter_refs(vset2.get_unique_refs());
3896                                         add(onSum2.in(vset2) == 1);
3897                                     }
3898 
3899                                     if(model_type == "on/off"){//if on/off is chosen
3900 
3901                                         indices partns("partns");
3902                                         partns = indices(partns1,partns2);
3903                                         auto inst_partition = indices(added,partns);
3904                                         auto total_entries = inst_partition.get_nb_entries();
3905 
3906                                         if(binvar_ptr3 !=_vars_name.end()){ //means the combined binary variable has been used before
3907                                             auto binvar3 = static_pointer_cast<var<int>>(binvar_ptr3->second);
3908                                             param<int> lb3("lb3"), ub3("ub3");
3909                                             lb3.in(added,partns);
3910                                             ub3.in(added,partns);
3911                                             lb3.set_val(0), ub3.set_val(1);
3912                                             auto added3 = binvar3->add_bounds(lb3,ub3);
3913                                             reindex_vars();
3914 
3915                                             Constraint<> onLink1(pair.first+"_binaryLink1");
3916                                             onLink1 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) - binvar3->in(inst_partition);
3917                                             add(onLink1.in(inst_partition) >= 0);
3918 
3919                                             Constraint<> onLink2(pair.first+"_binaryLink2");
3920                                             onLink2 = binvar2->in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - binvar3->in(inst_partition);
3921                                             add(onLink2.in(inst_partition) >= 0);
3922 
3923                                             Constraint<> onLink3(pair.first+"_binaryLink3");
3924                                             onLink3 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) + binvar2->in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - binvar3->in(inst_partition);
3925                                             add(onLink3.in(inst_partition) <= 0);
3926 
3927                                             Constraint<> onSumComb(pair.first+"_binarySum");
3928                                             onSumComb = sum((binvar3->in(added3)).in_matrix(nb_entries,total_entries-nb_entries));
3929                                             add(onSumComb.in(added) == 1);
3930 
3931                                             add_on_off_McCormick_refined(pair.first, vlift->in(added), o1.in(o1_ids), o2.in(o2_ids), binvar3->in(added3));
3932                                         }
3933                                         else{ //means the combined binary variable has not been used before
3934                                             var<int> on(name1+name2+"_binary",0,1);
3935                                             add(on.in(inst_partition));
3936 
3937                                             Constraint<> onLink1(pair.first+"_binaryLink1");
3938                                             onLink1 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) - on;
3939                                             add(onLink1.in(inst_partition) >= 0);
3940 
3941                                             Constraint<> onLink2(pair.first+"_binaryLink2");
3942                                             onLink2 = binvar2->in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - on;
3943                                             add(onLink2.in(inst_partition) >= 0);
3944 
3945                                             Constraint<> onLink3(pair.first+"_binaryLink3");
3946                                             onLink3 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) + binvar2->in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - on;
3947                                             add(onLink3.in(inst_partition) <= 0);
3948 
3949                                             Constraint<> onSumComb(pair.first+"_binarySum");
3950                                             onSumComb = sum(on.in_matrix(nb_entries,total_entries-nb_entries));
3951                                             add(onSumComb.in(added) == 1);
3952 
3953                                             add_on_off_McCormick_refined(pair.first, vlift->in(added), o1.in(o1_ids), o2.in(o2_ids), on);
3954                                         }
3955                                     }
3956 
3957                                     else{//means it is one of the lambda formulations
3958 
3959                                         //difference is this has one more partition index
3960                                         indices partns1_lambda("partns1_lambda");
3961                                         for (int i = 0; i < num_partns1+1; ++i)
3962                                         {
3963                                             partns1_lambda.add(name1+ "{" +to_string(i+1) + "}");
3964                                         }
3965 
3966                                         indices partns2_lambda("partns2_lambda");
3967                                         for (int i = 0; i < num_partns2+1; ++i)
3968                                         {
3969                                             partns2_lambda.add(name2+ "{" +to_string(i+1) + "}");
3970                                         }
3971 
3972                                         indices partns_lambda("partns_lambda");
3973                                         partns_lambda = indices(partns1_lambda,partns2_lambda);
3974                                         auto inst_partition_lambda = indices(added,partns_lambda);
3975                                         auto inst_partition_bounds1 = indices(added,partns1_lambda);
3976                                         auto inst_partition_bounds2 = indices(added,partns2_lambda);
3977 
3978                                         // Convex combination variables
3979                                         auto lambda_ptr = _vars_name.find(name1+name2+"_lambda");
3980                                         auto lambda = static_pointer_cast<var<double>>(lambda_ptr->second);
3981                                         param<double> lb_lambda("lb_lambda"), ub_lambda("ub_lambda");
3982                                         lb_lambda.in(added,partns_lambda);
3983                                         ub_lambda.in(added,partns_lambda);
3984                                         lb_lambda.set_val(0), ub_lambda.set_val(1);
3985                                         auto added_lambda = lambda->add_bounds(lb_lambda,ub_lambda);
3986                                         reindex_vars();
3987 
3988                                         /** Parameters */
3989                                         // Bounds on variable v1 & v2
3990                                         param<> bounds1(name1+"_bounds1");
3991                                         bounds1.in(inst_partition_bounds1);
3992 
3993                                         param<> bounds2(name2+"_bounds2");
3994                                         bounds2.in(inst_partition_bounds2);
3995 
3996                                         // Function values on the extreme points
3997                                         param<> EP(name1+name2+"_grid_values");
3998                                         EP.in(inst_partition_lambda);
3999                                         auto total_entries = inst_partition_lambda.get_nb_entries();
4000 
4001                                         size_t nb_ins = vlift->in(added).get_nb_inst();
4002                                         auto o1_global_lb = o1.get_lb();
4003                                         auto increment1 = (o1.get_ub() - o1_global_lb)/num_partns1;
4004 
4005                                         auto o2_global_lb = o2.get_lb();
4006                                         auto increment2 = (o2.get_ub() - o2_global_lb)/num_partns2;
4007 
4008                                         // fill bounds1 and function values
4009                                         for (int i=0 ; i<num_partns1+1; ++i) {
4010                                             auto bound_partn1 = o1_global_lb + increment1*i;
4011                                             bound_partn1.eval_all();
4012                                             for (size_t inst = 0; inst< nb_ins; inst++){
4013                                                 auto cur_var_id = vlift->get_id_inst(inst);
4014                                                 auto cur_var_idx = added._keys->at(cur_var_id);
4015                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
4016                                                 bounds1.set_val(cur_idx,bound_partn1.eval(inst));
4017                                                 for(int j=0; j<num_partns2+1; ++j){
4018                                                     auto bound_partn2 = o2_global_lb + increment2*j;
4019                                                     bound_partn2.eval_all();
4020                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"}";
4021                                                     EP.set_val(cur_idx,(bound_partn1.eval(inst)*bound_partn2.eval(inst)));
4022                                                 }
4023                                             }
4024                                         }
4025                                         // fill bounds2
4026                                         for (int i=0 ; i<num_partns2+1; ++i) {
4027                                             auto bound_partn2 = o2_global_lb + increment2*i;
4028                                             bound_partn2.eval_all();
4029                                             for (size_t inst = 0; inst< nb_ins; inst++){
4030                                                 auto cur_var_id = vlift->get_id_inst(inst);
4031                                                 auto cur_var_idx = added._keys->at(cur_var_id);
4032                                                 string cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"}";
4033                                                 bounds2.set_val(cur_idx,bound_partn2.eval(inst));
4034                                             }
4035                                         }
4036 
4037                                         // Lambda coefficient matrix when linking with partition variables
4038                                         param<> lambda_coef1(name1+"_lambda_linking_coefficients1");
4039                                         param<> lambda_coef2(name2+"_lambda_linking_coefficients2");
4040 
4041                                         // Partition coefficient matrix when linking with lambda variables
4042                                         param<> on_coef1(name1+"_partition_linking_coefficients1");
4043                                         param<> on_coef2(name2+"_partition_linking_coefficients2");
4044 
4045                                         // create constraint indices
4046                                         indices const_idx1("const_idx1");
4047                                         indices const_idx2("const_idx2");
4048 
4049                                         if(model_type == "lambda_II"){
4050 
4051                                             //fill constraint indices
4052                                             for (int i=0; i<num_partns1+1; ++i){
4053                                                 const_idx1.add(to_string(i+1));
4054                                             }
4055 
4056                                             //fill constraint indices
4057                                             for (int i=0; i<num_partns2+1; ++i){
4058                                                 const_idx2.add(to_string(i+1));
4059                                             }
4060 
4061                                             // Lambda coefficient matrix when linking with partition variables
4062                                             if(num_partns1 > 1) lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
4063                                             if(num_partns2 > 1) lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
4064 
4065                                             // Lambda coefficient matrix when linking with partition variables
4066                                             if(num_partns1 > 1) on_coef1.in(indices(added, partns1, const_idx1));
4067                                             if(num_partns2 > 1) on_coef2.in(indices(added, partns2, const_idx2));
4068 
4069                                             // fill lambda_coef1 and lambda_coef2
4070                                             for (size_t inst = 0; inst< nb_ins; inst++){
4071                                                 auto cur_var_id = vlift->get_id_inst(inst);
4072                                                 auto cur_var_idx = added._keys->at(cur_var_id);
4073                                                 for (int i=0 ; i<num_partns1+1; ++i) {
4074                                                     for (int j=0 ; j<num_partns2+1; ++j) {
4075                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
4076                                                         if(num_partns1 > 1) lambda_coef1.set_val(cur_idx,1);
4077                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(j+1);
4078                                                         if(num_partns2 > 1) lambda_coef2.set_val(cur_idx,1);
4079                                                     }
4080                                                 }
4081                                             }
4082 
4083                                             // fill on_coef1 and on_coef2
4084                                             for (size_t inst = 0; inst< nb_ins; inst++){
4085                                                 auto cur_var_id = vlift->get_id_inst(inst);
4086                                                 auto cur_var_idx = added._keys->at(cur_var_id);
4087                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
4088                                                 if(num_partns1 > 1) on_coef1.set_val(cur_idx,1);
4089                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
4090                                                 if(num_partns2 > 1) on_coef2.set_val(cur_idx,1);
4091                                                 if(num_partns1 > 1) {
4092                                                     for (int i=1 ; i<num_partns1; ++i) {
4093                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i)+"},"+to_string(i+1);
4094                                                         on_coef1.set_val(cur_idx,1);
4095                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
4096                                                         on_coef1.set_val(cur_idx,1);
4097                                                     }
4098                                                 }
4099                                                 if(num_partns2 > 1) {
4100                                                     for (int i=1 ; i<num_partns2; ++i) {
4101                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(i)+"},"+to_string(i+1);
4102                                                         on_coef2.set_val(cur_idx,1);
4103                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(i+1);
4104                                                         on_coef2.set_val(cur_idx,1);
4105                                                     }
4106                                                 }
4107                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1)+"},"+to_string(num_partns1+1);
4108                                                 if(num_partns1 > 1) on_coef1.set_val(cur_idx,1);
4109                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(num_partns2)+"},"+to_string(num_partns2+1);
4110                                                 if(num_partns2 > 1) on_coef2.set_val(cur_idx,1);
4111                                             }
4112                                         }
4113 
4114 
4115                                         else /*means model_type == "lambda_III" */{
4116 
4117                                             //fill constraint indices
4118                                             for (int i=0; i<(num_partns1-2)*2+2; ++i){
4119                                                 const_idx1.add(to_string(i+1));
4120                                             }
4121 
4122                                             //fill constraint indices
4123                                             for (int i=0; i<(num_partns2-2)*2+2; ++i){
4124                                                 const_idx2.add(to_string(i+1));
4125                                             }
4126 
4127                                             // Lambda coefficient matrix when linking with partition variables
4128                                             if(num_partns1 > 1) lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
4129                                             if(num_partns2 > 1) lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
4130 
4131                                             // Partition coefficient matrix when linking with lambda variables
4132                                             if(num_partns1 > 1) on_coef1.in(indices(added, partns1, const_idx1));
4133                                             if(num_partns2 > 1) on_coef2.in(indices(added, partns2, const_idx2));
4134 
4135                                             // fill lambda_coef1 and lambda_coef2
4136                                             for (size_t inst = 0; inst< nb_ins; inst++){
4137                                                 auto cur_var_id = vlift->get_id_inst(inst);
4138                                                 auto cur_var_idx = added._keys->at(cur_var_id);
4139                                                 if(num_partns1 > 1) {
4140                                                     for (int j=0; j<num_partns2+1; ++j) {
4141                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(1);
4142                                                         lambda_coef1.set_val(cur_idx,1);
4143                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string((num_partns1-2)*2+2);
4144                                                         lambda_coef1.set_val(cur_idx,1);
4145                                                     }
4146 
4147                                                     for (int i=1 ; i<(num_partns1-2)*2+1; i=i+2) {
4148                                                         for (int j=(i-1)/2 + 2; j<num_partns1+1; ++j) {
4149                                                             for(int k=0; k<num_partns2+1; ++k){
4150                                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+1);
4151                                                                 lambda_coef1.set_val(cur_idx,1);
4152                                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+2);
4153                                                                 lambda_coef1.set_val(cur_idx,-1);
4154                                                             }
4155                                                         }
4156                                                     }
4157                                                 }
4158                                                 if(num_partns2 > 1) {
4159                                                     for (int i=0; i<num_partns1+1; ++i) {
4160                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(1)+"},"+to_string(1);
4161                                                         lambda_coef2.set_val(cur_idx,1);
4162                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(num_partns2+1)+"},"+to_string((num_partns2-2)*2+2);
4163                                                         lambda_coef2.set_val(cur_idx,1);
4164                                                     }
4165 
4166                                                     for (int i=1 ; i<(num_partns2-2)*2+1; i=i+2) {
4167                                                         for (int j=(i-1)/2 + 2; j<num_partns2+1; ++j) {
4168                                                             for(int k=0; k<num_partns1+1; ++k){
4169                                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
4170                                                                 lambda_coef2.set_val(cur_idx,1);
4171                                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
4172                                                                 lambda_coef2.set_val(cur_idx,-1);
4173                                                             }
4174                                                         }
4175                                                     }
4176                                                 }
4177                                             }
4178 
4179 
4180 
4181                                             // fill on_coef1 and on_coef2
4182                                             for (size_t inst = 0; inst< nb_ins; inst++){
4183                                                 auto cur_var_id = vlift->get_id_inst(inst);
4184                                                 auto cur_var_idx = added._keys->at(cur_var_id);
4185                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
4186                                                 if(num_partns1 > 1) {
4187                                                     on_coef1.set_val(cur_idx,1);
4188 
4189                                                     for (int i=1; i<num_partns1; ++i) {
4190                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(2);
4191                                                         on_coef1.set_val(cur_idx, 1);
4192                                                     }
4193 
4194                                                     for (int i=2 ; i<(num_partns1-2)*2+2; i=i+2) {
4195                                                         for (int j=i/2+1; j<num_partns1; ++j) {
4196                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
4197                                                             on_coef1.set_val(cur_idx,-1);
4198                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
4199                                                             on_coef1.set_val(cur_idx,1);
4200                                                         }
4201                                                     }
4202                                                 }
4203                                                 if(num_partns2 > 1) {
4204                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
4205                                                     on_coef2.set_val(cur_idx,1);
4206                                                     for (int i=1; i<num_partns2; ++i) {
4207                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(2);
4208                                                         on_coef2.set_val(cur_idx, 1);
4209                                                     }
4210 
4211                                                     for (int i=2 ; i<(num_partns2-2)*2+2; i=i+2) {
4212                                                         for (int j=i/2+1; j<num_partns2; ++j) {
4213                                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
4214                                                             on_coef2.set_val(cur_idx,-1);
4215                                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
4216                                                             on_coef2.set_val(cur_idx,1);
4217                                                         }
4218                                                     }
4219                                                 }
4220                                             }
4221                                         }
4222 
4223 
4224                                         /** Constraints */
4225                                         // Representation of the bilinear term with convex combination
4226                                         Constraint<> bln_rep(pair.first+"_bln_rep");
4227                                         bln_rep = EP.in_matrix(nb_entries,total_entries-nb_entries)*lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - vlift->in(added);
4228                                         add(bln_rep.in(added) == 0);
4229 
4230                                         // Representation of o1 with convex combination
4231                                         Constraint<> o1_rep(pair.first+"_o1_rep");
4232                                         o1_rep = bounds1.from_ith(0,inst_partition_lambda).in_matrix(nb_entries, 1) * lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - o1.in(o1_ids);
4233                                         add(o1_rep.in(added) == 0);
4234 
4235                                         // Representation of o2 with convex combination
4236                                         Constraint<> o2_rep(pair.first+"_o2_rep");
4237                                         o2_rep = bounds2.in_ignore_ith(nb_entries, 1, inst_partition_lambda).in_matrix(nb_entries,1) * lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - o2.in(o2_ids);
4238                                         add(o2_rep.in(added) == 0);
4239 
4240                                         // Linking partition variables1 with lambda
4241                                         if(model_type == "lambda_II"){
4242                                             if(num_partns1 > 1) {
4243                                                 Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_II");
4244                                                 on_link_lambda1 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
4245                                                 add(on_link_lambda1.in(indices(added,const_idx1)) <= 0);
4246                                             }
4247                                             if(num_partns2 > 1) {
4248                                                 Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_II");
4249                                                 on_link_lambda2 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - binvar2->in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
4250                                                 add(on_link_lambda2.in(indices(added,const_idx2)) <= 0);
4251                                             }
4252                                         }
4253                                         else{
4254                                             if(num_partns1 > 1) {
4255                                                 Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_III");
4256                                                 on_link_lambda1 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
4257                                                 add(on_link_lambda1.in(indices(added,const_idx1)) <= 0);
4258                                             }
4259                                             if(num_partns2 > 1) {
4260                                                 Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_III");
4261                                                 on_link_lambda2 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - binvar2->in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
4262                                                 add(on_link_lambda2.in(indices(added,const_idx2)) <= 0);
4263                                             }
4264                                         }
4265                                         // sum over lambda
4266                                         Constraint<> lambdaSum(pair.first+"_lambdaSum");
4267                                         lambdaSum = sum(lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries));
4268                                         add(lambdaSum.in(added) == 1);
4269                                     }
4270                                 }
4271                             }
4272                             else if(binvar_ptr1 !=_vars_name.end() && binvar_ptr2 ==_vars_name.end()){ //means v2 has not been partitioned before
4273                                 {
4274                                     DebugOn("<<<<<<<<<< THIS IS SEEN LIFT -> DOUBLE -> SEEN FIRST BINARY -> DIFF CORE VARS <<<<<<<<<<<" << endl);
4275 
4276                                     auto binvar1 = static_pointer_cast<var<int>>(binvar_ptr1->second);
4277                                     indices partns1("partns1");
4278                                     for (int i = 0; i < num_partns1 ; ++i)
4279                                     {
4280                                         partns1.add(name1+ "{" +to_string(i+1) + "}");
4281                                     }
4282                                     param<int> lb1("lb1"), ub1("ub1");
4283                                     lb1.in(o1_ids_uq,partns1);
4284                                     ub1.in(o1_ids_uq,partns1);
4285                                     lb1.set_val(0), ub1.set_val(1);
4286                                     auto added1 = binvar1->add_bounds(lb1,ub1);
4287                                     reindex_vars();
4288 
4289                                     var<int> on2(name2+"_binary",0,1);
4290                                     indices partns2("partns2");
4291                                     for (int i = 0; i < num_partns2 ; ++i)
4292                                     {
4293                                         partns2.add(name2+ "{" + to_string(i+1) + "}");
4294                                     }
4295                                     add(on2.in(o2_ids_uq,partns2));
4296 
4297                                     auto nb_entries_v1 = o1_ids.get_nb_entries();
4298                                     auto nb_entries_v2 = o2_ids.get_nb_entries();
4299                                     auto nb_entries = unique_ids.get_nb_entries();
4300 
4301                                     if(!added1.empty()){
4302                                         Constraint<> onSum1(o1._name+"_binarySum");
4303                                         onSum1 = sum(binvar1->in(added1).in_matrix(nb_entries_v1,1));
4304                                         auto vset1 = added1.from_ith(0,nb_entries_v1);
4305                                         vset1.filter_refs(vset1.get_unique_refs());
4306                                         add(onSum1.in(vset1) == 1);
4307                                     }
4308 
4309                                     Constraint<> onSum2(o2._name+"_binarySum");
4310                                     onSum2 = sum(on2.in_matrix(nb_entries_v2,1));
4311                                     add(onSum2.in(o2_ids_uq) == 1);
4312 
4313                                     if(model_type == "on/off"){//if on/off is chosen
4314 
4315                                         indices partns("partns");
4316                                         partns = indices(partns1,partns2);
4317                                         auto inst_partition = indices(added,partns);
4318                                         auto total_entries = inst_partition.get_nb_entries();
4319 
4320                                         if(binvar_ptr3 !=_vars_name.end()){ //means the combined binary variable has been used before
4321 
4322                                             auto binvar3 = static_pointer_cast<var<int>>(binvar_ptr3->second);
4323                                             param<int> lb3("lb3"), ub3("ub3");
4324                                             lb3.in(added,partns);
4325                                             ub3.in(added,partns);
4326                                             lb3.set_val(0), ub3.set_val(1);
4327                                             auto added3 = binvar3->add_bounds(lb3,ub3);
4328                                             reindex_vars();
4329 
4330                                             Constraint<> onLink1(pair.first+"_binaryLink1");
4331                                             onLink1 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) - binvar3->in(inst_partition);
4332                                             add(onLink1.in(inst_partition) >= 0);
4333 
4334                                             Constraint<> onLink2(pair.first+"_binaryLink2");
4335                                             onLink2 = on2.in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - binvar3->in(inst_partition);
4336                                             add(onLink2.in(inst_partition) >= 0);
4337 
4338                                             Constraint<> onLink3(pair.first+"_binaryLink3");
4339                                             onLink3 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) + on2.in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - binvar3->in(inst_partition);
4340                                             add(onLink3.in(inst_partition) <= 0);
4341 
4342                                             Constraint<> onSumComb(pair.first+"_binarySum");
4343                                             onSumComb = sum((binvar3->in(added3)).in_matrix(nb_entries,total_entries-nb_entries));
4344                                             add(onSumComb.in(added) == 1);
4345 
4346                                             add_on_off_McCormick_refined(pair.first, vlift->in(added), o1.in(o1_ids), o2.in(o2_ids), binvar3->in(added3));
4347                                         }
4348                                         else{ //means the combined binary variable has not been used before
4349                                             var<int> on(name1+name2+"_binary",0,1);
4350                                             add(on.in(inst_partition));
4351 
4352                                             Constraint<> onLink1(pair.first+"_binaryLink1");
4353                                             onLink1 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) - on;
4354                                             add(onLink1.in(inst_partition) >= 0);
4355 
4356                                             Constraint<> onLink2(pair.first+"_binaryLink2");
4357                                             onLink2 = on2.in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - on;
4358                                             add(onLink2.in(inst_partition) >= 0);
4359 
4360                                             Constraint<> onLink3(pair.first+"_binaryLink3");
4361                                             onLink3 = binvar1->from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) + on2.in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - on;
4362                                             add(onLink3.in(inst_partition) <= 0);
4363 
4364                                             Constraint<> onSumComb(pair.first+"_binarySum");
4365                                             onSumComb = sum(on.in_matrix(nb_entries,total_entries-nb_entries));
4366                                             add(onSumComb.in(added) == 1);
4367 
4368                                             add_on_off_McCormick_refined(pair.first, vlift->in(added), o1.in(o1_ids), o2.in(o2_ids), on);
4369                                         }
4370                                     }
4371 
4372                                     else{//means it is one of the lambda formulations
4373 
4374                                         //difference is this has one more partition index
4375                                         indices partns1_lambda("partns1_lambda");
4376                                         for (int i = 0; i < num_partns1+1; ++i)
4377                                         {
4378                                             partns1_lambda.add(name1+ "{" +to_string(i+1) + "}");
4379                                         }
4380 
4381                                         indices partns2_lambda("partns2_lambda");
4382                                         for (int i = 0; i < num_partns2+1; ++i)
4383                                         {
4384                                             partns2_lambda.add(name2+ "{" +to_string(i+1) + "}");
4385                                         }
4386 
4387                                         indices partns_lambda("partns_lambda");
4388                                         partns_lambda = indices(partns1_lambda,partns2_lambda);
4389                                         auto inst_partition_lambda = indices(added,partns_lambda);
4390                                         auto inst_partition_bounds1 = indices(added,partns1_lambda);
4391                                         auto inst_partition_bounds2 = indices(added,partns2_lambda);
4392 
4393                                         // Convex combination variables
4394                                         auto lambda_ptr = _vars_name.find(name1+name2+"_lambda");
4395                                         auto lambda = static_pointer_cast<var<double>>(lambda_ptr->second);
4396                                         param<double> lb_lambda("lb_lambda"), ub_lambda("ub_lambda");
4397                                         lb_lambda.in(added,partns_lambda);
4398                                         ub_lambda.in(added,partns_lambda);
4399                                         lb_lambda.set_val(0), ub_lambda.set_val(1);
4400                                         auto added_lambda = lambda->add_bounds(lb_lambda,ub_lambda);
4401                                         reindex_vars();
4402 
4403                                         /** Parameters */
4404                                         // Bounds on variable v1 & v2
4405                                         param<> bounds1(name1+"_bounds1");
4406                                         bounds1.in(inst_partition_bounds1);
4407 
4408                                         param<> bounds2(name2+"_bounds2");
4409                                         bounds2.in(inst_partition_bounds2);
4410 
4411                                         // Function values on the extreme points
4412                                         param<> EP(name1+name2+"_grid_values");
4413                                         EP.in(inst_partition_lambda);
4414                                         auto total_entries = inst_partition_lambda.get_nb_entries();
4415 
4416                                         size_t nb_ins = vlift->in(added).get_nb_inst();
4417                                         auto o1_global_lb = o1.get_lb();
4418                                         auto increment1 = (o1.get_ub() - o1_global_lb)/num_partns1;
4419 
4420                                         auto o2_global_lb = o2.get_lb();
4421                                         auto increment2 = (o2.get_ub() - o2_global_lb)/num_partns2;
4422 
4423                                         // fill bounds1 and function values
4424                                         for (int i=0 ; i<num_partns1+1; ++i) {
4425                                             auto bound_partn1 = o1_global_lb + increment1*i;
4426                                             bound_partn1.eval_all();
4427                                             for (size_t inst = 0; inst< nb_ins; inst++){
4428                                                 auto cur_var_id = vlift->get_id_inst(inst);
4429                                                 auto cur_var_idx = added._keys->at(cur_var_id);
4430                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
4431                                                 bounds1.set_val(cur_idx,bound_partn1.eval(inst));
4432                                                 for(int j=0; j<num_partns2+1; ++j){
4433                                                     auto bound_partn2 = o2_global_lb + increment2*j;
4434                                                     bound_partn2.eval_all();
4435                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"}";
4436                                                     EP.set_val(cur_idx,(bound_partn1.eval(inst)*bound_partn2.eval(inst)));
4437                                                 }
4438                                             }
4439                                         }
4440                                         // fill bounds2
4441                                         for (int i=0 ; i<num_partns2+1; ++i) {
4442                                             auto bound_partn2 = o2_global_lb + increment2*i;
4443                                             bound_partn2.eval_all();
4444                                             for (size_t inst = 0; inst< nb_ins; inst++){
4445                                                 auto cur_var_id = vlift->get_id_inst(inst);
4446                                                 auto cur_var_idx = added._keys->at(cur_var_id);
4447                                                 string cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"}";
4448                                                 bounds2.set_val(cur_idx,bound_partn2.eval(inst));
4449                                             }
4450                                         }
4451 
4452                                         // Lambda coefficient matrix when linking with partition variables
4453                                         param<> lambda_coef1(name1+"_lambda_linking_coefficients1");
4454                                         param<> lambda_coef2(name2+"_lambda_linking_coefficients2");
4455 
4456                                         // Partition coefficient matrix when linking with lambda variables
4457                                         param<> on_coef1(name1+"_partition_linking_coefficients1");
4458                                         param<> on_coef2(name2+"_partition_linking_coefficients2");
4459 
4460                                         // create constraint indices
4461                                         indices const_idx1("const_idx1");
4462                                         indices const_idx2("const_idx2");
4463 
4464                                         if(model_type == "lambda_II"){
4465 
4466                                             //fill constraint indices
4467                                             for (int i=0; i<num_partns1+1; ++i){
4468                                                 const_idx1.add(to_string(i+1));
4469                                             }
4470 
4471                                             //fill constraint indices
4472                                             for (int i=0; i<num_partns2+1; ++i){
4473                                                 const_idx2.add(to_string(i+1));
4474                                             }
4475 
4476                                             // Lambda coefficient matrix when linking with partition variables
4477                                             if(num_partns1 > 1) lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
4478                                             if(num_partns2 > 1) lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
4479 
4480                                             // Lambda coefficient matrix when linking with partition variables
4481                                             if(num_partns1 > 1) on_coef1.in(indices(added, partns1, const_idx1));
4482                                             if(num_partns2 > 1) on_coef2.in(indices(added, partns2, const_idx2));
4483 
4484                                             // fill lambda_coef1 and lambda_coef2
4485                                             for (size_t inst = 0; inst< nb_ins; inst++){
4486                                                 auto cur_var_id = vlift->get_id_inst(inst);
4487                                                 auto cur_var_idx = added._keys->at(cur_var_id);
4488                                                 for (int i=0 ; i<num_partns1+1; ++i) {
4489                                                     for (int j=0 ; j<num_partns2+1; ++j) {
4490                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
4491                                                         if(num_partns1 > 1) lambda_coef1.set_val(cur_idx,1);
4492                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(j+1);
4493                                                         if(num_partns2 > 1) lambda_coef2.set_val(cur_idx,1);
4494                                                     }
4495                                                 }
4496                                             }
4497 
4498                                             // fill on_coef1 and on_coef2
4499                                             for (size_t inst = 0; inst< nb_ins; inst++){
4500                                                 auto cur_var_id = vlift->get_id_inst(inst);
4501                                                 auto cur_var_idx = added._keys->at(cur_var_id);
4502                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
4503                                                 if(num_partns1 > 1) on_coef1.set_val(cur_idx,1);
4504                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
4505                                                 if(num_partns2 > 1) on_coef2.set_val(cur_idx,1);
4506                                                 if(num_partns1 > 1) {
4507                                                     for (int i=1 ; i<num_partns1; ++i) {
4508                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i)+"},"+to_string(i+1);
4509                                                         on_coef1.set_val(cur_idx,1);
4510                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
4511                                                         on_coef1.set_val(cur_idx,1);
4512                                                     }
4513                                                 }
4514                                                 if(num_partns2 > 1) {
4515                                                     for (int i=1 ; i<num_partns2; ++i) {
4516                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(i)+"},"+to_string(i+1);
4517                                                         on_coef2.set_val(cur_idx,1);
4518                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(i+1);
4519                                                         on_coef2.set_val(cur_idx,1);
4520                                                     }
4521                                                 }
4522                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1)+"},"+to_string(num_partns1+1);
4523                                                 if(num_partns1 > 1) on_coef1.set_val(cur_idx,1);
4524                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(num_partns2)+"},"+to_string(num_partns2+1);
4525                                                 if(num_partns2 > 1) on_coef2.set_val(cur_idx,1);
4526                                             }
4527                                         }
4528 
4529 
4530                                         else /*means model_type == "lambda_III" */{
4531 
4532                                             //fill constraint indices
4533                                             for (int i=0; i<(num_partns1-2)*2+2; ++i){
4534                                                 const_idx1.add(to_string(i+1));
4535                                             }
4536 
4537                                             //fill constraint indices
4538                                             for (int i=0; i<(num_partns2-2)*2+2; ++i){
4539                                                 const_idx2.add(to_string(i+1));
4540                                             }
4541 
4542                                             // Lambda coefficient matrix when linking with partition variables
4543                                             if(num_partns1 > 1) lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
4544                                             if(num_partns2 > 1) lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
4545 
4546                                             // Partition coefficient matrix when linking with lambda variables
4547                                             if(num_partns1 > 1) on_coef1.in(indices(added, partns1, const_idx1));
4548                                             if(num_partns2 > 1) on_coef2.in(indices(added, partns2, const_idx2));
4549 
4550                                             // fill lambda_coef1 and lambda_coef2
4551                                             for (size_t inst = 0; inst< nb_ins; inst++){
4552                                                 auto cur_var_id = vlift->get_id_inst(inst);
4553                                                 auto cur_var_idx = added._keys->at(cur_var_id);
4554                                                 if(num_partns1 > 1) {
4555                                                     for (int j=0; j<num_partns2+1; ++j) {
4556                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(1);
4557                                                         lambda_coef1.set_val(cur_idx,1);
4558                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string((num_partns1-2)*2+2);
4559                                                         lambda_coef1.set_val(cur_idx,1);
4560                                                     }
4561 
4562                                                     for (int i=1 ; i<(num_partns1-2)*2+1; i=i+2) {
4563                                                         for (int j=(i-1)/2 + 2; j<num_partns1+1; ++j) {
4564                                                             for(int k=0; k<num_partns2+1; ++k){
4565                                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+1);
4566                                                                 lambda_coef1.set_val(cur_idx,1);
4567                                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+2);
4568                                                                 lambda_coef1.set_val(cur_idx,-1);
4569                                                             }
4570                                                         }
4571                                                     }
4572                                                 }
4573                                                 if(num_partns2 > 1) {
4574                                                     for (int i=0; i<num_partns1+1; ++i) {
4575                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(1)+"},"+to_string(1);
4576                                                         lambda_coef2.set_val(cur_idx,1);
4577                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(num_partns2+1)+"},"+to_string((num_partns2-2)*2+2);
4578                                                         lambda_coef2.set_val(cur_idx,1);
4579                                                     }
4580 
4581                                                     for (int i=1 ; i<(num_partns2-2)*2+1; i=i+2) {
4582                                                         for (int j=(i-1)/2 + 2; j<num_partns2+1; ++j) {
4583                                                             for(int k=0; k<num_partns1+1; ++k){
4584                                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
4585                                                                 lambda_coef2.set_val(cur_idx,1);
4586                                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
4587                                                                 lambda_coef2.set_val(cur_idx,-1);
4588                                                             }
4589                                                         }
4590                                                     }
4591                                                 }
4592                                             }
4593 
4594 
4595 
4596                                             // fill on_coef1 and on_coef2
4597                                             for (size_t inst = 0; inst< nb_ins; inst++){
4598                                                 auto cur_var_id = vlift->get_id_inst(inst);
4599                                                 auto cur_var_idx = added._keys->at(cur_var_id);
4600                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
4601                                                 if(num_partns1 > 1) {
4602                                                     on_coef1.set_val(cur_idx,1);
4603 
4604                                                     for (int i=1; i<num_partns1; ++i) {
4605                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(2);
4606                                                         on_coef1.set_val(cur_idx, 1);
4607                                                     }
4608 
4609                                                     for (int i=2 ; i<(num_partns1-2)*2+2; i=i+2) {
4610                                                         for (int j=i/2+1; j<num_partns1; ++j) {
4611                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
4612                                                             on_coef1.set_val(cur_idx,-1);
4613                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
4614                                                             on_coef1.set_val(cur_idx,1);
4615                                                         }
4616                                                     }
4617                                                 }
4618                                                 if(num_partns2 > 1) {
4619                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
4620                                                     on_coef2.set_val(cur_idx,1);
4621                                                     for (int i=1; i<num_partns2; ++i) {
4622                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(2);
4623                                                         on_coef2.set_val(cur_idx, 1);
4624                                                     }
4625 
4626                                                     for (int i=2 ; i<(num_partns2-2)*2+2; i=i+2) {
4627                                                         for (int j=i/2+1; j<num_partns2; ++j) {
4628                                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
4629                                                             on_coef2.set_val(cur_idx,-1);
4630                                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
4631                                                             on_coef2.set_val(cur_idx,1);
4632                                                         }
4633                                                     }
4634                                                 }
4635                                             }
4636                                         }
4637 
4638 
4639                                         /** Constraints */
4640                                         // Representation of the bilinear term with convex combination
4641                                         Constraint<> bln_rep(pair.first+"_bln_rep");
4642                                         bln_rep = EP.in_matrix(nb_entries,total_entries-nb_entries)*lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - vlift->in(added);
4643                                         add(bln_rep.in(added) == 0);
4644 
4645                                         // Representation of o1 with convex combination
4646                                         Constraint<> o1_rep(pair.first+"_o1_rep");
4647                                         o1_rep = bounds1.from_ith(0,inst_partition_lambda).in_matrix(nb_entries, 1) * lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - o1.in(o1_ids);
4648                                         add(o1_rep.in(added) == 0);
4649 
4650                                         // Representation of o2 with convex combination
4651                                         Constraint<> o2_rep(pair.first+"_o2_rep");
4652                                         o2_rep = bounds2.in_ignore_ith(nb_entries, 1, inst_partition_lambda).in_matrix(nb_entries,1) * lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - o2.in(o2_ids);
4653                                         add(o2_rep.in(added) == 0);
4654 
4655                                         // Linking partition variables1 with lambda
4656                                         if(model_type == "lambda_II"){
4657                                             if(num_partns1 > 1) {
4658                                                 Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_II");
4659                                                 on_link_lambda1 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
4660                                                 add(on_link_lambda1.in(indices(added,const_idx1)) <= 0);
4661                                             }
4662                                             if(num_partns2 > 1) {
4663                                                 Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_II");
4664                                                 on_link_lambda2 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - on2.in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
4665                                                 add(on_link_lambda2.in(indices(added,const_idx2)) <= 0);
4666                                             }
4667                                         }
4668                                         else{
4669                                             if(num_partns1 > 1) {
4670                                                 Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_III");
4671                                                 on_link_lambda1 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - binvar1->in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
4672                                                 add(on_link_lambda1.in(indices(added,const_idx1)) <= 0);
4673                                             }
4674                                             if(num_partns2 > 1) {
4675                                                 Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_III");
4676                                                 on_link_lambda2 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - on2.in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
4677                                                 add(on_link_lambda2.in(indices(added,const_idx2)) <= 0);
4678                                             }
4679                                         }
4680                                         // sum over lambda
4681                                         Constraint<> lambdaSum(pair.first+"_lambdaSum");
4682                                         lambdaSum = sum(lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries));
4683                                         add(lambdaSum.in(added) == 1);
4684                                     }
4685                                 }
4686                             }
4687                             else if(binvar_ptr1 ==_vars_name.end() && binvar_ptr2 !=_vars_name.end()){ //means v1 has not been partitioned before
4688                                 DebugOn("<<<<<<<<<< THIS IS SEEN LIFT -> DOUBLE -> SEEN SECOND BINARY -> DIFF CORE VARS <<<<<<<<<<<" << endl);
4689 
4690                                 var<int> on1(name1+"_binary",0,1);
4691                                 indices partns1("partns1");
4692                                 for (int i = 0; i < num_partns1 ; ++i)
4693                                 {
4694                                     partns1.add(name1+ "{" + to_string(i+1) + "}");
4695                                 }
4696                                 add(on1.in(o1_ids_uq,partns1));
4697 
4698                                 auto binvar2 = static_pointer_cast<var<int>>(binvar_ptr2->second);
4699                                 indices partns2("partns2");
4700                                 for (int i = 0; i < num_partns2 ; ++i)
4701                                 {
4702                                     partns2.add(name2+ "{" + to_string(i+1) + "}");
4703                                 }
4704                                 param<int> lb2("lb2"), ub2("ub2");
4705                                 lb2.in(o2_ids_uq,partns2);
4706                                 ub2.in(o2_ids_uq,partns2);
4707                                 lb2.set_val(0), ub2.set_val(1);
4708                                 auto added2 = binvar2->add_bounds(lb2,ub2);
4709                                 reindex_vars();
4710 
4711                                 auto nb_entries_v1 = o1_ids.get_nb_entries();
4712                                 auto nb_entries_v2 = o2_ids.get_nb_entries();
4713                                 auto nb_entries = added.get_nb_entries();
4714 
4715                                 Constraint<> onSum1(o1._name+"_binarySum");
4716                                 onSum1 = sum(on1.in_matrix(nb_entries_v1,1));
4717                                 add(onSum1.in(o1_ids_uq) == 1);
4718 
4719                                 if(!added2.empty()){
4720                                     Constraint<> onSum2(o2._name+"_binarySum");
4721                                     onSum2 = sum(binvar2->in(added2).in_matrix(nb_entries_v2,1));
4722                                     auto vset2 = added2.from_ith(0,nb_entries_v2);
4723                                     vset2.filter_refs(vset2.get_unique_refs());
4724                                     add(onSum2.in(vset2) == 1);
4725                                 }
4726 
4727                                 if(model_type == "on/off"){//if on/off is chosen
4728 
4729                                     indices partns("partns");
4730                                     partns = indices(partns1,partns2);
4731                                     auto inst_partition = indices(added,partns);
4732                                     auto total_entries = inst_partition.get_nb_entries();
4733 
4734                                     if(binvar_ptr3 !=_vars_name.end()){ //means the combined binary variable has been used before
4735                                         auto binvar3 = static_pointer_cast<var<int>>(binvar_ptr3->second);
4736                                         param<int> lb3("lb3"), ub3("ub3");
4737                                         lb3.in(added,partns);
4738                                         ub3.in(added,partns);
4739                                         lb3.set_val(0), ub3.set_val(1);
4740                                         auto added3 = binvar3->add_bounds(lb3,ub3);
4741                                         reindex_vars();
4742 
4743                                         Constraint<> onLink1(pair.first+"_binaryLink1");
4744                                         onLink1 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) - binvar3->in(inst_partition);
4745                                         add(onLink1.in(inst_partition) >= 0);
4746 
4747                                         Constraint<> onLink2(pair.first+"_binaryLink2");
4748                                         onLink2 = binvar2->in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - binvar3->in(inst_partition);
4749                                         add(onLink2.in(inst_partition) >= 0);
4750 
4751                                         Constraint<> onLink3(pair.first+"_binaryLink3");
4752                                         onLink3 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) + binvar2->in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - binvar3->in(inst_partition);
4753                                         add(onLink3.in(inst_partition) <= 0);
4754 
4755                                         Constraint<> onSumComb(pair.first+"_binarySum");
4756                                         onSumComb = sum((binvar3->in(added3)).in_matrix(nb_entries,total_entries-nb_entries));
4757                                         add(onSumComb.in(added) == 1);
4758 
4759                                         add_on_off_McCormick_refined(pair.first, vlift->in(added), o1.in(o1_ids), o2.in(o2_ids), binvar3->in(added3));
4760                                     }
4761 
4762                                     else{ //means the combined binary variable has not been used before
4763                                         var<int> on(name1+name2+"_binary",0,1);
4764                                         add(on.in(inst_partition));
4765 
4766                                         Constraint<> onLink1(pair.first+"_binaryLink1");
4767                                         onLink1 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) - on;
4768                                         add(onLink1.in(inst_partition) >= 0);
4769 
4770                                         Constraint<> onLink2(pair.first+"_binaryLink2");
4771                                         onLink2 = binvar2->in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - on;
4772                                         add(onLink2.in(inst_partition) >= 0);
4773 
4774                                         Constraint<> onLink3(pair.first+"_binaryLink3");
4775                                         onLink3 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) + binvar2->in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - on;
4776                                         add(onLink3.in(inst_partition) <= 0);
4777 
4778                                         Constraint<> onSumComb(pair.first+"_binarySum");
4779                                         onSumComb = sum(on.in_matrix(nb_entries,total_entries-nb_entries));
4780                                         add(onSumComb.in(added) == 1);
4781 
4782                                         add_on_off_McCormick_refined(pair.first, vlift->in(added), o1.in(o1_ids), o2.in(o2_ids), on);
4783                                     }
4784 
4785                                 }
4786 
4787                                 else{//means it is one of the lambda formulations
4788 
4789                                     //difference is this has one more partition index
4790                                     indices partns1_lambda("partns1_lambda");
4791                                     for (int i = 0; i < num_partns1+1; ++i)
4792                                     {
4793                                         partns1_lambda.add(name1+ "{" +to_string(i+1) + "}");
4794                                     }
4795 
4796                                     indices partns2_lambda("partns2_lambda");
4797                                     for (int i = 0; i < num_partns2+1; ++i)
4798                                     {
4799                                         partns2_lambda.add(name2+ "{" +to_string(i+1) + "}");
4800                                     }
4801 
4802                                     indices partns_lambda("partns_lambda");
4803                                     partns_lambda = indices(partns1_lambda,partns2_lambda);
4804                                     auto inst_partition_lambda = indices(added,partns_lambda);
4805                                     auto inst_partition_bounds1 = indices(added,partns1_lambda);
4806                                     auto inst_partition_bounds2 = indices(added,partns2_lambda);
4807 
4808                                     // Convex combination variables
4809                                     auto lambda_ptr = _vars_name.find(name1+name2+"_lambda");
4810                                     auto lambda = static_pointer_cast<var<double>>(lambda_ptr->second);
4811                                     param<double> lb_lambda("lb_lambda"), ub_lambda("ub_lambda");
4812                                     lb_lambda.in(added,partns_lambda);
4813                                     ub_lambda.in(added,partns_lambda);
4814                                     lb_lambda.set_val(0), ub_lambda.set_val(1);
4815                                     auto added_lambda = lambda->add_bounds(lb_lambda,ub_lambda);
4816                                     reindex_vars();
4817 
4818                                     /** Parameters */
4819                                     // Bounds on variable v1 & v2
4820                                     param<> bounds1(name1+"_bounds1");
4821                                     bounds1.in(inst_partition_bounds1);
4822 
4823                                     param<> bounds2(name2+"_bounds2");
4824                                     bounds2.in(inst_partition_bounds2);
4825 
4826                                     // Function values on the extreme points
4827                                     param<> EP(name1+name2+"_grid_values");
4828                                     EP.in(inst_partition_lambda);
4829                                     auto total_entries = inst_partition_lambda.get_nb_entries();
4830 
4831                                     size_t nb_ins = vlift->in(added).get_nb_inst();
4832                                     auto o1_global_lb = o1.get_lb();
4833                                     auto increment1 = (o1.get_ub() - o1_global_lb)/num_partns1;
4834 
4835                                     auto o2_global_lb = o2.get_lb();
4836                                     auto increment2 = (o2.get_ub() - o2_global_lb)/num_partns2;
4837 
4838                                     // fill bounds1 and function values
4839                                     for (int i=0 ; i<num_partns1+1; ++i) {
4840                                         auto bound_partn1 = o1_global_lb + increment1*i;
4841                                         bound_partn1.eval_all();
4842                                         for (size_t inst = 0; inst< nb_ins; inst++){
4843                                             auto cur_var_id = vlift->get_id_inst(inst);
4844                                             auto cur_var_idx = added._keys->at(cur_var_id);
4845                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
4846                                             bounds1.set_val(cur_idx,bound_partn1.eval(inst));
4847                                             for(int j=0; j<num_partns2+1; ++j){
4848                                                 auto bound_partn2 = o2_global_lb + increment2*j;
4849                                                 bound_partn2.eval_all();
4850                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"}";
4851                                                 EP.set_val(cur_idx,(bound_partn1.eval(inst)*bound_partn2.eval(inst)));
4852                                             }
4853                                         }
4854                                     }
4855                                     // fill bounds2
4856                                     for (int i=0 ; i<num_partns2+1; ++i) {
4857                                         auto bound_partn2 = o2_global_lb + increment2*i;
4858                                         bound_partn2.eval_all();
4859                                         for (size_t inst = 0; inst< nb_ins; inst++){
4860                                             auto cur_var_id = vlift->get_id_inst(inst);
4861                                             auto cur_var_idx = added._keys->at(cur_var_id);
4862                                             string cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"}";
4863                                             bounds2.set_val(cur_idx,bound_partn2.eval(inst));
4864                                         }
4865                                     }
4866 
4867                                     // Lambda coefficient matrix when linking with partition variables
4868                                     param<> lambda_coef1(name1+"_lambda_linking_coefficients1");
4869                                     param<> lambda_coef2(name2+"_lambda_linking_coefficients2");
4870 
4871                                     // Partition coefficient matrix when linking with lambda variables
4872                                     param<> on_coef1(name1+"_partition_linking_coefficients1");
4873                                     param<> on_coef2(name2+"_partition_linking_coefficients2");
4874 
4875                                     // create constraint indices
4876                                     indices const_idx1("const_idx1");
4877                                     indices const_idx2("const_idx2");
4878 
4879                                     if(model_type == "lambda_II"){
4880 
4881                                         //fill constraint indices
4882                                         for (int i=0; i<num_partns1+1; ++i){
4883                                             const_idx1.add(to_string(i+1));
4884                                         }
4885 
4886                                         //fill constraint indices
4887                                         for (int i=0; i<num_partns2+1; ++i){
4888                                             const_idx2.add(to_string(i+1));
4889                                         }
4890 
4891                                         // Lambda coefficient matrix when linking with partition variables
4892                                         if(num_partns1 > 1) lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
4893                                         if(num_partns2 > 1) lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
4894 
4895                                         // Lambda coefficient matrix when linking with partition variables
4896                                         if(num_partns1 > 1) on_coef1.in(indices(added, partns1, const_idx1));
4897                                         if(num_partns2 > 1) on_coef2.in(indices(added, partns2, const_idx2));
4898 
4899                                         // fill lambda_coef1 and lambda_coef2
4900                                         for (size_t inst = 0; inst< nb_ins; inst++){
4901                                             auto cur_var_id = vlift->get_id_inst(inst);
4902                                             auto cur_var_idx = added._keys->at(cur_var_id);
4903                                             for (int i=0 ; i<num_partns1+1; ++i) {
4904                                                 for (int j=0 ; j<num_partns2+1; ++j) {
4905                                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
4906                                                     if(num_partns1 > 1) lambda_coef1.set_val(cur_idx,1);
4907                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(j+1);
4908                                                     if(num_partns2 > 1) lambda_coef2.set_val(cur_idx,1);
4909                                                 }
4910                                             }
4911                                         }
4912 
4913                                         // fill on_coef1 and on_coef2
4914                                         for (size_t inst = 0; inst< nb_ins; inst++){
4915                                             auto cur_var_id = vlift->get_id_inst(inst);
4916                                             auto cur_var_idx = added._keys->at(cur_var_id);
4917                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
4918                                             if(num_partns1 > 1) on_coef1.set_val(cur_idx,1);
4919                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
4920                                             if(num_partns2 > 1) on_coef2.set_val(cur_idx,1);
4921                                             if(num_partns1 > 1) {
4922                                                 for (int i=1 ; i<num_partns1; ++i) {
4923                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i)+"},"+to_string(i+1);
4924                                                     on_coef1.set_val(cur_idx,1);
4925                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
4926                                                     on_coef1.set_val(cur_idx,1);
4927                                                 }
4928                                             }
4929                                             if(num_partns2 > 1) {
4930                                                 for (int i=1 ; i<num_partns2; ++i) {
4931                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(i)+"},"+to_string(i+1);
4932                                                     on_coef2.set_val(cur_idx,1);
4933                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(i+1);
4934                                                     on_coef2.set_val(cur_idx,1);
4935                                                 }
4936                                             }
4937                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1)+"},"+to_string(num_partns1+1);
4938                                             if(num_partns1 > 1) on_coef1.set_val(cur_idx,1);
4939                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(num_partns2)+"},"+to_string(num_partns2+1);
4940                                             if(num_partns2 > 1) on_coef2.set_val(cur_idx,1);
4941                                         }
4942                                     }
4943 
4944 
4945                                     else /*means model_type == "lambda_III" */{
4946 
4947                                         //fill constraint indices
4948                                         for (int i=0; i<(num_partns1-2)*2+2; ++i){
4949                                             const_idx1.add(to_string(i+1));
4950                                         }
4951 
4952                                         //fill constraint indices
4953                                         for (int i=0; i<(num_partns2-2)*2+2; ++i){
4954                                             const_idx2.add(to_string(i+1));
4955                                         }
4956 
4957                                         // Lambda coefficient matrix when linking with partition variables
4958                                         if(num_partns1 > 1) lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
4959                                         if(num_partns2 > 1) lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
4960 
4961                                         // Partition coefficient matrix when linking with lambda variables
4962                                         if(num_partns1 > 1) on_coef1.in(indices(added, partns1, const_idx1));
4963                                         if(num_partns2 > 1) on_coef2.in(indices(added, partns2, const_idx2));
4964 
4965                                         // fill lambda_coef1 and lambda_coef2
4966                                         for (size_t inst = 0; inst< nb_ins; inst++){
4967                                             auto cur_var_id = vlift->get_id_inst(inst);
4968                                             auto cur_var_idx = added._keys->at(cur_var_id);
4969                                             if(num_partns1 > 1) {
4970                                                 for (int j=0; j<num_partns2+1; ++j) {
4971                                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(1);
4972                                                     lambda_coef1.set_val(cur_idx,1);
4973                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string((num_partns1-2)*2+2);
4974                                                     lambda_coef1.set_val(cur_idx,1);
4975                                                 }
4976 
4977                                                 for (int i=1 ; i<(num_partns1-2)*2+1; i=i+2) {
4978                                                     for (int j=(i-1)/2 + 2; j<num_partns1+1; ++j) {
4979                                                         for(int k=0; k<num_partns2+1; ++k){
4980                                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+1);
4981                                                             lambda_coef1.set_val(cur_idx,1);
4982                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+2);
4983                                                             lambda_coef1.set_val(cur_idx,-1);
4984                                                         }
4985                                                     }
4986                                                 }
4987                                             }
4988                                             if(num_partns2 > 1) {
4989                                                 for (int i=0; i<num_partns1+1; ++i) {
4990                                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(1)+"},"+to_string(1);
4991                                                     lambda_coef2.set_val(cur_idx,1);
4992                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(num_partns2+1)+"},"+to_string((num_partns2-2)*2+2);
4993                                                     lambda_coef2.set_val(cur_idx,1);
4994                                                 }
4995 
4996                                                 for (int i=1 ; i<(num_partns2-2)*2+1; i=i+2) {
4997                                                     for (int j=(i-1)/2 + 2; j<num_partns2+1; ++j) {
4998                                                         for(int k=0; k<num_partns1+1; ++k){
4999                                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
5000                                                             lambda_coef2.set_val(cur_idx,1);
5001                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
5002                                                             lambda_coef2.set_val(cur_idx,-1);
5003                                                         }
5004                                                     }
5005                                                 }
5006                                             }
5007                                         }
5008 
5009 
5010 
5011                                         // fill on_coef1 and on_coef2
5012                                         for (size_t inst = 0; inst< nb_ins; inst++){
5013                                             auto cur_var_id = vlift->get_id_inst(inst);
5014                                             auto cur_var_idx = added._keys->at(cur_var_id);
5015                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
5016                                             if(num_partns1 > 1) {
5017                                                 on_coef1.set_val(cur_idx,1);
5018 
5019                                                 for (int i=1; i<num_partns1; ++i) {
5020                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(2);
5021                                                     on_coef1.set_val(cur_idx, 1);
5022                                                 }
5023 
5024                                                 for (int i=2 ; i<(num_partns1-2)*2+2; i=i+2) {
5025                                                     for (int j=i/2+1; j<num_partns1; ++j) {
5026                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
5027                                                         on_coef1.set_val(cur_idx,-1);
5028                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
5029                                                         on_coef1.set_val(cur_idx,1);
5030                                                     }
5031                                                 }
5032                                             }
5033                                             if(num_partns2 > 1) {
5034                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
5035                                                 on_coef2.set_val(cur_idx,1);
5036                                                 for (int i=1; i<num_partns2; ++i) {
5037                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(2);
5038                                                     on_coef2.set_val(cur_idx, 1);
5039                                                 }
5040 
5041                                                 for (int i=2 ; i<(num_partns2-2)*2+2; i=i+2) {
5042                                                     for (int j=i/2+1; j<num_partns2; ++j) {
5043                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
5044                                                         on_coef2.set_val(cur_idx,-1);
5045                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
5046                                                         on_coef2.set_val(cur_idx,1);
5047                                                     }
5048                                                 }
5049                                             }
5050                                         }
5051                                     }
5052 
5053 
5054                                     /** Constraints */
5055                                     // Representation of the bilinear term with convex combination
5056                                     Constraint<> bln_rep(pair.first+"_bln_rep");
5057                                     bln_rep = EP.in_matrix(nb_entries,total_entries-nb_entries)*lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - vlift->in(added);
5058                                     add(bln_rep.in(added) == 0);
5059 
5060                                     // Representation of o1 with convex combination
5061                                     Constraint<> o1_rep(pair.first+"_o1_rep");
5062                                     o1_rep = bounds1.from_ith(0,inst_partition_lambda).in_matrix(nb_entries, 1) * lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - o1.in(o1_ids);
5063                                     add(o1_rep.in(added) == 0);
5064 
5065                                     // Representation of o2 with convex combination
5066                                     Constraint<> o2_rep(pair.first+"_o2_rep");
5067                                     o2_rep = bounds2.in_ignore_ith(nb_entries, 1, inst_partition_lambda).in_matrix(nb_entries,1) * lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - o2.in(o2_ids);
5068                                     add(o2_rep.in(added) == 0);
5069 
5070                                     // Linking partition variables1 with lambda
5071                                     if(model_type == "lambda_II"){
5072                                         if(num_partns1 > 1) {
5073                                             Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_II");
5074                                             on_link_lambda1 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
5075                                             add(on_link_lambda1.in(indices(added,const_idx1)) <= 0);
5076                                         }
5077                                         if(num_partns2 > 1) {
5078                                             Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_II");
5079                                             on_link_lambda2 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - binvar2->in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
5080                                             add(on_link_lambda2.in(indices(added,const_idx2)) <= 0);
5081                                         }
5082                                     }
5083                                     else{
5084                                         if(num_partns1 > 1) {
5085                                             Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_III");
5086                                             on_link_lambda1 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
5087                                             add(on_link_lambda1.in(indices(added,const_idx1)) <= 0);
5088                                         }
5089                                         if(num_partns2 > 1) {
5090                                             Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_III");
5091                                             on_link_lambda2 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - binvar2->in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
5092                                             add(on_link_lambda2.in(indices(added,const_idx2)) <= 0);
5093                                         }
5094                                     }
5095                                     // sum over lambda
5096                                     Constraint<> lambdaSum(pair.first+"_lambdaSum");
5097                                     lambdaSum = sum(lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries));
5098                                     add(lambdaSum.in(added) == 1);
5099                                 }
5100                             }
5101                             else{ //means v1 and v2 have not been partitioned before
5102                                 if(name1 == name2){
5103                                     DebugOn("<<<<<<<<<< THIS IS SEEN LIFT -> DOUBLE -> NOT SEEN BINARIES -> SAME CORE VARS <<<<<<<<<<<" << endl);
5104 
5105                                     var<int> on1(name1+"_binary",0,1);
5106                                     indices partns1("partns1");
5107                                     for (int i = 0; i < num_partns1 ; ++i)
5108                                     {
5109                                         partns1.add(name1+ "{" +to_string(i+1) + "}");
5110                                     }
5111                                     add(on1.in(union_ids(o1_ids_uq, o2_ids_uq),partns1));
5112                                     indices partns2("partns2");
5113                                     for (int i = 0; i < num_partns2 ; ++i)
5114                                     {
5115                                         partns2.add(name2+ "{" +to_string(i+1) + "}");
5116                                     }
5117 
5118                                     auto nb_entries_v1 = o1_ids.get_nb_entries();
5119                                     auto nb_entries_v2 = o2_ids.get_nb_entries();
5120                                     auto nb_entries = unique_ids.get_nb_entries();
5121 
5122                                     Constraint<> onSum1(o1._name+"_binarySum");
5123                                     onSum1 = sum(on1.in_matrix(nb_entries_v1,1));
5124                                     add(onSum1.in(union_ids(o1_ids_uq,o2_ids_uq)) == 1);
5125 
5126                                     if(model_type == "on/off"){//if on/off is chosen
5127 
5128                                         indices partns("partns");
5129                                         partns = indices(partns1,partns2);
5130                                         auto inst_partition = indices(added,partns);
5131                                         auto total_entries = inst_partition.get_nb_entries();
5132 
5133                                         if(binvar_ptr3 !=_vars_name.end()){ //means the combined binary variable has been used before
5134                                             auto binvar3 = static_pointer_cast<var<int>>(binvar_ptr3->second);
5135                                             param<int> lb3("lb3"), ub3("ub3");
5136                                             lb3.in(added,partns);
5137                                             ub3.in(added,partns);
5138                                             lb3.set_val(0), ub3.set_val(1);
5139                                             auto added3 = binvar3->add_bounds(lb3,ub3);
5140                                             reindex_vars();
5141 
5142                                             Constraint<> onLink1(pair.first+"_binaryLink1");
5143                                             onLink1 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v1)) - binvar3->in(inst_partition);
5144                                             add(onLink1.in(inst_partition) >= 0);
5145 
5146                                             Constraint<> onLink2(pair.first+"_binaryLink2");
5147                                             onLink2 = on1.in_ignore_ith(nb_entries_v1,1,inst_partition.ignore_ith(0,nb_entries_v1)) - binvar3->in(inst_partition);
5148                                             add(onLink2.in(inst_partition) >= 0);
5149 
5150                                             Constraint<> onLink3(pair.first+"_binaryLink3");
5151                                             onLink3 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v1)) + on1.in_ignore_ith(nb_entries_v1,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - binvar3->in(inst_partition);
5152                                             add(onLink3.in(inst_partition) <= 0);
5153 
5154                                             Constraint<> onSumComb(pair.first+"_binarySum");
5155                                             onSumComb = sum((binvar3->in(added3)).in_matrix(nb_entries,total_entries-nb_entries));
5156                                             add(onSumComb.in(added) == 1);
5157 
5158                                             add_on_off_McCormick_refined(pair.first, vlift->in(added), o1.in(o1_ids), o2.in(o2_ids), binvar3->in(added3));
5159                                         }
5160 
5161                                         else{ //means the combined binary variable has not been used before
5162 
5163                                             var<int> on(name1+name2+"_binary",0,1);
5164                                             add(on.in(inst_partition));
5165 
5166                                             Constraint<> onLink1(pair.first+"_binaryLink1");
5167                                             onLink1 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v1)) - on;
5168                                             add(onLink1.in(inst_partition) >= 0);
5169 
5170                                             Constraint<> onLink2(pair.first+"_binaryLink2");
5171                                             onLink2 = on1.in_ignore_ith(nb_entries_v1,1,inst_partition.ignore_ith(0,nb_entries_v1)) - on;
5172                                             add(onLink2.in(inst_partition) >= 0);
5173 
5174                                             Constraint<> onLink3(pair.first+"_binaryLink3");
5175                                             onLink3 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v1)) + on1.in_ignore_ith(nb_entries_v1,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - on;
5176                                             add(onLink3.in(inst_partition) <= 0);
5177 
5178                                             Constraint<> onSumComb(pair.first+"_binarySum");
5179                                             onSumComb = sum(on.in_matrix(nb_entries,total_entries-nb_entries));
5180                                             add(onSumComb.in(added) == 1);
5181 
5182                                             add_on_off_McCormick_refined(pair.first, vlift->in(added), o1.in(o1_ids), o2.in(o2_ids), on);
5183                                         }
5184                                     }
5185 
5186                                     else{//means it is one of the lambda formulations
5187 
5188                                         //difference is this has one more partition index
5189                                         indices partns1_lambda("partns1_lambda");
5190                                         for (int i = 0; i < num_partns1+1; ++i)
5191                                         {
5192                                             partns1_lambda.add(name1+ "{" +to_string(i+1) + "}");
5193                                         }
5194 
5195                                         indices partns2_lambda("partns2_lambda");
5196                                         for (int i = 0; i < num_partns2+1; ++i)
5197                                         {
5198                                             partns2_lambda.add(name2+ "{" +to_string(i+1) + "}");
5199                                         }
5200 
5201                                         indices partns_lambda("partns_lambda");
5202                                         partns_lambda = indices(partns1_lambda,partns2_lambda);
5203                                         auto inst_partition_lambda = indices(added,partns_lambda);
5204                                         auto inst_partition_bounds1 = indices(added,partns1_lambda);
5205                                         auto inst_partition_bounds2 = indices(added,partns2_lambda);
5206 
5207                                         // Convex combination variables
5208                                         auto lambda_ptr = _vars_name.find(name1+name2+"_lambda");
5209                                         auto lambda = static_pointer_cast<var<double>>(lambda_ptr->second);
5210                                         param<double> lb_lambda("lb_lambda"), ub_lambda("ub_lambda");
5211                                         lb_lambda.in(added,partns_lambda);
5212                                         ub_lambda.in(added,partns_lambda);
5213                                         lb_lambda.set_val(0), ub_lambda.set_val(1);
5214                                         auto added_lambda = lambda->add_bounds(lb_lambda,ub_lambda);
5215                                         reindex_vars();
5216 
5217                                         /** Parameters */
5218                                         // Bounds on variable v1 & v2
5219                                         param<> bounds1(name1+"_bounds1");
5220                                         bounds1.in(inst_partition_bounds1);
5221 
5222                                         param<> bounds2(name2+"_bounds2");
5223                                         bounds2.in(inst_partition_bounds2);
5224 
5225                                         // Function values on the extreme points
5226                                         param<> EP(name1+name2+"_grid_values");
5227                                         EP.in(inst_partition_lambda);
5228                                         auto total_entries = inst_partition_lambda.get_nb_entries();
5229 
5230                                         size_t nb_ins = vlift->in(added).get_nb_inst();
5231                                         auto o1_global_lb = o1.get_lb();
5232                                         auto increment1 = (o1.get_ub() - o1_global_lb)/num_partns1;
5233 
5234                                         auto o2_global_lb = o2.get_lb();
5235                                         auto increment2 = (o2.get_ub() - o2_global_lb)/num_partns2;
5236 
5237                                         // fill bounds and function values
5238                                         for (int i=0 ; i<num_partns1+1; ++i) {
5239                                             auto bound_partn1 = o1_global_lb + increment1*i;
5240                                             bound_partn1.eval_all();
5241                                             auto bound_partn2 = o2_global_lb + increment2*i;
5242                                             bound_partn2.eval_all();
5243                                             for (size_t inst = 0; inst< nb_ins; inst++){
5244                                                 auto cur_var_id = vlift->get_id_inst(inst);
5245                                                 auto cur_var_idx = added._keys->at(cur_var_id);
5246                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
5247                                                 bounds1.set_val(cur_idx,bound_partn1.eval(inst));
5248                                                 bounds2.set_val(cur_idx,bound_partn2.eval(inst));
5249                                                 for(int j=0; j<num_partns2+1; ++j){
5250                                                     auto bound_partn2_temp = o2_global_lb + increment2*j;
5251                                                     bound_partn2_temp.eval_all();
5252                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"}";
5253                                                     EP.set_val(cur_idx,(bound_partn1.eval(inst)*bound_partn2_temp.eval(inst)));
5254                                                 }
5255                                             }
5256                                         }
5257 
5258                                         // Lambda coefficient matrix when linking with partition variables
5259                                         param<> lambda_coef1(name1+"_lambda_linking_coefficients1");
5260                                         param<> lambda_coef2(name2+"_lambda_linking_coefficients2");
5261 
5262                                         // Partition coefficient matrix when linking with lambda variables
5263                                         param<> on_coef1(name1+"_partition_linking_coefficients1");
5264                                         param<> on_coef2(name2+"_partition_linking_coefficients2");
5265 
5266                                         // create constraint indices
5267                                         indices const_idx1("const_idx1");
5268                                         indices const_idx2("const_idx2");
5269 
5270                                         if(model_type == "lambda_II"){
5271 
5272                                             //fill constraint indices
5273                                             for (int i=0; i<num_partns1+1; ++i){
5274                                                 const_idx1.add(to_string(i+1));
5275                                             }
5276 
5277                                             //fill constraint indices
5278                                             for (int i=0; i<num_partns2+1; ++i){
5279                                                 const_idx2.add(to_string(i+1));
5280                                             }
5281 
5282                                             // Lambda coefficient matrix when linking with partition variables
5283                                             lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
5284                                             lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
5285 
5286                                             // Lambda coefficient matrix when linking with partition variables
5287                                             on_coef1.in(indices(added, partns1, const_idx1));
5288                                             on_coef2.in(indices(added, partns2, const_idx2));
5289 
5290                                             // fill lambda_coef1 and lambda_coef2
5291                                             for (size_t inst = 0; inst< nb_ins; inst++){
5292                                                 auto cur_var_id = vlift->get_id_inst(inst);
5293                                                 auto cur_var_idx = added._keys->at(cur_var_id);
5294                                                 for (int i=0 ; i<num_partns1+1; ++i) {
5295                                                     for (int j=0 ; j<num_partns2+1; ++j) {
5296                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
5297                                                         lambda_coef1.set_val(cur_idx,1);
5298                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(j+1);
5299                                                         lambda_coef2.set_val(cur_idx,1);
5300                                                     }
5301                                                 }
5302                                             }
5303 
5304                                             // fill on_coef1 and on_coef2
5305                                             for (size_t inst = 0; inst< nb_ins; inst++){
5306                                                 auto cur_var_id = vlift->get_id_inst(inst);
5307                                                 auto cur_var_idx = added._keys->at(cur_var_id);
5308                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
5309                                                 on_coef1.set_val(cur_idx,1);
5310                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
5311                                                 on_coef2.set_val(cur_idx,1);
5312                                                 for (int i=1 ; i<num_partns1; ++i) {
5313                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i)+"},"+to_string(i+1);
5314                                                     on_coef1.set_val(cur_idx,1);
5315                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
5316                                                     on_coef1.set_val(cur_idx,1);
5317                                                 }
5318                                                 for (int i=1 ; i<num_partns2; ++i) {
5319                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(i)+"},"+to_string(i+1);
5320                                                     on_coef2.set_val(cur_idx,1);
5321                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(i+1);
5322                                                     on_coef2.set_val(cur_idx,1);
5323                                                 }
5324                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1)+"},"+to_string(num_partns1+1);
5325                                                 on_coef1.set_val(cur_idx,1);
5326                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(num_partns2)+"},"+to_string(num_partns2+1);
5327                                                 on_coef2.set_val(cur_idx,1);
5328                                             }
5329                                         }
5330 
5331 
5332                                         else /*means model_type == "lambda_III" */{
5333 
5334                                             //fill constraint indices
5335                                             for (int i=0; i<(num_partns1-2)*2+2; ++i){
5336                                                 const_idx1.add(to_string(i+1));
5337                                             }
5338 
5339                                             //fill constraint indices
5340                                             for (int i=0; i<(num_partns2-2)*2+2; ++i){
5341                                                 const_idx2.add(to_string(i+1));
5342                                             }
5343 
5344                                             // Lambda coefficient matrix when linking with partition variables
5345                                             lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
5346                                             lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
5347 
5348                                             // Partition coefficient matrix when linking with lambda variables
5349                                             on_coef1.in(indices(added, partns1, const_idx1));
5350                                             on_coef2.in(indices(added, partns2, const_idx2));
5351 
5352                                             // fill lambda_coef1 and lambda_coef2
5353                                             for (size_t inst = 0; inst< nb_ins; inst++){
5354                                                 auto cur_var_id = vlift->get_id_inst(inst);
5355                                                 auto cur_var_idx = added._keys->at(cur_var_id);
5356                                                 for (int j=0; j<num_partns2+1; ++j) {
5357                                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(1);
5358                                                     lambda_coef1.set_val(cur_idx,1);
5359                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string((num_partns1-2)*2+2);
5360                                                     lambda_coef1.set_val(cur_idx,1);
5361                                                 }
5362 
5363                                                 for (int i=1 ; i<(num_partns1-2)*2+1; i=i+2) {
5364                                                     for (int j=(i-1)/2 + 2; j<num_partns1+1; ++j) {
5365                                                         for(int k=0; k<num_partns2+1; ++k){
5366                                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+1);
5367                                                             lambda_coef1.set_val(cur_idx,1);
5368                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+2);
5369                                                             lambda_coef1.set_val(cur_idx,-1);
5370                                                         }
5371                                                     }
5372                                                 }
5373 
5374                                                 for (int i=0; i<num_partns1+1; ++i) {
5375                                                     string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(1)+"},"+to_string(1);
5376                                                     lambda_coef2.set_val(cur_idx,1);
5377                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(num_partns2+1)+"},"+to_string((num_partns2-2)*2+2);
5378                                                     lambda_coef2.set_val(cur_idx,1);
5379                                                 }
5380 
5381                                                 for (int i=1 ; i<(num_partns2-2)*2+1; i=i+2) {
5382                                                     for (int j=(i-1)/2 + 2; j<num_partns2+1; ++j) {
5383                                                         for(int k=0; k<num_partns1+1; ++k){
5384                                                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
5385                                                             lambda_coef2.set_val(cur_idx,1);
5386                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
5387                                                             lambda_coef2.set_val(cur_idx,-1);
5388                                                         }
5389                                                     }
5390                                                 }
5391                                             }
5392 
5393 
5394 
5395                                             // fill on_coef1 and on_coef2
5396                                             for (size_t inst = 0; inst< nb_ins; inst++){
5397                                                 auto cur_var_id = vlift->get_id_inst(inst);
5398                                                 auto cur_var_idx = added._keys->at(cur_var_id);
5399                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
5400                                                 on_coef1.set_val(cur_idx,1);
5401 
5402                                                 for (int i=1; i<num_partns1; ++i) {
5403                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(2);
5404                                                     on_coef1.set_val(cur_idx, 1);
5405                                                 }
5406 
5407                                                 for (int i=2 ; i<(num_partns1-2)*2+2; i=i+2) {
5408                                                     for (int j=i/2+1; j<num_partns1; ++j) {
5409                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
5410                                                         on_coef1.set_val(cur_idx,-1);
5411                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
5412                                                         on_coef1.set_val(cur_idx,1);
5413                                                     }
5414                                                 }
5415 
5416                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
5417                                                 on_coef2.set_val(cur_idx,1);
5418                                                 for (int i=1; i<num_partns2; ++i) {
5419                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(2);
5420                                                     on_coef2.set_val(cur_idx, 1);
5421                                                 }
5422 
5423                                                 for (int i=2 ; i<(num_partns2-2)*2+2; i=i+2) {
5424                                                     for (int j=i/2+1; j<num_partns2; ++j) {
5425                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
5426                                                         on_coef2.set_val(cur_idx,-1);
5427                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
5428                                                         on_coef2.set_val(cur_idx,1);
5429                                                     }
5430                                                 }
5431                                             }
5432                                         }
5433 
5434 
5435                                         /** Constraints */
5436                                         // Representation of the bilinear term with convex combination
5437                                         Constraint<> bln_rep(pair.first+"_bln_rep");
5438                                         bln_rep = EP.in_matrix(nb_entries,total_entries-nb_entries)*lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - vlift->in(added);
5439                                         add(bln_rep.in(added) == 0);
5440 
5441                                         // Representation of o1 with convex combination
5442                                         Constraint<> o1_rep(pair.first+"_o1_rep");
5443                                         o1_rep = bounds1.from_ith(0,inst_partition_lambda).in_matrix(nb_entries, 1) * lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - o1.in(o1_ids);
5444                                         add(o1_rep.in(added) == 0);
5445 
5446                                         // Representation of o2 with convex combination
5447                                         Constraint<> o2_rep(pair.first+"_o2_rep");
5448                                         o2_rep = bounds2.in_ignore_ith(nb_entries, 1, inst_partition_lambda).in_matrix(nb_entries,1) * lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - o2.in(o2_ids);
5449                                         add(o2_rep.in(added) == 0);
5450 
5451                                         // Linking partition variables1 with lambda
5452                                         if(model_type == "lambda_II"){
5453                                             Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_II");
5454                                             on_link_lambda1 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
5455                                             add(on_link_lambda1.in(indices(added,const_idx1)) <= 0);
5456 
5457                                             Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_II");
5458                                             on_link_lambda2 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
5459                                             add(on_link_lambda2.in(indices(added,const_idx2)) <= 0);
5460                                         }
5461                                         else{
5462                                             Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_III");
5463                                             on_link_lambda1 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
5464                                             add(on_link_lambda1.in(indices(added,const_idx1)) <= 0);
5465 
5466                                             Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_III");
5467                                             on_link_lambda2 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
5468                                             add(on_link_lambda2.in(indices(added,const_idx2)) <= 0);
5469                                         }
5470                                         // sum over lambda
5471                                         Constraint<> lambdaSum(pair.first+"_lambdaSum");
5472                                         lambdaSum = sum(lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries));
5473                                         add(lambdaSum.in(added) == 1);
5474                                     }
5475 
5476                                 }
5477                                 else{
5478                                     DebugOn("<<<<<<<<<< THIS IS SEEN LIFT -> DOUBLE -> NOT SEEN BINARIES -> DIFF CORE VARS <<<<<<<<<<<" << endl);
5479 
5480                                     var<int> on1(name1+"_binary",0,1);
5481                                     indices partns1("partns1");
5482                                     for (int i = 0; i < num_partns1 ; ++i)
5483                                     {
5484                                         partns1.add(name1+ "{" + to_string(i+1) + "}");
5485                                     }
5486                                     add(on1.in(o1_ids_uq,partns1));
5487 
5488                                     var<int> on2(name2+"_binary",0,1);
5489                                     indices partns2("partns2");
5490                                     for (int i = 0; i < num_partns2 ; ++i)
5491                                     {
5492                                         partns2.add(name2+ "{" + to_string(i+1) + "}");
5493                                     }
5494                                     add(on2.in(o2_ids_uq,partns2));
5495 
5496                                     auto nb_entries_v1 = o1_ids.get_nb_entries();
5497                                     auto nb_entries_v2 = o2_ids.get_nb_entries();
5498                                     auto nb_entries = unique_ids.get_nb_entries();
5499 
5500                                     Constraint<> onSum1(o1._name+"_binarySum");
5501                                     onSum1 = sum(on1.in_matrix(nb_entries_v1,1));
5502                                     add(onSum1.in(o1_ids_uq) == 1);
5503 
5504                                     Constraint<> onSum2(o2._name+"_binarySum");
5505                                     onSum2 = sum(on2.in_matrix(nb_entries_v2,1));
5506                                     add(onSum2.in(o2_ids_uq) == 1);
5507 
5508                                     if(model_type == "on/off"){//if on/off is chosen
5509 
5510                                         indices partns("partns");
5511                                         partns = indices(partns1,partns2);
5512                                         auto inst_partition = indices(added,partns);
5513                                         auto total_entries = inst_partition.get_nb_entries();
5514 
5515                                         if(binvar_ptr3 !=_vars_name.end()){ //means the combined binary variable has been used before
5516                                             auto binvar3 = static_pointer_cast<var<int>>(binvar_ptr3->second);
5517                                             param<int> lb3("lb3"), ub3("ub3");
5518                                             lb3.in(added,partns);
5519                                             ub3.in(added,partns);
5520                                             lb3.set_val(0), ub3.set_val(1);
5521                                             auto added3 = binvar3->add_bounds(lb3,ub3);
5522                                             reindex_vars();
5523 
5524                                             Constraint<> onLink1(pair.first+"_binaryLink1");
5525                                             onLink1 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) - binvar3->in(inst_partition);
5526                                             add(onLink1.in(inst_partition) >= 0);
5527 
5528                                             Constraint<> onLink2(pair.first+"_binaryLink2");
5529                                             onLink2 = on2.in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - binvar3->in(inst_partition);
5530                                             add(onLink2.in(inst_partition) >= 0);
5531 
5532                                             Constraint<> onLink3(pair.first+"_binaryLink3");
5533                                             onLink3 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) + on2.in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - binvar3->in(inst_partition);
5534                                             add(onLink3.in(inst_partition) <= 0);
5535 
5536                                             Constraint<> onSumComb(pair.first+"_binarySum");
5537                                             onSumComb = sum((binvar3->in(added3)).in_matrix(nb_entries,total_entries-nb_entries));
5538                                             add(onSumComb.in(added) == 1);
5539 
5540                                             add_on_off_McCormick_refined(pair.first, vlift->in(added), o1.in(o1_ids), o2.in(o2_ids), binvar3->in(added3));
5541                                         }
5542                                         else{ //means the combined binary variable has not been used before
5543                                             var<int> on(name1+name2+"_binary",0,1);
5544                                             add(on.in(inst_partition));
5545 
5546                                             Constraint<> onLink1(pair.first+"_binaryLink1");
5547                                             onLink1 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) - on;
5548                                             add(onLink1.in(inst_partition) >= 0);
5549 
5550                                             Constraint<> onLink2(pair.first+"_binaryLink2");
5551                                             onLink2 = on2.in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - on;
5552                                             add(onLink2.in(inst_partition) >= 0);
5553 
5554                                             Constraint<> onLink3(pair.first+"_binaryLink3");
5555                                             onLink3 = on1.from_ith(0,inst_partition.ignore_ith(nb_entries_v1, nb_entries_v2)) + on2.in_ignore_ith(nb_entries_v2,1,inst_partition.ignore_ith(0,nb_entries_v1)) - 1 - on;
5556                                             add(onLink3.in(inst_partition) <= 0);
5557 
5558                                             Constraint<> onSumComb(pair.first+"_binarySum");
5559                                             onSumComb = sum(on.in_matrix(nb_entries,total_entries-nb_entries));
5560                                             add(onSumComb.in(added) == 1);
5561 
5562                                             add_on_off_McCormick_refined(pair.first, vlift->in(added), o1.in(o1_ids), o2.in(o2_ids), on);
5563                                         }
5564                                     }
5565 
5566                                     else{//means it is one of the lambda formulations
5567 
5568                                         //difference is this has one more partition index
5569                                         indices partns1_lambda("partns1_lambda");
5570                                         for (int i = 0; i < num_partns1+1; ++i)
5571                                         {
5572                                             partns1_lambda.add(name1+ "{" +to_string(i+1) + "}");
5573                                         }
5574 
5575                                         indices partns2_lambda("partns2_lambda");
5576                                         for (int i = 0; i < num_partns2+1; ++i)
5577                                         {
5578                                             partns2_lambda.add(name2+ "{" +to_string(i+1) + "}");
5579                                         }
5580 
5581                                         indices partns_lambda("partns_lambda");
5582                                         partns_lambda = indices(partns1_lambda,partns2_lambda);
5583                                         auto inst_partition_lambda = indices(added,partns_lambda);
5584                                         auto inst_partition_bounds1 = indices(added,partns1_lambda);
5585                                         auto inst_partition_bounds2 = indices(added,partns2_lambda);
5586 
5587                                         // Convex combination variables
5588                                         auto lambda_ptr = _vars_name.find(name1+name2+"_lambda");
5589                                         auto lambda = static_pointer_cast<var<double>>(lambda_ptr->second);
5590                                         param<double> lb_lambda("lb_lambda"), ub_lambda("ub_lambda");
5591                                         lb_lambda.in(added,partns_lambda);
5592                                         ub_lambda.in(added,partns_lambda);
5593                                         lb_lambda.set_val(0), ub_lambda.set_val(1);
5594                                         auto added_lambda = lambda->add_bounds(lb_lambda,ub_lambda);
5595                                         reindex_vars();
5596 
5597                                         /** Parameters */
5598                                         // Bounds on variable v1 & v2
5599                                         param<> bounds1(name1+"_bounds1");
5600                                         bounds1.in(inst_partition_bounds1);
5601 
5602                                         param<> bounds2(name2+"_bounds2");
5603                                         bounds2.in(inst_partition_bounds2);
5604 
5605                                         // Function values on the extreme points
5606                                         param<> EP(name1+name2+"_grid_values");
5607                                         EP.in(inst_partition_lambda);
5608                                         auto total_entries = inst_partition_lambda.get_nb_entries();
5609 
5610                                         size_t nb_ins = vlift->in(added).get_nb_inst();
5611                                         auto o1_global_lb = o1.get_lb();
5612                                         auto increment1 = (o1.get_ub() - o1_global_lb)/num_partns1;
5613 
5614                                         auto o2_global_lb = o2.get_lb();
5615                                         auto increment2 = (o2.get_ub() - o2_global_lb)/num_partns2;
5616 
5617                                         // fill bounds1 and function values
5618                                         for (int i=0 ; i<num_partns1+1; ++i) {
5619                                             auto bound_partn1 = o1_global_lb + increment1*i;
5620                                             bound_partn1.eval_all();
5621                                             for (size_t inst = 0; inst< nb_ins; inst++){
5622                                                 auto cur_var_id = vlift->get_id_inst(inst);
5623                                                 auto cur_var_idx = added._keys->at(cur_var_id);
5624                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
5625                                                 bounds1.set_val(cur_idx,bound_partn1.eval(inst));
5626                                                 for(int j=0; j<num_partns2+1; ++j){
5627                                                     auto bound_partn2 = o2_global_lb + increment2*j;
5628                                                     bound_partn2.eval_all();
5629                                                     cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"}";
5630                                                     EP.set_val(cur_idx,(bound_partn1.eval(inst)*bound_partn2.eval(inst)));
5631                                                 }
5632                                             }
5633                                         }
5634                                         // fill bounds2
5635                                         for (int i=0 ; i<num_partns2+1; ++i) {
5636                                             auto bound_partn2 = o2_global_lb + increment2*i;
5637                                             bound_partn2.eval_all();
5638                                             for (size_t inst = 0; inst< nb_ins; inst++){
5639                                                 auto cur_var_id = vlift->get_id_inst(inst);
5640                                                 auto cur_var_idx = added._keys->at(cur_var_id);
5641                                                 string cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"}";
5642                                                 bounds2.set_val(cur_idx,bound_partn2.eval(inst));
5643                                             }
5644                                         }
5645 
5646                                         // Lambda coefficient matrix when linking with partition variables
5647                                         param<> lambda_coef1(name1+"_lambda_linking_coefficients1");
5648                                         param<> lambda_coef2(name2+"_lambda_linking_coefficients2");
5649 
5650                                         // Partition coefficient matrix when linking with lambda variables
5651                                         param<> on_coef1(name1+"_partition_linking_coefficients1");
5652                                         param<> on_coef2(name2+"_partition_linking_coefficients2");
5653 
5654                                         // create constraint indices
5655                                         indices const_idx1("const_idx1");
5656                                         indices const_idx2("const_idx2");
5657 
5658                                         if(model_type == "lambda_II"){
5659 
5660                                             //fill constraint indices
5661                                             for (int i=0; i<num_partns1+1; ++i){
5662                                                 const_idx1.add(to_string(i+1));
5663                                             }
5664 
5665                                             //fill constraint indices
5666                                             for (int i=0; i<num_partns2+1; ++i){
5667                                                 const_idx2.add(to_string(i+1));
5668                                             }
5669 
5670                                             // Lambda coefficient matrix when linking with partition variables
5671                                             if(num_partns1 > 1) lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
5672                                             if(num_partns2 > 1) lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
5673 
5674                                             // Lambda coefficient matrix when linking with partition variables
5675                                             if(num_partns1 > 1) on_coef1.in(indices(added, partns1, const_idx1));
5676                                             if(num_partns2 > 1) on_coef2.in(indices(added, partns2, const_idx2));
5677 
5678                                             // fill lambda_coef1 and lambda_coef2
5679                                             for (size_t inst = 0; inst< nb_ins; inst++){
5680                                                 auto cur_var_id = vlift->get_id_inst(inst);
5681                                                 auto cur_var_idx = added._keys->at(cur_var_id);
5682                                                 for (int i=0 ; i<num_partns1+1; ++i) {
5683                                                     for (int j=0 ; j<num_partns2+1; ++j) {
5684                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
5685                                                         if(num_partns1 > 1) lambda_coef1.set_val(cur_idx,1);
5686                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(j+1);
5687                                                         if(num_partns2 > 1) lambda_coef2.set_val(cur_idx,1);
5688                                                     }
5689                                                 }
5690                                             }
5691 
5692                                             // fill on_coef1 and on_coef2
5693                                             for (size_t inst = 0; inst< nb_ins; inst++){
5694                                                 auto cur_var_id = vlift->get_id_inst(inst);
5695                                                 auto cur_var_idx = added._keys->at(cur_var_id);
5696                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
5697                                                 if(num_partns1 > 1) on_coef1.set_val(cur_idx,1);
5698                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
5699                                                 if(num_partns2 > 1) on_coef2.set_val(cur_idx,1);
5700                                                 if(num_partns1 > 1) {
5701                                                     for (int i=1 ; i<num_partns1; ++i) {
5702                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i)+"},"+to_string(i+1);
5703                                                         on_coef1.set_val(cur_idx,1);
5704                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(i+1);
5705                                                         on_coef1.set_val(cur_idx,1);
5706                                                     }
5707                                                 }
5708                                                 if(num_partns2 > 1) {
5709                                                     for (int i=1 ; i<num_partns2; ++i) {
5710                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(i)+"},"+to_string(i+1);
5711                                                         on_coef2.set_val(cur_idx,1);
5712                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(i+1);
5713                                                         on_coef2.set_val(cur_idx,1);
5714                                                     }
5715                                                 }
5716                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1)+"},"+to_string(num_partns1+1);
5717                                                 if(num_partns1 > 1) on_coef1.set_val(cur_idx,1);
5718                                                 cur_idx = cur_var_idx+","+name2+"{"+to_string(num_partns2)+"},"+to_string(num_partns2+1);
5719                                                 if(num_partns2 > 1) on_coef2.set_val(cur_idx,1);
5720                                             }
5721                                         }
5722 
5723 
5724                                         else /*means model_type == "lambda_III" */{
5725 
5726                                             //fill constraint indices
5727                                             for (int i=0; i<(num_partns1-2)*2+2; ++i){
5728                                                 const_idx1.add(to_string(i+1));
5729                                             }
5730 
5731                                             //fill constraint indices
5732                                             for (int i=0; i<(num_partns2-2)*2+2; ++i){
5733                                                 const_idx2.add(to_string(i+1));
5734                                             }
5735 
5736                                             // Lambda coefficient matrix when linking with partition variables
5737                                             if(num_partns1 > 1) lambda_coef1.in(indices(inst_partition_lambda, const_idx1));
5738                                             if(num_partns2 > 1) lambda_coef2.in(indices(inst_partition_lambda, const_idx2));
5739 
5740                                             // Partition coefficient matrix when linking with lambda variables
5741                                             if(num_partns1 > 1) on_coef1.in(indices(added, partns1, const_idx1));
5742                                             if(num_partns2 > 1) on_coef2.in(indices(added, partns2, const_idx2));
5743 
5744                                             // fill lambda_coef1 and lambda_coef2
5745                                             for (size_t inst = 0; inst< nb_ins; inst++){
5746                                                 auto cur_var_id = vlift->get_id_inst(inst);
5747                                                 auto cur_var_idx = added._keys->at(cur_var_id);
5748                                                 if(num_partns1 > 1) {
5749                                                     for (int j=0; j<num_partns2+1; ++j) {
5750                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(1);
5751                                                         lambda_coef1.set_val(cur_idx,1);
5752                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(num_partns1+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string((num_partns1-2)*2+2);
5753                                                         lambda_coef1.set_val(cur_idx,1);
5754                                                     }
5755 
5756                                                     for (int i=1 ; i<(num_partns1-2)*2+1; i=i+2) {
5757                                                         for (int j=(i-1)/2 + 2; j<num_partns1+1; ++j) {
5758                                                             for(int k=0; k<num_partns2+1; ++k){
5759                                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+1);
5760                                                                 lambda_coef1.set_val(cur_idx,1);
5761                                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+name2+"{"+to_string(k+1)+"},"+to_string(i+2);
5762                                                                 lambda_coef1.set_val(cur_idx,-1);
5763                                                             }
5764                                                         }
5765                                                     }
5766                                                 }
5767                                                 if(num_partns2 > 1) {
5768                                                     for (int i=0; i<num_partns1+1; ++i) {
5769                                                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(1)+"},"+to_string(1);
5770                                                         lambda_coef2.set_val(cur_idx,1);
5771                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(num_partns2+1)+"},"+to_string((num_partns2-2)*2+2);
5772                                                         lambda_coef2.set_val(cur_idx,1);
5773                                                     }
5774 
5775                                                     for (int i=1 ; i<(num_partns2-2)*2+1; i=i+2) {
5776                                                         for (int j=(i-1)/2 + 2; j<num_partns2+1; ++j) {
5777                                                             for(int k=0; k<num_partns1+1; ++k){
5778                                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
5779                                                                 lambda_coef2.set_val(cur_idx,1);
5780                                                                 cur_idx = cur_var_idx+","+name1+"{"+to_string(k+1)+"},"+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
5781                                                                 lambda_coef2.set_val(cur_idx,-1);
5782                                                             }
5783                                                         }
5784                                                     }
5785                                                 }
5786                                             }
5787 
5788 
5789 
5790                                             // fill on_coef1 and on_coef2
5791                                             for (size_t inst = 0; inst< nb_ins; inst++){
5792                                                 auto cur_var_id = vlift->get_id_inst(inst);
5793                                                 auto cur_var_idx = added._keys->at(cur_var_id);
5794                                                 string cur_idx = cur_var_idx+","+name1+"{"+to_string(1)+"},"+to_string(1);
5795                                                 if(num_partns1 > 1) {
5796                                                     on_coef1.set_val(cur_idx,1);
5797 
5798                                                     for (int i=1; i<num_partns1; ++i) {
5799                                                         cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+to_string(2);
5800                                                         on_coef1.set_val(cur_idx, 1);
5801                                                     }
5802 
5803                                                     for (int i=2 ; i<(num_partns1-2)*2+2; i=i+2) {
5804                                                         for (int j=i/2+1; j<num_partns1; ++j) {
5805                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+1);
5806                                                             on_coef1.set_val(cur_idx,-1);
5807                                                             cur_idx = cur_var_idx+","+name1+"{"+to_string(j+1)+"},"+to_string(i+2);
5808                                                             on_coef1.set_val(cur_idx,1);
5809                                                         }
5810                                                     }
5811                                                 }
5812                                                 if(num_partns2 > 1) {
5813                                                     cur_idx = cur_var_idx+","+name2+"{"+to_string(1)+"},"+to_string(1);
5814                                                     on_coef2.set_val(cur_idx,1);
5815                                                     for (int i=1; i<num_partns2; ++i) {
5816                                                         cur_idx = cur_var_idx+","+name2+"{"+to_string(i+1)+"},"+to_string(2);
5817                                                         on_coef2.set_val(cur_idx, 1);
5818                                                     }
5819 
5820                                                     for (int i=2 ; i<(num_partns2-2)*2+2; i=i+2) {
5821                                                         for (int j=i/2+1; j<num_partns2; ++j) {
5822                                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+1);
5823                                                             on_coef2.set_val(cur_idx,-1);
5824                                                             cur_idx = cur_var_idx+","+name2+"{"+to_string(j+1)+"},"+to_string(i+2);
5825                                                             on_coef2.set_val(cur_idx,1);
5826                                                         }
5827                                                     }
5828                                                 }
5829                                             }
5830                                         }
5831 
5832 
5833                                         /** Constraints */
5834                                         // Representation of the bilinear term with convex combination
5835                                         Constraint<> bln_rep(pair.first+"_bln_rep");
5836                                         bln_rep = EP.in_matrix(nb_entries,total_entries-nb_entries)*lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - vlift->in(added);
5837                                         add(bln_rep.in(added) == 0);
5838 
5839                                         // Representation of o1 with convex combination
5840                                         Constraint<> o1_rep(pair.first+"_o1_rep");
5841                                         o1_rep = bounds1.from_ith(0,inst_partition_lambda).in_matrix(nb_entries, 1) * lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - o1.in(o1_ids);
5842                                         add(o1_rep.in(added) == 0);
5843 
5844                                         // Representation of o2 with convex combination
5845                                         Constraint<> o2_rep(pair.first+"_o2_rep");
5846                                         o2_rep = bounds2.in_ignore_ith(nb_entries, 1, inst_partition_lambda).in_matrix(nb_entries,1) * lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries) - o2.in(o2_ids);
5847                                         add(o2_rep.in(added) == 0);
5848 
5849                                         // Linking partition variables1 with lambda
5850                                         if(model_type == "lambda_II"){
5851                                             if(num_partns1 > 1) {
5852                                                 Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_II");
5853                                                 on_link_lambda1 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
5854                                                 add(on_link_lambda1.in(indices(added,const_idx1)) <= 0);
5855                                             }
5856                                             if(num_partns2 > 1) {
5857                                                 Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_II");
5858                                                 on_link_lambda2 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - on2.in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
5859                                                 add(on_link_lambda2.in(indices(added,const_idx2)) <= 0);
5860                                             }
5861                                         }
5862                                         else{
5863                                             if(num_partns1 > 1) {
5864                                                 Constraint<> on_link_lambda1(pair.first+"_on_link_lambda1_III");
5865                                                 on_link_lambda1 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef1.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef1.in_matrix(nb_entries,total_entries-nb_entries) - on1.in_ignore_ith(nb_entries_v1,nb_entries_v2,on_coef1.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef1.in_matrix(nb_entries,1);
5866                                                 add(on_link_lambda1.in(indices(added,const_idx1)) <= 0);
5867                                             }
5868                                             if(num_partns2 > 1) {
5869                                                 Constraint<> on_link_lambda2(pair.first+"_on_link_lambda2_III");
5870                                                 on_link_lambda2 = lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries).from_ith(0,lambda_coef2.get_matrix_ids(nb_entries,total_entries-nb_entries))*lambda_coef2.in_matrix(nb_entries,total_entries-nb_entries) - on2.in_ignore_ith(0,nb_entries_v1,on_coef2.get_matrix_ids(nb_entries,1).from_ith(0,nb_entries+1)) * on_coef2.in_matrix(nb_entries,1);
5871                                                 add(on_link_lambda2.in(indices(added,const_idx2)) <= 0);
5872                                             }
5873                                         }
5874                                         // sum over lambda
5875                                         Constraint<> lambdaSum(pair.first+"_lambdaSum");
5876                                         lambdaSum = sum(lambda->in(added_lambda).in_matrix(nb_entries,total_entries-nb_entries));
5877                                         add(lambdaSum.in(added) == 1);
5878                                     }
5879                                 }
5880                             }
5881                         }
5882 #endif
5883                     }
5884                     else {
5885                         add_McCormick(pair.first, vlift->in(added), o1.in(o1_ids), o2.in(o2_ids));
5886                     }
5887                 }
5888             }
5889 
5890             lifted.insert(lt);
5891         }
5892         for (auto &pair:*c._pterms) {
5893             auto term = pair.second;
5894             lterm lt;
5895             lt._sign = term._sign;
5896             if (term._coef->is_function()) {
5897                 auto coef = *static_pointer_cast<func<type>>(term._coef);
5898                 lt._coef = func<type>(coef).copy();
5899             }
5900             else if(term._coef->is_param()) {
5901                 auto coef = *static_pointer_cast<param<type>>(term._coef);
5902                 lt._coef = param<type>(coef).copy();
5903             }
5904             else if(term._coef->is_number()) {
5905                 auto coef = *static_pointer_cast<constant<type>>(term._coef);
5906                 lt._coef = constant<type>(coef).copy();
5907             }
5908             func<type> prod = 1;
5909             string prod_name = "Lift(";
5910             auto list = pair.second._l;
5911             for (auto &ppi: *list) {
5912                 auto p = ppi.first;
5913                 auto orig_var = *static_pointer_cast<var<type>>(p);
5914                 if(ppi.second>1){
5915                     prod_name += orig_var.get_name(true,true)+"("+orig_var._indices->get_name()+")^"+to_string(ppi.second);
5916                     //TODO Lift univarite power function
5917                 }
5918                 else{
5919                     prod_name += orig_var.get_name(true,true)+"("+orig_var._indices->get_name()+")";
5920                 }
5921                 prod *= pow(orig_var,ppi.second);
5922             }
5923             prod_name += ")";
5924 
5925             auto ids = *c._indices;
5926             param<type> lb("lb"), ub("ub");
5927             lb.in(ids);ub.in(ids);
5928             lb.set_val(prod._range->first);
5929             ub.set_val(prod._range->second);
5930             var<type> vlift(prod_name, lb, ub);
5931             auto it = _vars_name.find(prod_name);
5932             if(it==_vars_name.end()){
5933                 vlift._lift=true;
5934                 add(vlift.in(ids));
5935                 lt._p = make_shared<var<type>>(vlift);
5936             }
5937             else {
5938                 vlift = *static_pointer_cast<var<type>>(it->second);
5939                 lt._p = make_shared<var<type>>(vlift);
5940             }
5941             lifted.insert(lt);
5942         }
5943 
5944         lifted._range = c._range;
5945         lifted._all_convexity = linear_;
5946         lifted._all_sign = c._all_sign;
5947         lifted._ftype = lin_;
5948         lifted._ctype = c._ctype;
5949         lifted._indices = c._indices;
5950         lifted._dim[0] = c._dim[0];
5951         lifted._dim[1] = c._dim[1];
5952         return lifted;
5953     }
5954 
5955     template <typename type>
5956     template<typename T,
5957     typename std::enable_if<is_same<T,double>::value>::type*>
run_obbt(shared_ptr<Model<T>> relaxed_model,double max_time,unsigned max_iter,double rel_tol,double abs_tol,unsigned nb_threads,SolverType ub_solver_type,SolverType lb_solver_type,double ub_solver_tol,double lb_solver_tol,double range_tol,bool linearize)5958     std::tuple<bool,int,double,double,double,double,double,double> Model<type>::run_obbt(shared_ptr<Model<T>> relaxed_model, double max_time, unsigned max_iter, double rel_tol, double abs_tol, unsigned nb_threads, SolverType ub_solver_type, SolverType lb_solver_type, double ub_solver_tol, double lb_solver_tol, double range_tol, bool linearize) {
5959         std::tuple<bool,int,double,double,double,double,double,double> res;
5960         int total_iter=0, global_iter=1;
5961         int output;
5962         double total_time =0, time_start = get_wall_time(), time_end = 0, lower_bound_nonlin_init = numeric_limits<double>::lowest();
5963         solver<> UB_solver(*this,ub_solver_type);
5964         UB_solver.run(output = 5, ub_solver_tol);
5965         DebugOn("Upper bound = "<<this->get_obj_val()<<endl);
5966         solver<> LBnonlin_solver(relaxed_model,lb_solver_type);
5967         if(!linearize)
5968             LBnonlin_solver.set_option("bound_relax_factor", lb_solver_tol*1e-2);
5969         else
5970             LBnonlin_solver.set_option("bound_relax_factor", lb_solver_tol*0.9e-1);
5971         LBnonlin_solver.run(output = 5, lb_solver_tol);
5972         if(relaxed_model->_status==0)
5973         {
5974             lower_bound_nonlin_init = relaxed_model->get_obj_val();
5975             DebugOn("Initial lower bound = "<<this->get_obj_val()<<endl);
5976         }
5977         shared_ptr<Model<>> obbt_model=relaxed_model;
5978         Model<> interior_model;
5979         if(linearize){
5980             auto lin_model=relaxed_model->buildOA();
5981             interior_model=lin_model->add_outer_app_solution(*relaxed_model);
5982             obbt_model=lin_model;
5983         }
5984 
5985         auto status = run_obbt_one_iteration(relaxed_model, max_time, max_iter, rel_tol, abs_tol, nb_threads, ub_solver_type, lb_solver_type, ub_solver_tol, lb_solver_tol, range_tol, linearize, obbt_model, interior_model);
5986         double upper_bound = get<5>(status);
5987 
5988         total_iter += get<1>(status);
5989         auto lower_bound=obbt_model->get_obj_val();
5990         auto gap = (upper_bound - lower_bound)/std::abs(upper_bound);
5991         while(get<1>(status)>1 && (gap > rel_tol || (upper_bound-lower_bound)>abs_tol)){
5992             if(total_iter>= max_iter)
5993                 break;
5994             status = run_obbt_one_iteration(relaxed_model, max_time, max_iter, rel_tol, abs_tol, nb_threads, ub_solver_type, lb_solver_type, ub_solver_tol, lb_solver_tol, range_tol, linearize, obbt_model, interior_model);
5995             total_iter += get<1>(status);
5996             if(get<1>(status)>0)
5997                 global_iter++;
5998             //        obbt_model->print();
5999 
6000         }
6001         time_end = get_wall_time();
6002         total_time = time_end - time_start;
6003         get<0>(res)=get<0>(status);
6004         get<1>(res)=total_iter;
6005         get<2>(res)=total_time;
6006         get<3>(res)=lower_bound_nonlin_init;
6007         get<4>(res)=get<4>(status);
6008         get<5>(res)=get<5>(status);
6009         get<6>(res)=get<6>(status);
6010         get<7>(res)=get<7>(status);
6011         upper_bound=get<5>(status);
6012         DebugOn("Total wall-clock time spent in OBBT = " << total_time << endl);
6013         DebugOn("Total number of OBBT iterations = " << total_iter << endl);
6014         DebugOn("Number of global iterations = " << global_iter << endl);
6015         auto gapnl=(upper_bound-lower_bound_nonlin_init)/std::abs(upper_bound)*100;
6016         DebugOn("Initial gap = "<<gapnl<<"%"<<endl);
6017         auto lower_bound_final=obbt_model->get_obj_val();
6018         auto gap_final = 100*(upper_bound - lower_bound_final)/std::abs(upper_bound);
6019         DebugOn("Final gap = " << to_string(gap_final) << "%."<<endl);
6020         return res;
6021     }
6022 
6023     template <typename type>
6024     template<typename T,
6025     typename std::enable_if<is_same<T,double>::value>::type*>
run_obbt_one_iteration(shared_ptr<Model<T>> relaxed_model,double max_time,unsigned max_iter,double rel_tol,double abs_tol,unsigned nb_threads,SolverType ub_solver_type,SolverType lb_solver_type,double ub_solver_tol,double lb_solver_tol,double range_tol,bool linearize,shared_ptr<Model<T>> obbt_model,Model<T> & interior_model)6026     std::tuple<bool,int,double,double,double,double,double,double> Model<type>::run_obbt_one_iteration(shared_ptr<Model<T>> relaxed_model, double max_time, unsigned max_iter, double rel_tol, double abs_tol, unsigned nb_threads, SolverType ub_solver_type, SolverType lb_solver_type, double ub_solver_tol, double lb_solver_tol, double range_tol, bool linearize, shared_ptr<Model<T>> obbt_model, Model<T> & interior_model) {
6027 
6028         std::tuple<bool,int,double, double, double, double, double, double> res;
6029 #ifdef USE_MPI
6030         int worker_id, nb_workers;
6031         auto err_rank = MPI_Comm_rank(MPI_COMM_WORLD, &worker_id);
6032         auto err_size = MPI_Comm_size(MPI_COMM_WORLD, &nb_workers);
6033 #endif
6034         int nb_total_threads = nb_threads; /** Used when MPI is ON to multiply with the number of workers */
6035 #ifdef USE_MPI
6036         nb_total_threads *= nb_workers;
6037 #endif
6038         vector<shared_ptr<Model<>>> batch_models;
6039         map<string, bool> fixed_point;
6040         map<string, double> interval_original, interval_new, ub_original, lb_original;
6041         string var_key,var_key_k,key_lb,key_ub, key_lb_k, key_ub_k;
6042         string vname;
6043         string mname, mkname, vkname, keyk, dirk;
6044         string var_vp_key, vp_key_lb, vp_key_ub;
6045         string dir_array[2]={"LB", "UB"};
6046         var<> vark, vk, v, var_ub;
6047         double boundk1, objk, left, right, mid, temp, tempa;
6048         bool terminate=false;
6049         bool break_flag=false, time_limit = false, close=false;
6050         bool xb_true=true;
6051         double sum=0, avg=0, num_var=0.0;
6052         const double fixed_tol_abs=1e-3, fixed_tol_rel=1e-3, zero_tol=1e-6;
6053         int gap_count_int=1, iter=0;
6054         int output = 0;
6055         int batch_model_count=0;
6056         double solver_time =0, solver_time_end, gapnl,gap, solver_time_start = get_wall_time();
6057         vector<double> ub_sol;
6058         shared_ptr<map<string,size_t>> p_map;
6059         /* Running upper and lower bound solvers */
6060         vector<double> obbt_solution(relaxed_model->_nb_vars);
6061         double lower_bound_nonlin_init = numeric_limits<double>::min(), lower_bound_init = numeric_limits<double>::min(), upper_bound = 0, lower_bound = numeric_limits<double>::min();
6062         if(relaxed_model->_status==0)
6063         {
6064             /* Check if gap is already not zero at root node */
6065             lower_bound_nonlin_init=relaxed_model->get_obj_val();
6066             lower_bound_init = obbt_model->get_obj_val();
6067             lower_bound = lower_bound_init;
6068             upper_bound=this->get_obj_val();
6069             get_solution(ub_sol);/* store current solution */
6070             gapnl=(upper_bound-lower_bound_nonlin_init)/std::abs(upper_bound)*100;
6071             DebugOn("Initial nolinear gap = "<<gapnl<<"%"<<endl);
6072             if ((upper_bound-lower_bound_init)>=abs_tol || (upper_bound-lower_bound_init)/(std::abs(upper_bound)+zero_tol)>=rel_tol)
6073             {
6074                 /* Add the upper bound constraint on the objective */
6075                 if(linearize){
6076                     obbt_model->_first_run = true;
6077                     solver<> LB_solver(obbt_model,lb_solver_type);
6078                     LB_solver.run(output = 0, lb_solver_tol);
6079                     lower_bound_init=obbt_model->get_obj_val();
6080                     auto gaplin=(upper_bound-lower_bound_init)/std::abs(upper_bound)*100;
6081                     // obbt_model->print();
6082                     DebugOn("Initial linear gap = "<<gaplin<<"%"<<endl);
6083                 }
6084                 param<> ub("ub");
6085                 ub = this->get_obj_val();
6086                 auto obj = *obbt_model->_obj;
6087                 Constraint<type> obj_ub("obj|ub");
6088                 obj_ub = obj - ub;
6089                 obbt_model->add(obj_ub<=0);
6090 
6091 
6092                 /**/
6093                 terminate=false;
6094                 for(auto &it:obbt_model->_vars_name)
6095                 {
6096                     string vname=it.first;
6097                     v=obbt_model->template get_var<double>(vname);
6098                     auto v_keys=v.get_keys();
6099                     auto v_key_map=v.get_keys_map();
6100                     for(auto &key: *v_keys)
6101                     {
6102                         var_key = vname+"|"+ key;
6103                         key_lb= var_key +"|LB";
6104                         key_ub= var_key +"|UB";
6105                         /* Do not do OBBT on lifted variables */
6106                         if(v._lift){
6107                             fixed_point[key_lb]=true;
6108                             fixed_point[key_ub]=true;
6109                             DebugOff("Skipping OBBT for "<<vname<<"\t"<<key<<endl);
6110                         }
6111                         else{
6112                             fixed_point[key_lb]=false;
6113                             fixed_point[key_ub]=false;
6114                         }
6115                         auto key_pos=v_key_map->at(key);
6116 
6117                         if(v._off[key_pos]==true)
6118                         {
6119                             fixed_point[key_lb]=true;
6120                             fixed_point[key_ub]=true;
6121                             DebugOff("Off var: "<<vname<<"\t"<<key<<endl);
6122                         }
6123 
6124                         interval_original[var_key]=v.get_ub(key)-v.get_lb(key);
6125                         ub_original[var_key]=v.get_ub(key);
6126                         lb_original[var_key]=v.get_lb(key);
6127                         interval_new[var_key]=v.get_ub(key)-v.get_lb(key);
6128 
6129                     }
6130 
6131                 }
6132                 solver_time= get_wall_time()-solver_time_start;
6133                 for(auto i=0;i<nb_total_threads;i++){
6134                     auto modelk = obbt_model->copy();
6135                     batch_models.push_back(modelk);
6136                 }
6137                 while(solver_time<=max_time && !terminate && iter<max_iter)
6138                 {
6139                     iter++;
6140                     terminate=true;
6141                     for (auto it=obbt_model->_vars_name.begin(); it!=obbt_model->_vars_name.end(); it++)
6142                     {
6143                         vname=it->first;
6144                         v = obbt_model->template get_var<double>(vname);
6145                         auto v_keys=v.get_keys();
6146                         for(auto it_key=v.get_keys()->begin(); it_key!=v.get_keys()->end(); it_key++)
6147                         {
6148                             auto key = *it_key;
6149                             solver_time_end=get_wall_time();
6150                             solver_time= solver_time_end-solver_time_start;
6151                             if(solver_time>=max_time)
6152                             {
6153                                 break_flag=true;
6154                                 time_limit = true;
6155                                 break;
6156                             }
6157                             var_key=vname+"|"+ key;
6158                             key_lb= var_key +"|LB";
6159                             key_ub= var_key +"|UB";
6160                             interval_new[var_key]=v.get_ub(key)-v.get_lb(key);
6161                             if(std::abs(v.get_ub(key)-v.get_lb(key))<=range_tol)
6162                             {
6163                                 fixed_point[key_lb]=true;
6164                                 fixed_point[key_ub]=true;
6165 
6166                             }
6167                             /* Add to batch if not reached fixed point, or if we're at the last key of the last variable */
6168                             if(fixed_point[key_lb]==false || fixed_point[key_ub]==false || (next(it)==obbt_model->_vars_name.end() && next(it_key)==v.get_keys()->end()))
6169                             {
6170                                 /* Loop on Min/Max, upper bound and lower bound */
6171                                 for(auto &dir: dir_array)
6172                                 {
6173                                     mname=vname+"|"+key+"|"+dir;
6174                                     if(fixed_point[mname]==false){
6175                                         batch_models[batch_model_count]->set_name(mname);
6176                                         vark=batch_models[batch_model_count]->template get_var<T>(vname);
6177                                         vark.initialize_midpoint();
6178                                         if(dir=="LB")
6179                                         {
6180                                             batch_models[batch_model_count]->min(vark(key));
6181                                         }
6182                                         else
6183                                         {
6184                                             batch_models[batch_model_count]->max(vark(key));
6185 
6186                                         }
6187                                         batch_models[batch_model_count++]->reindex();
6188                                         //                                modelk->print();
6189 
6190                                     }
6191                                     /* When batch models has reached size of nb_threads or when at the last key of last variable */
6192                                     if (batch_model_count==nb_total_threads || (next(it)==obbt_model->_vars_name.end() && next(it_key)==v.get_keys()->end() && dir=="UB"))
6193                                     {
6194                                         double batch_time_start = get_wall_time();
6195 #ifdef USE_MPI
6196                                         run_MPI(batch_models,lb_solver_type,lb_solver_tol,nb_threads,"ma27",2000,2000, false,true);
6197 #else
6198                                         run_parallel(batch_models,lb_solver_type,lb_solver_tol,nb_threads, 2000);
6199 #endif
6200                                         double batch_time_end = get_wall_time();
6201                                         auto batch_time = batch_time_end - batch_time_start;
6202                                         DebugOn("Done running batch models, solve time = " << to_string(batch_time) << endl);
6203                                         auto model_count=0;
6204                                         for (auto &model:batch_models)
6205                                         {
6206                                             if(model_count<batch_model_count){
6207                                                 /* Update bounds only if the model status is solved to optimal */
6208                                                 if(model->_status==0)
6209                                                 {
6210                                                     mkname=model->get_name();
6211                                                     std::size_t pos = mkname.find("|");
6212                                                     vkname.assign(mkname, 0, pos);
6213                                                     mkname=mkname.substr(pos+1);
6214                                                     pos=mkname.find("|");
6215                                                     keyk.assign(mkname, 0, pos);
6216                                                     dirk=mkname.substr(pos+1);
6217                                                     vk=obbt_model->template get_var<T>(vkname);
6218                                                     var_key_k=vkname+"|"+keyk;
6219                                                     objk=model->get_obj_val();
6220                                                     auto update_lb=false;
6221                                                     auto update_ub=false;
6222                                                     if(dirk=="LB")
6223                                                     {
6224                                                         boundk1=vk.get_lb(keyk);
6225                                                         //Uncertainty in objk=obk+-solver_tolerance, here we choose lowest possible value in uncertainty interval
6226                                                         objk=std::max(objk-range_tol, boundk1);
6227                                                     }
6228                                                     else
6229                                                     {
6230                                                         boundk1=vk.get_ub(keyk);
6231                                                         //Uncertainty in objk=obk+-solver_tolerance, here we choose highest possible value in uncertainty interval
6232                                                         objk=std::min(objk+range_tol, boundk1);
6233 
6234                                                     }
6235                                                     if((std::abs(boundk1-objk) <= fixed_tol_abs || std::abs((boundk1-objk)/(boundk1+zero_tol))<=fixed_tol_rel))
6236                                                     {//do not close intervals to OBBT before finishing at least one full iteration over all variables
6237                                                         fixed_point[model->get_name()]=true;
6238                                                     }
6239                                                     else
6240                                                     {
6241                                                         if(dirk=="LB"){
6242                                                             vk.set_lb(keyk, objk);
6243                                                             update_lb=true;
6244                                                         }
6245                                                         else{
6246                                                             vk.set_ub(keyk, objk);
6247                                                             update_ub=true;
6248                                                         }
6249                                                         //If crossover in bounds,just exchange them
6250                                                         if(vk.get_ub(keyk)<vk.get_lb(keyk))
6251                                                         {
6252                                                             fixed_point[var_key_k+"|LB"]=true;
6253                                                             fixed_point[var_key_k+"|UB"]=true;
6254                                                             temp=vk.get_ub(keyk);
6255                                                             tempa=vk.get_lb(keyk);
6256                                                             vk.set_ub(keyk, tempa);
6257                                                             vk.set_lb(keyk, temp);
6258                                                             update_lb=true;
6259                                                             update_ub=true;
6260 
6261                                                         }
6262                                                         else if(!vk._lift){
6263                                                             fixed_point[model->get_name()]=false;
6264                                                             terminate=false;
6265                                                         }
6266                                                     }
6267                                                     //If interval becomes smaller than range_tol, reset bounds so that interval=range_tol
6268                                                     if(std::abs(vk.get_ub(keyk)-vk.get_lb(keyk))<range_tol)
6269                                                     {
6270                                                         //If original interval is itself smaller than range_tol, do not have to reset interval
6271                                                         if(interval_original[var_key_k]>=range_tol)
6272                                                         {
6273                                                             DebugOn("Entered reset");
6274                                                             //Mid is the midpoint of interval
6275                                                             mid=(vk.get_ub(keyk)+vk.get_lb(keyk))/2.0;
6276                                                             left=mid-range_tol/2.0;
6277                                                             right=mid+range_tol/2.0;
6278                                                             //If resized interval does not cross original bounds, reset
6279                                                             if(right<=ub_original[var_key_k] && left>=lb_original[var_key_k])
6280                                                             {
6281                                                                 vk.set_ub(keyk, right);
6282                                                                 vk.set_lb(keyk, left);
6283                                                                 update_lb=true;
6284                                                                 update_ub=true;
6285                                                             }
6286                                                             //If resized interval crosses original upperbound, set the new bound to upperbound, and lower bound is expanded to upperbound-range_tolerance
6287                                                             else if(right>ub_original[var_key_k])
6288                                                             {
6289 
6290                                                                 vk.set_ub(keyk, ub_original[var_key_k]);
6291                                                                 vk.set_lb(keyk, ub_original[var_key_k]-range_tol);
6292                                                                 update_lb=true;
6293                                                                 update_ub=true;
6294                                                             }
6295                                                             //If resized interval crosses original lowerbound, set the new bound to lowerbound, and upper bound is expanded to lowerbound+range_tolerance
6296                                                             else if(left<lb_original[var_key_k])
6297                                                             {
6298                                                                 vk.set_lb(keyk, lb_original[var_key_k]);
6299                                                                 vk.set_ub(keyk, lb_original[var_key_k]+range_tol);
6300                                                                 update_lb=true;
6301                                                                 update_ub=true;
6302 
6303                                                             }
6304                                                             //In the resized interval both original lower and upper bounds can not be crosses, because original interval is greater
6305                                                             //than range_tol
6306 
6307                                                         }
6308                                                     }
6309                                                     if(update_lb||update_ub){
6310                                                         for(auto &mod:batch_models){
6311                                                             auto vkmod=mod->template get_var<T>(vkname);
6312                                                             if(update_lb){
6313                                                                 vkmod.set_lb(keyk, vk.get_lb(keyk));
6314                                                             }
6315                                                             if(update_ub){
6316                                                                 vkmod.set_ub(keyk, vk.get_ub(keyk));
6317                                                             }
6318                                                         }
6319                                                     }
6320                                                     if(linearize){
6321                                                         //if(linearize && !fixed_point[model->get_name()]){
6322                                                         //if(std::abs(vk.get_ub(keyk)-vk.get_lb(keyk))>range_tol){
6323                                                         model->get_solution(obbt_solution);
6324                                                         relaxed_model->add_iterative(interior_model, obbt_solution, obbt_model);
6325                                                         //}
6326                                                     }
6327 
6328                                                 }
6329                                                 else
6330                                                 {
6331                                                     //                                            model->print();
6332                                                     DebugOn("OBBT step has failed in iteration\t"<<iter<<endl);
6333 
6334                                                 }
6335                                                 model_count++;
6336                                             }
6337                                         }
6338                                         for(auto &mod:batch_models){
6339                                             mod->reset_constrs();
6340                                             mod->reset_lifted_vars_bounds();
6341                                         }
6342                                         batch_model_count=0;
6343                                     }
6344                                 }
6345                             }
6346                         }
6347                     }
6348 
6349                     //Check if OBBT has converged, can check every gap_count_int intervals
6350                     if(iter%gap_count_int==0)
6351                     {
6352                         solver_time= get_wall_time()-solver_time_start;
6353 
6354 
6355                         //this->print();
6356                         //                    auto new_obbt = *obbt_model;
6357                         //                    obbt_model = new_obbt.copy();
6358                         if(linearize){
6359                             obbt_model->reset();
6360                             obbt_model->reindex();
6361                         }
6362                         obbt_model->reset_constrs();
6363                         obbt_model->reset_lifted_vars_bounds();
6364                         obbt_model->_first_run = true;
6365                         //                    obbt_model->print();
6366                         solver<> LB_solver(obbt_model,lb_solver_type);
6367                         if(!linearize)
6368                             LB_solver.set_option("bound_relax_factor", lb_solver_tol*1e-2);
6369                         else
6370                             LB_solver.set_option("bound_relax_factor", lb_solver_tol*0.9e-1);
6371                         LB_solver.run(output = 0, lb_solver_tol);
6372                         if(obbt_model->_status==0)
6373                         {
6374                             lower_bound=obbt_model->get_obj_val();
6375                             auto gap = 100*(upper_bound - lower_bound)/std::abs(upper_bound);
6376                             DebugOn("Gap "<<gap<<" at iteration "<<iter<<" and solver time "<<solver_time<<endl);
6377                             if(linearize){
6378                             unsigned nb_OA_cuts = 0;
6379                             for (auto const &iter: relaxed_model->_OA_cuts) {
6380                                 nb_OA_cuts += iter.second.size();
6381                             }
6382                             DebugOn("Number of OA cuts = "<< nb_OA_cuts<<endl);
6383                             }
6384                             DebugOn("Updating bounds on original problem and resolving"<<endl);
6385                             this->copy_bounds(obbt_model);
6386                             this->copy_solution(obbt_model);
6387                             solver<> UB_solver(*this,ub_solver_type);
6388                             UB_solver.run(output = 0, ub_solver_tol);
6389                             auto new_ub = get_obj_val();
6390                             if(new_ub<upper_bound){
6391                                 upper_bound = new_ub;
6392                                 get_solution(ub_sol);
6393                                 DebugOn("Found a better feasible point!"<<endl);
6394                                 DebugOn("New upper bound = "<< upper_bound << endl);
6395                                 auto ub = static_pointer_cast<param<>>(obbt_model->get_constraint("obj|ub")->_params->begin()->second.first);
6396                                 ub->set_val(upper_bound);
6397                                 for(auto &mod:batch_models){
6398                                     auto ub = static_pointer_cast<param<>>(mod->get_constraint("obj|ub")->_params->begin()->second.first);
6399                                     ub->set_val(upper_bound);
6400                                     mod->reset_constrs();
6401                                 }
6402                             }
6403                             else {
6404                                 set_solution(ub_sol);
6405                                 _obj->set_val(upper_bound);
6406                             }
6407                         }
6408                         else {
6409                             DebugOn("Failed to solve OBBT Model " << obbt_model->_name <<endl);
6410                         }
6411 
6412 
6413                         if (std::abs(upper_bound- lower_bound)<=abs_tol && ((upper_bound- lower_bound))/(std::abs(upper_bound)+zero_tol)<=rel_tol)
6414                         {
6415                             DebugOn("Gap closed at iter "<< iter<<endl);
6416                             DebugOn("Initial Gap Nonlinear = " << to_string(gapnl) << "%."<<endl);
6417                             gap = 100*std::abs(upper_bound - lower_bound)/std::abs(upper_bound);
6418                             DebugOn("Final Gap = " << to_string(gap) << "%."<<endl);
6419                             DebugOn("Upper bound = " << to_string(upper_bound) << "."<<endl);
6420                             DebugOn("Lower bound = " << to_string(lower_bound) << "."<<endl);
6421                             DebugOn("Time\t"<<solver_time<<endl);
6422                             // relaxed_model->_obj->set_val(lower_bound);
6423                             close=true;
6424                             terminate=true;
6425                             //                        obbt_model->print();
6426                         }
6427                     }
6428 
6429                     if(break_flag==true)
6430                     {
6431                         DebugOn("Maximum Time Exceeded\t"<<max_time<<endl);
6432                         DebugOn("Iterations\t"<<iter<<endl);
6433 
6434                         break;
6435                     }
6436                     solver_time= get_wall_time()-solver_time_start;
6437                     DebugOn("Solved Fixed Point iteration " << iter << endl);
6438                 }
6439 
6440                 vector<double> interval_gap;
6441 
6442                 for(auto &it:obbt_model->_vars_name)
6443                 {
6444                     string vname=it.first;
6445                     v=obbt_model->template get_var<double>(vname);
6446                     auto v_keys=v.get_keys();
6447                     bool in_orig_model=false;
6448                     if(this->_vars_name.find(vname)!=this->_vars_name.end())
6449                     {
6450                         var_ub=this->template get_var<T>(vname);
6451                         in_orig_model=true;
6452                     }
6453                     for(auto &key: *v_keys)
6454                     { num_var++;
6455                         var_key=vname+"|"+ key;
6456                         interval_gap.push_back((interval_original[var_key]-interval_new[var_key])/(interval_original[var_key]+zero_tol)*100.0);
6457                         sum+=interval_gap.back();
6458                         if( in_orig_model)
6459                         {
6460                             var_ub.uneval();
6461                             if((var_ub.eval(key)-v.get_lb(key)) <0.000 || (var_ub.eval(key)-v.get_ub(key))>0.000){
6462                                 xb_true=false;
6463                                 DebugOn("xb false Variable " <<vname<< " key "<< key<< " UB_value " <<var_ub.eval(key) <<"OBBT, lb, ub "<< v.get_lb(key)<<" "<< v.get_ub(key)<<endl);
6464                             }
6465                         }
6466                         DebugOff(var_key<<" " << interval_gap.back()<< " LB flag = " << fixed_point.at(var_key+"|LB") << endl);
6467                         DebugOff(var_key<<" " << interval_gap.back()<< " UB flag = " << fixed_point.at(var_key+"|UB") << endl);
6468                     }
6469 
6470                 }
6471                 avg=sum/num_var;
6472 
6473                 DebugOn("Average interval reduction\t"<<avg<<endl);
6474 
6475                 if(!close)
6476                 {
6477 
6478                     //                obbt_model->reset_constrs();
6479                     solver<> LB_solver(obbt_model,lb_solver_type);
6480                     LB_solver.run(output = 0, lb_solver_tol);
6481                 }
6482 
6483             }
6484             else{
6485                 close=true;
6486             }
6487             if(!close)
6488             {
6489 #ifdef USE_MPI
6490                 if(worker_id==0){
6491 #endif
6492                     //                obbt_model->reset_constrs();
6493                     solver<> LB_solver(obbt_model,lb_solver_type);
6494                     LB_solver.run(output = 0, lb_solver_tol);
6495                     if(obbt_model->_status==0)
6496                     {
6497 
6498                         DebugOff("\nLower bound = " << " " << to_string(obbt_model->get_obj_val()) << " " <<endl);
6499                         DebugOff("Solution Print"<<endl);
6500                         //                    SDP->print_solution();
6501                         //                    this->print_constraints_stats(tol);
6502                         DebugOn("Initial Gap Nonlinear = " << to_string(gapnl) << "%."<<endl);
6503                         lower_bound=obbt_model->get_obj_val();
6504                         gap = 100*std::abs(upper_bound - lower_bound)/std::abs(upper_bound);
6505                         DebugOn("Final Gap = " << to_string(gap) << "%."<<endl);
6506                         DebugOn("Upper bound = " << to_string(upper_bound) << "."<<endl);
6507                         DebugOn("Lower bound = " << to_string(lower_bound) << "."<<endl);
6508                         DebugOn("Time\t"<<solver_time<<endl);
6509 
6510                     }
6511                     else
6512                     {
6513                         DebugOn("Initial Gap = " << to_string(gapnl) << "%."<<endl);
6514                         DebugOn("Lower bounding problem status = " << relaxed_model->_status <<endl);
6515                         DebugOn("Lower bounding problem not solved to optimality, cannot compute final gap"<<endl);
6516                     }
6517                     if(time_limit){
6518                         DebugOn("Reached Time limit!"<<endl);
6519                     }
6520                     else {
6521                         DebugOn("Terminate\t"<<terminate<<endl);
6522                     }
6523 
6524 
6525                     DebugOn("Time\t"<<solver_time<<endl);
6526                     DebugOn("Iterations\t"<<iter<<endl);
6527 #ifdef USE_MPI
6528                 }
6529 #endif
6530 
6531             }
6532             //        relaxed_model->_obj->set_val(lower_bound);
6533         }
6534         else
6535         {
6536             DebugOn("Lower bounding problem not solved to optimality, cannot compute initial gap"<<endl);
6537         }
6538         std::get<0>(res) = terminate;
6539         std::get<1>(res) = iter;
6540         std::get<2>(res) = solver_time;
6541         std::get<3>(res) = lower_bound_nonlin_init;
6542         std::get<4>(res) = lower_bound_init;
6543         std::get<5>(res) = upper_bound;
6544         std::get<6>(res) = lower_bound;
6545         std::get<7>(res) = avg;
6546         return res;
6547     }
6548 
6549 template <typename type>
6550 template<typename T,typename std::enable_if<is_arithmetic<T>::value>::type*>
readNL(const string & fname)6551 int Model<type>::readNL(const string& fname){
6552 #ifdef USE_MP
6553     mp::Problem p;
6554     mp::ReadNLFile(fname, p);
6555     auto nb_vars = p.num_vars();
6556     auto nb_cstr = p.num_algebraic_cons();
6557     DebugOn("The number of variables is " << nb_vars << endl);
6558     DebugOn("The number of constraints is " << nb_cstr << endl);
6559     indices C("C"), I("I"), LinConstr("LinConstr"), QuadConstr("QuadConstr"), NonLinConstr("NonLinConstr");
6560     int nb_cont = 0;
6561     int nb_int = 0;
6562     int nb_other = 0;
6563     vector<int> C_ids,I_ids;
6564     for (const auto v: p.vars()) {
6565         if(v.type()==mp::var::CONTINUOUS){
6566             nb_cont++;
6567             C.insert(to_string(v.index()));
6568             C_ids.push_back(v.index());
6569         }
6570         else if(v.type()==mp::var::INTEGER){
6571             I.insert(to_string(v.index()));
6572             I_ids.push_back(v.index());
6573             nb_int++;
6574         }
6575         else{
6576             throw invalid_argument("Unrecognised variable type, can only be continuous or integer");
6577         }
6578     }
6579     DebugOn("Number of continuous variables = " << nb_cont << endl);
6580     DebugOn("Number of integer variables = " << nb_int << endl);
6581 
6582     param<> x_ub("x_ub"), x_lb("x_lb");
6583     param<int> y_ub("y_ub"), y_lb("y_lb");
6584     x_ub.in(C);x_lb.in(C);
6585     y_ub.in(I);y_lb.in(I);
6586     for (int i = 0; i<C.size(); i++) {
6587         x_lb.set_val(i, p.var(C_ids[i]).lb());
6588         x_ub.set_val(i, p.var(C_ids[i]).ub());
6589     }
6590     for (int i = 0; i<I.size(); i++) {
6591         y_lb.set_val(i, p.var(I_ids[i]).lb());
6592         y_ub.set_val(i, p.var(I_ids[i]).ub());
6593     }
6594     var<> x("x", x_lb, x_ub);
6595     var<int> y("y", y_lb, y_ub);
6596 
6597     param<> rhs("rhs");
6598     int nb_lin = 0;
6599     int nb_nonlin = 0;
6600     int index = 0;
6601     _name = fname;
6602 
6603     if(!C.empty())
6604         add(x.in(C));
6605     if(!I.empty()){
6606         add(y.in(I));
6607         replace_integers();
6608     }
6609 
6610     MPConverter converter(*this);
6611     map<int,vector<int>> constr_sparsity;
6612     vector<int> C_lin, C_nonlin, C_quad;
6613 
6614     int num_objs = p.num_objs();
6615     if(num_objs>=1){
6616         if(num_objs>=2){
6617             DebugOn("Gravity currently supports only one objective, will only add the first one");
6618         }
6619         mp::Problem::Objective obj = p.obj(0);
6620         mp::obj::Type main_obj_type = p.obj(0).type();
6621         func<> objective;
6622         auto lexpr = obj.linear_expr();
6623         for (const auto term: lexpr){
6624             auto coef = term.coef();
6625             auto var_id = term.var_index();
6626             if(coef!=0)
6627                 objective += coef*converter.get_cont_int_var(var_id);
6628         }
6629         auto nl_expr = obj.nonlinear_expr();
6630         if (nl_expr){
6631             auto expr = converter.Visit(nl_expr);
6632             objective += expr;
6633         }
6634         auto sense = main_obj_type == mp::obj::MIN;
6635         if(sense){
6636             min(objective);
6637         }
6638         else {
6639             max(objective);
6640         }
6641     }
6642 
6643     for (const auto con: p.algebraic_cons()) {
6644         auto lexpr = con.linear_expr();
6645         auto nl_expr = con.nonlinear_expr();
6646         if (nl_expr){
6647             auto expr = converter.Visit(nl_expr);
6648             nb_nonlin++;
6649             C_nonlin.push_back(index);
6650             NonLinConstr.insert(to_string(index));
6651             for (const auto term: lexpr){
6652                 auto coef = term.coef();
6653                 auto var_id = term.var_index();
6654                 if(coef!=0)
6655                     expr += coef*converter.get_cont_int_var(var_id);
6656             }
6657 
6658             auto c_lb = con.lb();
6659             auto c_ub = con.ub();
6660             if(c_lb==c_ub){
6661                 Constraint<> c("NL_C_eq_"+to_string(index));
6662                 c += expr;
6663                 add(c == c_lb);
6664             }
6665             else {
6666                 if(c_lb>numeric_limits<double>::lowest()){
6667                     Constraint<> c("NL_C_lb_"+to_string(index));
6668                     c += expr;
6669                     add(c >= c_lb);
6670                 }
6671                 if(c_ub<numeric_limits<double>::max()){
6672                     Constraint<> c("NL_C_ub_"+to_string(index));
6673                     c += expr;
6674                     add(c <= c_ub);
6675                 }
6676             }
6677         }
6678         else{
6679             int nb_terms = 0;
6680             func<> expr;
6681             for (const auto term: lexpr){
6682                 auto coef = term.coef();
6683                 auto var_id = term.var_index();
6684                 if(coef!=0){
6685                     expr += coef*converter.get_cont_int_var(var_id);
6686                     nb_terms++;
6687                 }
6688             }
6689             auto c_lb = con.lb();
6690             auto c_ub = con.ub();
6691             if(nb_terms==1 && c_lb!=c_ub){/* this is just a bound constraint */
6692                 const auto term = *lexpr.begin();
6693                 auto coef = term.coef();
6694                 auto var_id = term.var_index();
6695                 auto v = converter.get_cont_int_var(var_id);
6696                 if(coef != 0 && c_lb>numeric_limits<double>::lowest()){
6697                     if(v._is_relaxed){/* an integer */
6698                         v._lb->_val->at(v.get_id_inst()) = c_lb/coef;
6699                     }
6700                     else {
6701                         v._lb->_val->at(v.get_id_inst()) = c_lb/coef;
6702                     }
6703                 }
6704                 if(coef != 0 && c_ub<numeric_limits<double>::max()){
6705                     if(v._is_relaxed){/* an integer */
6706                         v._ub->_val->at(v.get_id_inst()) = c_ub/coef;
6707                     }
6708                     else {
6709                         v._ub->_val->at(v.get_id_inst()) = c_ub/coef;
6710                     }
6711                 }
6712             }
6713             else{
6714                 constr_sparsity[nb_terms].push_back(index);
6715                 if(c_lb==c_ub){
6716                     Constraint<> c("Lin_C_eq_"+to_string(index));
6717                     c += expr;
6718                     add(c == c_lb);
6719                 }
6720                 else {
6721                     if(c_lb>numeric_limits<double>::lowest()){
6722                         Constraint<> c("Lin_C_lb_"+to_string(index));
6723                         c += expr;
6724                         add(c >= c_lb);
6725                     }
6726                     if(c_ub<numeric_limits<double>::max()){
6727                         Constraint<> c("Lin_C_ub_"+to_string(index));
6728                         c += expr;
6729                         add(c <= c_ub);
6730                     }
6731                 }
6732                 LinConstr.insert(to_string(index));
6733                 C_lin.push_back(index);
6734                 nb_lin++;
6735             }
6736         }
6737         index++;
6738     }
6739     DebugOn("Number of linear constraints = " << nb_lin << endl);
6740     DebugOn("Number of non linear constraints = " << nb_nonlin << endl);
6741     DebugOn("Number of sparsity degrees for linear constraints = " << constr_sparsity.size() << endl);
6742 
6743 #endif
6744     return 0;
6745 }
6746 
6747 
6748 
6749     template std::tuple<bool,int,double,double,double,double,double,double> gravity::Model<double>::run_obbt<double, (void*)0>(shared_ptr<Model<double>> relaxed_model, double max_time, unsigned max_iter, double rel_tol, double abs_tol, unsigned nb_threads, SolverType ub_solver_type, SolverType lb_solver_type, double ub_solver_tol, double lb_solver_tol, double range_tol, bool linearize);
6750 
6751     template std::tuple<bool,int,double,double,double,double,double,double> gravity::Model<double>::run_obbt_one_iteration<double, (void*)0>(shared_ptr<Model<double>> relaxed_model, double max_time, unsigned max_iter, double rel_tol, double abs_tol, unsigned nb_threads, SolverType ub_solver_type, SolverType lb_solver_type, double ub_solver_tol, double lb_solver_tol, double range_tol, bool linearize, shared_ptr<Model<double>> obbt_model, Model<double> & interior_model);
6752 
6753     template Constraint<Cpx> Model<Cpx>::lift(Constraint<Cpx>& c, string model_type);
6754     template Constraint<> Model<>::lift(Constraint<>& c, string model_type);
6755     template int gravity::Model<double>::readNL<double, (void*)0>(const string&);
6756 
6757 
6758     //    template void Model<double>::run_obbt(double max_time, unsigned max_iter);
6759     //    template func<double> constant<double>::get_real() const;
6760     //    template class Model<double>;
6761     //    template class Model<Cpx>;
6762 
6763 }
6764 
6765 
6766 
6767 
6768