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