1 //
2 //  model.hpp
3 //  Gravity
4 //
5 //  Created by Hijazi, Hassan
6 //
7 //
8 #define M_PI		3.14159265358979323846
9 #ifndef model_hpp
10 #define model_hpp
11 
12 #include <stdio.h>
13 #include <bitset>
14 #include <gravity/constraint.h>
15 #include <map>
16 #include <unordered_set>
17 #include <math.h>
18 #include <vector>
19 #include <deque>
20 #include <thread>
21 #include <iostream>
22 #include <functional>
23 #ifdef USE_MPI
24 #include <mpi.h>
25 #endif
26 #ifdef USE_IPOPT
27 #define HAVE_STDDEF_H
28 #include <IpIpoptApplication.hpp>
29 #include <IpTNLP.hpp>
30 #undef HAVE_STDDEF_H
31 #endif
32 #ifdef USE_GUROBI
33 #include <gurobi_c++.h>
34 #endif
35 #ifdef USE_BONMIN
36 #include <BonTMINLP.hpp>
37 #endif
38 #ifdef USE_MP
39 #include "mp/nl.h"
40 #include "mp/problem.h"
41 #include "mp/expr-visitor.h"
42 #endif
43 #ifdef USE_CoinUtils
44 #include "CoinMpsIO.hpp"
45 #include "CoinFileIO.hpp"
46 #include "CoinModel.hpp"
47 #endif
48 
49 using namespace std;
50 
51 namespace gravity {
52 
53 
54     template<typename type>
cstr_compare(const shared_ptr<Constraint<type>> & c1,const shared_ptr<Constraint<type>> & c2)55     const bool cstr_compare(const shared_ptr<Constraint<type>>& c1, const shared_ptr<Constraint<type>>& c2) {
56         if(c1->get_nb_inst() > c2->get_nb_inst())
57             return true;
58         return false;
59 //        return c1->nb_linear_terms() > c2->nb_linear_terms();
60     }
61 
62 const bool var_compare(const pair<string,shared_ptr<param_>>& v1, const pair<string,shared_ptr<param_>>& v2);
63 
64     /**
65      Parallel computation of the constraints stored in v[i] to v[j]
66      @param[in] v vector of constraints
67      @param[in] res vector storing the computation results
68      @param[in] i starting index in v
69      @param[in] j ending index in v
70      */
71     template<typename type,typename std::enable_if<is_arithmetic<type>::value>::type* = nullptr>
compute_constrs(vector<shared_ptr<Constraint<type>>> & v,double * res,size_t i,size_t j)72     void compute_constrs(vector<shared_ptr<Constraint<type>>>& v, double* res, size_t i, size_t j){
73         DebugOff("Calling compute_constrts with i =  " << i << "and j = "<< j << endl);
74         for (size_t idx = i; idx < j; idx++) {
75             auto c = v[idx];
76             //            c->print_symbolic();
77             //            if(c->_name == "Real(Linking_V_mag)_lifted"){
78             //                cout << "ok";
79             //            }
80             c->_new = false;
81             c->_evaluated = false;
82             size_t nb_ins = c->get_nb_inst();
83             size_t ind = 0;
84             for (size_t inst = 0; inst< nb_ins; inst++){
85                 if (!*c->_all_lazy || !c->_lazy[inst]) {
86                     res[c->_id+ind++] = c->eval(inst);
87                     DebugOff("Accessing res at position " << c->_id+inst << endl);
88                     //                _cons_vals[index++] = res[c->_id+inst];
89                     DebugOff("g[" << to_string(c->_id+inst) << "] = " << to_string(res[c->_id+inst]) << endl);
90                     //                    if(c->_id+inst==15){
91                     //                        cout << "ok";
92                     //                    }
93                 }
94             }
95         }
96     }
97 
98     /**
99      Parallel computation of the jacobian of the constraints stored in v[i] to v[j]
100      @param[in] v vector of constraints
101      @param[in] res vector storing the computation results
102      @param[in] i starting index in v
103      @param[in] j ending index in v
104      @param[in] first_call true if first_call to this function, false otherwise
105      @param[in] jac_vals Gravity's internal vector for storing the results
106      */
107     template<typename type,typename std::enable_if<is_arithmetic<type>::value>::type* = nullptr>
compute_jac(vector<shared_ptr<Constraint<type>>> & vec,double * res,size_t i,size_t j,bool first_call,vector<double> & jac_vals)108     void compute_jac(vector<shared_ptr<Constraint<type>>>& vec, double* res, size_t i, size_t j, bool first_call, vector<double>& jac_vals){
109         size_t cid = 0, id_inst = 0;
110         string vid;
111         shared_ptr<Constraint<type>> c = NULL;
112         param_* v = NULL;
113         shared_ptr<func<type>> dfdx;
114         auto idx = vec[i]->_jac_cstr_idx;
115         for (size_t ind = i; ind < j; ind++) {
116             c = vec[ind];
117             auto nb_ins = c->get_nb_inst();
118             id_inst = 0;
119             if (c->is_linear() && !first_call) {
120                 //                        if (false) {
121                 DebugOff("Linear constraint, using stored jacobian!\n");
122                 for (size_t i = 0; i<nb_ins; i++) {
123                     if (!*c->_all_lazy || !c->_lazy[i]) {
124                         for (size_t j = 0; j<c->get_nb_vars(i); j++) {
125                             res[idx] = jac_vals[idx];
126                             idx++;
127                         }
128                     }
129                 }
130             }
131             else {
132                 for (auto &v_p: c->get_vars()){
133                     v = v_p.second.first.get();
134                     vid = v->_name;
135                     dfdx = c->get_stored_derivative(vid);
136                     if(!dfdx->is_constant()){
137                         dfdx->_evaluated=false;
138                     }
139                     id_inst = 0;
140                     for (size_t inst = 0; inst< nb_ins; inst++){
141                         if (!*c->_all_lazy || !c->_lazy[inst]) {
142                             cid = c->_id+id_inst++;
143 
144                             if (v->_is_vector || v->is_matrix_indexed()) {
145                                 auto dim = v->get_dim(inst);
146                                 for (size_t j = 0; j<dim; j++) {
147                                     res[idx] += dfdx->eval(inst,j);
148                                     jac_vals[idx] = res[idx];
149                                     DebugOff("jac_val["<< idx <<"] = " << jac_vals[idx] << endl);
150                                     idx++;
151                                 }
152                             }
153                             else {
154                                 res[idx] += dfdx->eval(inst);
155                                 jac_vals[idx] = res[idx];
156                                 idx++;
157                             }
158                         }
159                     }
160                 }
161             }
162         }
163     }
164 
165 
166     template<typename type = double>
167     class Model {
168 
169     protected:
170         string                                  _name; /**< Model name. */
171         set<pair<size_t,size_t>>                _hess; /**< Pairs of variables linked in the hessian, storing Ipopt indices here. */
172         deque<shared_ptr<func<type>>>           _nl_funcs; /**< Queue of all the nonlinear functions appearing in the model. */
173         map<string,shared_ptr<func<type>>>      _nl_funcs_map;/**< Map of all the nonlinear functions appearing in the model. */
174 
175 
176     public:
177 
178         bool                            _has_callback = false; /**< Has callback option. */
179         bool                            _has_lazy = false; /**< Has lazy constraints. */
180         bool                            _built = false; /**< Indicates if this model has been already built. */
181         bool                            _first_run = true; /**< Indicates if a solver was ran on this model. */
182 
183         bool                            _first_call_grad_obj = true; /**< Indicates if this is the first call to fill_in_grad_obj */
184         bool                            _first_call_jac = true; /**< Indicates if this is the first call to fill_in_jac */
185         bool                            _first_call_hess = true; /**< Indicates if this is the first call to fill_in_hess */
186         Convexity                       _convexity = linear_; /**< Indicates the convexity type of the current model */
187         MType                           _type = lin_m; /**< Model type, e.g., linar, quadratic, polynomial, NLP.. */
188         size_t                          _nb_vars = 0; /**< Number of variables. */
189         size_t                          _nb_params = 0; /**< Number of parameters. */
190         size_t                          _nb_cons = 0; /**< Number of constraints. */
191         size_t                          _nnz_g = 0; /**< Number of non zeros in the Jacobian */
192         size_t                          _nnz_h = 0; /**< Number of non zeros in the Hessian */
193         size_t                          _nnz_g_obj = 0; /**< Number of non zeros in the Objective gradient */
194 
195         /* Ipopt data structures */
196         vector<double>                  _jac_vals; /* Jacobian values stored in sparse format */
197         vector<double>                  _obj_grad_vals; /* Objective gradient values stored in sparse format */
198         vector<double>                  _hess_vals; /* Hessian values stored in sparse format */
199         /* */
200         map<pair<size_t,size_t>, size_t>                    _nnz_pairs;
201         vector<size_t>                                      _idx_it;
202         map<size_t, shared_ptr<param_>>                     _params; /**< Sorted map pointing to all parameters contained in this model. */
203         map<size_t, shared_ptr<param_>>                     _vars; /**< Sorted map pointing to all variables contained in this model. */
204         map<size_t, shared_ptr<param_>>                     _int_vars; /**< Sorted map pointing to all binary variables contained in this model. */
205         map<string, shared_ptr<param_>>                     _params_name; /**< Sorted map (by name) pointing to all parameters contained in this model. */
206         map<string, shared_ptr<param_>>                     _vars_name; /**< Sorted map (by name) pointing to all variables contained in this model. */
207         vector<shared_ptr<Constraint<type>>>                _cons_vec; /**< vector pointing to all constraints contained in this model. */
208         map<size_t, shared_ptr<Constraint<type>>>           _cons; /**< Sorted map (increasing index) pointing to all constraints contained in this model. */
209         map<string, shared_ptr<Constraint<type>>>           _cons_name; /**< Sorted map (by name) pointing to all constraints contained in this model. */
210         map<string, set<shared_ptr<Constraint<type>>>>      _v_in_cons; /**< Set of constraints where each variable appears. */
211         shared_ptr<func<type>>                              _obj = nullptr; /**< Pointer to objective function */
212         ObjectiveType                                       _objt = minimize; /**< Minimize or maximize */
213         int                                                 _status = -1;/**< status when last solved */
214         map<pair<string, string>,map<int,pair<shared_ptr<func<type>>,shared_ptr<func<type>>>>>            _hess_link; /* for each pair of variables appearing in the hessian, storing the set of constraints they appear together in */
215         map<size_t, set<vector<int>>>                        _OA_cuts; /**< Sorted map pointing to all OA cut coefficients for each constraint. */
216          template<typename T=type>
217         void merge_vars(const shared_ptr<expr<T>>& e, bool share_bounds = false){/**<  Transfer all variables and parameters to the model. */
218             switch (e->get_type()) {
219                 case uexp_c:{
220                     auto ue = (uexpr<type>*)e.get();
221                     if (ue->_son->is_function()) {
222                         auto f = static_pointer_cast<func<type>>(ue->_son);
223                         merge_vars(f,share_bounds);
224                     }
225                     break;
226                 }
227                 case bexp_c:{
228                     auto be = (bexpr<type>*)e.get();
229                     if (be->_lson->is_function()) {
230                         auto f = static_pointer_cast<func<type>>(be->_lson);
231                         merge_vars(f,share_bounds);
232                     }
233                     if (be->_rson->is_function()) {
234                         auto f = static_pointer_cast<func<type>>(be->_rson);
235                         merge_vars(f,share_bounds);
236                     }
237                     break;
238                 }
239                 default:
240                     break;
241             }
242         }
243 
244         /**
245          Subfunction of embed(func_&& f). Merge variables and parameters with f. If a variable x in f exists in the current funtion, x will now point to the same variable appearing in current function.
246          @param[in] f function to merge variables and parameters with.
247          */
248         template<typename T=type>
249         void merge_vars(const shared_ptr<Constraint<T>>& f, bool share_bounds = false){
250             for (auto &pair:*f->_lterms) {
251                 auto p = pair.second._p;
252                 if (p->is_var()) {
253                     auto pid = *p->_vec_id;
254                     p->share_vals(_vars.at(pid));
255                     if(share_bounds)
256                         p->share_bounds(_vars.at(pid));
257                 }
258                 else if(p->_name.find("-lb")!=string::npos || p->_name.find("-ub")!=string::npos){
259                     auto it = _params_name.insert({p->get_name(true,true), p});
260                     if(it.second){
261                         p->set_vec_id(_params.size());
262                         _params[_params.size()] = p;
263                     }
264                     else {
265                         auto pid = *it.first->second->_vec_id;
266                         p->share_vals(it.first->second);
267                         p->set_vec_id(pid);
268                     }
269                 }
270                 auto coef = pair.second._coef;
271                 if (coef->is_function()) {
272                     auto f_cst = static_pointer_cast<func<type>>(coef);
273                     merge_vars(f_cst);
274                 }
275                 else if(coef->is_param()) {
276                     auto p_cst = static_pointer_cast<param<type>>(coef);
277                     if(p_cst->_name.find("-lb")!=string::npos || p_cst->_name.find("-ub")!=string::npos){
278                         auto it = _params_name.insert({p_cst->get_name(true,true), p_cst});
279                         if(it.second){
280                             p_cst->set_vec_id(_params.size());
281                             _params[_params.size()] = p_cst;
282                         }
283                         else {
284                             auto pid = *it.first->second->_vec_id;
285                             p_cst->share_vals(it.first->second);
286                             p_cst->set_vec_id(pid);
287                         }
288                     }
289                 }
290 
291             }
292             for (auto &pair:*f->_qterms) {
293                 auto coef = pair.second._coef;
294                 auto p1 = pair.second._p->first;
295                 auto p2 = pair.second._p->second;
296                 if (p1->is_var()) {
297                     auto pid1 = *p1->_vec_id;
298                     p1->share_vals(_vars.at(pid1));
299                     if(share_bounds)
300                         p1->share_bounds(_vars.at(pid1));
301                 }
302                 else if(p1->_name.find("-lb")!=string::npos || p1->_name.find("-ub")!=string::npos){
303                     auto it = _params_name.insert({p1->get_name(true,true), p1});
304                     if(it.second){
305                         p1->set_vec_id(_params.size());
306                         _params[_params.size()] = p1;
307                     }
308                     else {
309                         auto pid = *it.first->second->_vec_id;
310                         p1->share_vals(it.first->second);
311                         p1->set_vec_id(pid);
312                     }
313                 }
314                 if (p2->is_var()) {
315                     auto pid2 = *p2->_vec_id;
316                     p2->share_vals(_vars.at(pid2));
317                     if(share_bounds)
318                         p2->share_bounds(_vars.at(pid2));
319                 }
320                 else if(p2->_name.find("-lb")!=string::npos || p2->_name.find("-ub")!=string::npos){
321                     auto it = _params_name.insert({p2->get_name(true,true), p2});
322                     if(it.second){
323                         p2->set_vec_id(_params.size());
324                         _params[_params.size()] = p2;
325                     }
326                     else {
327                         auto pid = *it.first->second->_vec_id;
328                         p2->share_vals(it.first->second);
329                         p2->set_vec_id(pid);
330                     }
331                 }
332                 if (coef->is_function()) {
333                     auto f_cst = static_pointer_cast<func<type>>(coef);
334                     merge_vars(f_cst);
335                 }
336                 else if(coef->is_param()) {
337                     auto p_cst = static_pointer_cast<param<type>>(coef);
338                     if(p_cst->_name.find("-lb")!=string::npos || p_cst->_name.find("-ub")!=string::npos){
339                         auto it = _params_name.insert({p_cst->get_name(true,true), p_cst});
340                         if(it.second){
341                             p_cst->set_vec_id(_params.size());
342                             _params[_params.size()] = p_cst;
343                         }
344                         else {
345                             auto pid = *it.first->second->_vec_id;
346                             p_cst->share_vals(it.first->second);
347                             p_cst->set_vec_id(pid);
348                         }
349                     }
350                 }
351             }
352             for (auto &pair:*f->_pterms) {
353                 auto list = pair.second._l;
354                 for (auto &ppi: *list) {
355                     auto p = ppi.first;
356                     if (p->is_var()) {
357                         auto pid = *p->_vec_id;
358                         ppi.first->share_vals(_vars.at(pid));
359                         if(share_bounds)
360                             ppi.first->share_bounds(_vars.at(pid));
361                     }
362                     else if(p->_name.find("-lb")!=string::npos || p->_name.find("-ub")!=string::npos){
363                         auto it = _params_name.insert({p->get_name(true,true), p});
364                         if(it.second){
365                             p->set_vec_id(_params.size());
366                             _params[_params.size()] = p;
367                         }
368                         else {
369                             auto pid = *it.first->second->_vec_id;
370                             p->share_vals(_params.at(pid));
371                             p->set_vec_id(pid);
372                         }
373                     }
374                 }
375                 auto coef = pair.second._coef;
376                 if (coef->is_function()) {
377                     auto f_cst = static_pointer_cast<func<type>>(coef);
378                     merge_vars(f_cst);
379                 }
380                 else if(coef->is_param()) {
381                     auto p_cst = static_pointer_cast<param<type>>(coef);
382                     if(p_cst->_name.find("-lb")!=string::npos || p_cst->_name.find("-ub")!=string::npos){
383                         auto it = _params_name.insert({p_cst->get_name(true,true), p_cst});
384                         if(it.second){
385                             p_cst->set_vec_id(_params.size());
386                             _params[_params.size()] = p_cst;
387                         }
388                         else {
389                             auto pid = *it.first->second->_vec_id;
390                             p_cst->share_vals(it.first->second);
391                             p_cst->set_vec_id(pid);
392                         }
393                     }
394                 }
395             }
396             if (f->_expr) {
397                 merge_vars(f->_expr, share_bounds);
398             }
399             if (f->get_cst()->is_function() && !f->get_cst()->func_is_number()) {
400                 auto c = static_pointer_cast<func<type>>(f->get_cst());
401                 merge_vars(c, share_bounds);
402             }
403         }
404 
405         /**
406          Subfunction of embed(func_&& f). Merge variables and parameters with f. If a variable x in f exists in the current funtion, x will now point to the same variable appearing in current function.
407          @param[in] f function to merge variables and parameters with.
408          */
409         template<typename T=type>
410         void merge_vars(const shared_ptr<func<T>>& f, bool share_bounds = false){
411             for (auto &pair:*f->_lterms) {
412                 auto p = pair.second._p;
413                 if (p->is_var()) {
414                     auto pid = *p->_vec_id;
415                     p->share_vals(_vars.at(pid));
416                     if(share_bounds)
417                         p->share_bounds(_vars.at(pid));
418                 }
419                 else  if(p->_name.find("xstar")==string::npos){
420                     auto it = _params_name.insert({p->get_name(true,true), p});
421                     if(it.second){
422                         p->set_vec_id(_params.size());
423                         _params[_params.size()] = p;
424                     }
425                     else {
426                         auto pid = *it.first->second->_vec_id;
427                         p->share_vals(it.first->second);
428                         p->set_vec_id(pid);
429                     }
430                 }
431                 auto coef = pair.second._coef;
432                 if (coef->is_function()) {
433                     auto f_cst = static_pointer_cast<func<type>>(coef);
434                     merge_vars(f_cst);
435                 }
436                 else if(coef->is_param()) {
437                     auto p_cst = static_pointer_cast<param<type>>(coef);
438                     if(p_cst->_name.find("xstar")==string::npos){
439                         auto it = _params_name.insert({p_cst->get_name(true,true), p_cst});
440                         if(it.second){
441                             p_cst->set_vec_id(_params.size());
442                             _params[_params.size()] = p_cst;
443                         }
444                         else {
445                             auto pid = *it.first->second->_vec_id;
446                             p_cst->share_vals(it.first->second);
447                             p_cst->set_vec_id(pid);
448                         }
449                     }
450                 }
451 
452             }
453             for (auto &pair:*f->_qterms) {
454                 auto coef = pair.second._coef;
455                 auto p1 = pair.second._p->first;
456                 auto p2 = pair.second._p->second;
457                 if (p1->is_var()) {
458                     auto pid1 = *p1->_vec_id;
459                     p1->share_vals(_vars.at(pid1));
460                     if(share_bounds)
461                         p1->share_bounds(_vars.at(pid1));
462                 }
463                 else  if(p1->_name.find("xstar")==string::npos){
464                     auto it = _params_name.insert({p1->get_name(true,true), p1});
465                     if(it.second){
466                         p1->set_vec_id(_params.size());
467                         _params[_params.size()] = p1;
468                     }
469                     else {
470                         auto pid = *it.first->second->_vec_id;
471                         p1->share_vals(it.first->second);
472                         p1->set_vec_id(pid);
473                     }
474                 }
475                 if (p2->is_var()) {
476                     auto pid2 = *p2->_vec_id;
477                     p2->share_vals(_vars.at(pid2));
478                     if(share_bounds)
479                         p2->share_bounds(_vars.at(pid2));
480                 }
481                 else  if(p2->_name.find("xstar")==string::npos){
482                     auto it = _params_name.insert({p2->get_name(true,true), p2});
483                     if(it.second){
484                         p2->set_vec_id(_params.size());
485                         _params[_params.size()] = p2;
486                     }
487                     else {
488                         auto pid = *it.first->second->_vec_id;
489                         p2->share_vals(it.first->second);
490                         p2->set_vec_id(pid);
491                     }
492                 }
493                 if (coef->is_function()) {
494                     auto f_cst = static_pointer_cast<func<type>>(coef);
495                     merge_vars(f_cst);
496                 }
497                 else if(coef->is_param()) {
498                     auto p_cst = static_pointer_cast<param<type>>(coef);
499                     if(p_cst->_name.find("xstar")==string::npos){
500                         auto it = _params_name.insert({p_cst->get_name(true,true), p_cst});
501                         if(it.second){
502                             p_cst->set_vec_id(_params.size());
503                             _params[_params.size()] = p_cst;
504                         }
505                         else {
506                             auto pid = *it.first->second->_vec_id;
507                             p_cst->share_vals(it.first->second);
508                             p_cst->set_vec_id(pid);
509                         }
510                     }
511                 }
512             }
513             for (auto &pair:*f->_pterms) {
514                 auto list = pair.second._l;
515                 for (auto &ppi: *list) {
516                     auto p = ppi.first;
517                     if (p->is_var()) {
518                         auto pid = *p->_vec_id;
519                         ppi.first->share_vals(_vars.at(pid));
520                         if(share_bounds)
521                             ppi.first->share_bounds(_vars.at(pid));
522                     }
523                     else  if(p->_name.find("xstar")==string::npos){
524                         auto it = _params_name.insert({p->get_name(true,true), p});
525                         if(it.second){
526                             p->set_vec_id(_params.size());
527                             _params[_params.size()] = p;
528                         }
529                         else {
530                             auto pid = *it.first->second->_vec_id;
531                             p->share_vals(_params.at(pid));
532                             p->set_vec_id(pid);
533                         }
534                     }
535                 }
536                 auto coef = pair.second._coef;
537                 if (coef->is_function()) {
538                     auto f_cst = static_pointer_cast<func<type>>(coef);
539                     merge_vars(f_cst);
540                 }
541                 else if(coef->is_param()) {
542                     auto p_cst = static_pointer_cast<param<type>>(coef);
543                     if(p_cst->_name.find("xstar")==string::npos){
544                         auto it = _params_name.insert({p_cst->get_name(true,true), p_cst});
545                         if(it.second){
546                             p_cst->set_vec_id(_params.size());
547                             _params[_params.size()] = p_cst;
548                         }
549                         else {
550                             auto pid = *it.first->second->_vec_id;
551                             p_cst->share_vals(it.first->second);
552                             p_cst->set_vec_id(pid);
553                         }
554                     }
555                 }
556             }
557             if (f->_expr) {
558                 merge_vars(f->_expr, share_bounds);
559             }
560             if (f->get_cst()->is_function() && !f->get_cst()->func_is_number()) {
561                 auto c = static_pointer_cast<func<type>>(f->get_cst());
562                 merge_vars(c, share_bounds);
563             }
564         }
565 
copy()566         shared_ptr<Model<type>> copy() const{
567             return make_shared<Model<>>(*this);
568         }
569 
Model(const Model & m)570         Model(const Model& m){
571             *this = m;
572         }
573 
574         Model& operator=(const Model& m){
575             _name = m._name;
576             for(auto &vp: m._vars){
577                 switch (vp.second->get_intype()) {
578                     case binary_: {
579                         auto vv = *static_pointer_cast<var<bool>>(vp.second);
580                         add(vv.deep_copy());
581                         break;
582                     }
583                     case short_: {
584                         auto vv = *static_pointer_cast<var<short>>(vp.second);
585                         add(vv.deep_copy());
586                         break;
587                     }
588                     case integer_: {
589                         auto vv = *static_pointer_cast<var<int>>(vp.second);
590                         add(vv.deep_copy());
591                         break;
592                     }
593                     case float_: {
594                         auto vv = *static_pointer_cast<var<float>>(vp.second);
595                         add(vv.deep_copy());
596                         break;
597                     }
598                     case double_: {
599                         auto vv = *static_pointer_cast<var<double>>(vp.second);
600                         add(vv.deep_copy());
601                         break;
602                     }
603                     case long_: {
604                         auto vv = *static_pointer_cast<var<long double>>(vp.second);
605                         add(vv.deep_copy());
606                         break;
607                     }
608                     case complex_: {
609                         auto vv = *static_pointer_cast<var<Cpx>>(vp.second);
610                         add(vv.deep_copy());
611                         break;
612                     }
613                 }
614             }
615             for(auto &cp: m._cons_name){
616                 auto c_cpy = make_shared<Constraint<type>>();
617                 c_cpy->deep_copy(*cp.second);
618                 merge_vars(c_cpy);
619                 c_cpy->uneval();
620                 if(*c_cpy->_all_lazy){
621                     add_lazy(*c_cpy);
622                 }
623                 else{
624                     add(*c_cpy);
625                 }
626             }
627             if(m._obj){
628                 func<type> obj_cpy;
629                 obj_cpy.deep_copy(*m._obj);
630                 set_objective(obj_cpy, _objt);
631             }
632             return *this;
633         }
634 
635         //        Model& operator=(const Model& m){
636         //            _name = m._name;
637         //            _hess = m._hess;
638         //            _nl_funcs =m._nl_funcs;
639         //            _nl_funcs_map = m._nl_funcs_map;
640         //            _has_lazy = m._has_lazy;
641         //            _built = m._built;
642         //            _first_run = m._first_run;
643         //            _first_call_gard_obj = m._first_call_gard_obj;
644         //            _first_call_jac = m._first_call_jac;
645         //            _first_call_hess = m._first_call_hess;
646         //            _convexity = m._convexity;
647         //            _type = m._type;
648         //            _nb_vars = m._nb_vars;
649         //            _nb_params = m._nb_params;
650         //            _nb_cons = m._nb_cons;
651         //            _nnz_g = m._nnz_g;
652         //            _nnz_h = m._nnz_h;
653         //            _nnz_g_obj = m._nnz_g_obj;
654         //            _jac_vals = m._jac_vals;
655         //            _obj_grad_vals = m._obj_grad_vals;
656         //            _hess_vals = m._hess_vals;
657         //            _params = m._params;
658         //            _vars = m._vars;
659         //            _int_vars = m._int_vars;
660         //            _params_name = m._params_name;
661         //            _vars_name = m._vars_name;
662         //            _cons_vec = m._cons_vec;
663         //            _cons = m._cons;
664         //            _cons_name = m._cons_name;
665         //            _v_in_cons = m._v_in_cons;
666         //            _obj = m._obj->copy();
667         //            _objt = m._objt;
668         //            _status = m._status;
669         //            _hess_link = m._hess_link;
670         //            return *this;
671         //        }
672 
673         /** Constructor */
674         //@{
Model()675         Model(){
676             _obj = make_shared<func<type>>();
677         };
Model(const string & name)678         Model(const string& name){
679             _name = name;
680             _obj = make_shared<func<type>>();
681         };
682         //@}
683 
684 
685         /* Accessors */
686 
get_name()687         string get_name() const {return _name;}
688 
is_linear()689         bool is_linear() const{
690             return _type==lin_m;
691         }
692 
is_convex()693         bool is_convex() const{
694             return _convexity==convex_;
695         }
696 
is_concave()697         bool is_concave() const{
698             return _convexity==concave_;
699         }
700 
has_var(const string & name)701         bool has_var(const string& name) const{
702             return (_vars_name.count(name)!=0);
703         };
704 
has_var(const param_ & v)705         bool has_var(const param_& v) const{
706             return (_vars.count(v.get_vec_id())!=0);
707         };
708 
709 
710 
711         /* Modifiers */
712 
set_name(const string & name)713         void set_name(const string& name){
714             _name = name;
715         }
716 
add_var(const shared_ptr<param_> & v)717         void add_var(const shared_ptr<param_>& v){
718             switch (v->get_intype()) {
719                 case binary_:
720                     add(*static_pointer_cast<var<bool>>(v));
721                     break;
722                 case short_:
723                     add(*static_pointer_cast<var<short>>(v));
724                     break;
725                 case integer_:
726                     add(*static_pointer_cast<var<int>>(v));
727                     break;
728                 case float_:
729                     add(*static_pointer_cast<var<float>>(v));
730                     break;
731                 case double_:
732                     add(*static_pointer_cast<var<double>>(v));
733                     break;
734                 case long_:
735                     add(*static_pointer_cast<var<long double>>(v));
736                     break;
737                 case complex_:
738                     add(*static_pointer_cast<var<Cpx>>(v));
739                     break;
740                 default:
741                     break;
742             }
743         }
744 
745         template <typename T>
add_var(var<T> & v)746         void add_var(var<T>& v){//Add variables by copy
747             auto name = v._name.substr(0,v._name.find_first_of("."));
748             //            auto name = v._name;
749             v._name = name;
750             if (_vars_name.count(v._name)==0) {
751                 v.set_id(_nb_vars);
752                 v.set_vec_id(_vars.size());
753                 shared_ptr<param_> newv;
754                 if (!v._indices) {
755                     Warning("WARNING adding unindexed variable to model: " << name << endl);
756                     Warning("Treating it as a one dimensional Real.\n");
757                     newv = (v.in(R(1))).pcopy();
758                 }
759                 else {
760                     newv = v.pcopy();
761                 }
762                 if(!v._lift){
763                     auto lb_param = v._lb->_params->begin()->second.first;
764                     auto ub_param = v._ub->_params->begin()->second.first;
765                     auto it = _params_name.insert({lb_param->get_name(true,true), lb_param});
766                     if(it.second){
767                         lb_param->set_vec_id(_params.size());
768                         _params[_params.size()] = lb_param;
769                     }
770                     else{
771                         throw invalid_argument("bound param with same name");
772                     }
773                     it = _params_name.insert({ub_param->get_name(true,true), ub_param});
774                     if(it.second){
775                         ub_param->set_vec_id(_params.size());
776                         _params[_params.size()] = ub_param;
777                     }
778                     else{
779                         throw invalid_argument("bound param with same name");
780                     }
781                     v._lb->_val = static_pointer_cast<param<T>>(lb_param)->_val;
782                     v._ub->_val = static_pointer_cast<param<T>>(ub_param)->_val;
783                 }
784                 else {
785                     merge_vars(v._lb);
786                     merge_vars(v._ub);
787                 }
788                 _vars_name[name] = newv;
789                 _vars[v.get_vec_id()] = newv;
790                 _nb_vars += v.get_dim();
791             }
792             else {
793                 throw invalid_argument("adding variable with same name, please rename: " + v._name);
794             }
795         };
796 
797 
798         template <typename T>
add_var(var<T> && v)799         void add_var(var<T>&& v){//Add variables by copy
800             auto name = v._name.substr(0,v._name.find_first_of("."));
801             //            auto name = v._name;
802             v._name = name;
803             if (_vars_name.count(v._name)==0) {
804                 v.set_id(_nb_vars);
805                 v.set_vec_id(_vars.size());
806                 if(!v._lift){
807                     auto lb_param = v._lb->_params->begin()->second.first;
808                     auto ub_param = v._ub->_params->begin()->second.first;
809                     auto it = _params_name.insert({lb_param->get_name(true,true), lb_param});
810                     if(it.second){
811                         lb_param->set_vec_id(_params.size());
812                         _params[_params.size()] = lb_param;
813                     }
814                     else{
815                         throw invalid_argument("bound param with same name");
816                     }
817                     it = _params_name.insert({ub_param->get_name(true,true), ub_param});
818                     if(it.second){
819                         ub_param->set_vec_id(_params.size());
820                         _params[_params.size()] = ub_param;
821                     }
822                     else{
823                         throw invalid_argument("bound param with same name");
824                     }
825                     v._lb->_val = static_pointer_cast<param<T>>(lb_param)->_val;
826                     v._ub->_val = static_pointer_cast<param<T>>(ub_param)->_val;
827                 }
828                 else {
829                     merge_vars(v._lb);
830                     merge_vars(v._ub);
831                 }
832                 shared_ptr<param_> newv;
833                 if (!v._indices) {
834                     Warning("WARNING adding unindexed variable to model: " << name << endl);
835                     Warning("Treating it as a one dimensional Real.\n");
836                     newv = make_shared<var<T>>(move((v.in(R(1)))));
837                 }
838                 else {
839                     newv = make_shared<var<T>>(move(v));
840                 }
841                 _vars_name[name] = newv;
842                 _vars[v.get_vec_id()] = newv;
843                 _nb_vars += newv->get_dim();
844                 //                merge_vars(v._lb);
845                 //                merge_vars(v._ub);
846             }
847         };
848 
849         template <typename T>
add(var<T> & v)850         void add(var<T>& v){//Add variables by copy
851             add_var(v);
852         }
853 
854         template <typename T, typename... Args>
add(var<T> && v,Args &&...args)855         void add(var<T>&& v, Args&&... args){
856             list<var<T>> vars;
857             vars = {forward<var<T>>(args)...};
858             vars.push_front(move(v));
859             for (auto &v:vars) {
860                 add_var(move(v));
861             }
862         }
863 
864 
865 
866         /* Output */
867 
868 
869         /* Accessors */
870 
update_nb_vars()871         void update_nb_vars(){
872             size_t n = 0;
873             for (auto &vp:_vars) {
874                 n += vp.second->get_dim();
875             }
876             _nb_vars = n;
877         };
878 
get_nb_vars()879         size_t get_nb_vars() const{
880             return _nb_vars;
881         };
882 
883 
get_nb_cons()884         size_t get_nb_cons() const{
885             size_t n = 0;
886             for (auto &cp:_cons) {
887                 n += cp.second->get_nb_instances();
888             }
889             return n;
890         };
891 
892 
get_nb_ineq()893         size_t get_nb_ineq() const{
894             size_t n = 0;
895             for (auto &cp:_cons) {
896                 if (cp.second->is_ineq()) {
897                     n += cp.second->get_nb_instances();
898                 }
899             }
900             return n;
901         };
902 
get_nb_eq()903         size_t get_nb_eq() const{
904             size_t n = 0;
905             for (auto &cp:_cons) {
906                 if (cp.second->is_eq()) {
907                     n += cp.second->get_nb_instances();
908                 }
909             }
910             return n;
911         };
912 
913 
get_nb_nnz_g()914         size_t get_nb_nnz_g(){
915             _nnz_g = 0;
916             for (auto &cp:_cons) {
917                 auto c = cp.second;
918                 auto nb_inst = c->get_nb_inst();
919                 for (size_t inst = 0; inst<nb_inst; inst++) {
920                     if (!*c->_all_lazy || !c->_lazy[inst]) {
921                         _nnz_g += c->get_nb_vars(inst);
922                     }
923                 }
924             }
925             return _nnz_g;
926         };
927 
928         template <typename T>
get_var(const string & vname)929         var<T> get_var(const string& vname) const{
930             auto it = _vars_name.find(vname);
931             if (it==_vars_name.end()) {
932                 throw invalid_argument("In function: Model::get_var(const string& vname) const, unable to find variable with given name");
933             }
934             auto v = dynamic_pointer_cast<var<T>>(it->second);
935             if(v){
936                 return *v;
937             }
938             throw invalid_argument("In function: Model::get_var<T>(const string& vname) const, cannot cast variable, make sure to use the right numerical type T");
939         }
940 
941         /* Return the number of nonzeros in the lower left part of the hessian */
942 
get_nb_nnz_h()943         size_t get_nb_nnz_h(){
944             size_t idx = 0, vid, vjd;
945             string vi_name, vj_name;
946             shared_ptr<param_> vi;
947             shared_ptr<param_> vj;
948             shared_ptr<Constraint<type>> c;
949             for (auto &pairs: _hess_link) {
950                 vi_name = pairs.first.first;
951                 vj_name = pairs.first.second;
952                 vi = (pairs.second.begin())->second.first->get_var(vi_name);
953                 vj = (pairs.second.begin())->second.first->get_var(vj_name);
954                 if (vi_name.compare(vj_name) > 0) {//ONLY STORE LOWER TRIANGULAR PART OF HESSIAN
955                     throw invalid_argument("SHOULD BE SORTED CORRECTLY IN FILL_MAPS");
956                 }
957                 vid = vi->get_id();
958                 vjd = vj->get_id();
959 
960 
961 
962                 for (auto &f_pair:pairs.second) {
963                     auto f = f_pair.second.first;
964                     if (f->_is_constraint) {
965                         c = static_pointer_cast<Constraint<type>>(f);
966                     }
967                     auto d2f = f_pair.second.second;
968                     size_t nb_inst = f->get_nb_inst();
969                     for (size_t inst = 0; inst<nb_inst; inst++) {
970                         if (!(f->_is_constraint && *c->_all_lazy && c->_lazy[inst])) {
971                             if(d2f->is_matrix_indexed()){
972                                 auto dim = d2f->get_dim(inst);
973                                 for (size_t j = 0; j<dim; j++) {
974                                     if(_nnz_pairs.insert({{vid + vi->get_id_inst(inst,j),vjd+vj->get_id_inst(inst,j)}, idx}).second){
975                                         idx++;
976                                     };
977                                     _idx_it.push_back(_nnz_pairs.at({vid + vi->get_id_inst(inst,j),vjd+vj->get_id_inst(inst,j)}));
978                                 }
979                             }
980                             else if (d2f->is_matrix()) {
981                                 for (size_t i = 0; i < d2f->get_nb_inst(); i++) {
982                                     for (size_t j = i; j < d2f->_dim[1]; j++) {
983                                         if(_nnz_pairs.insert({{vid + vi->get_id_inst(i),vjd+vj->get_id_inst(j)}, idx}).second){
984                                             idx++;
985                                         };
986                                         _idx_it.push_back(_nnz_pairs.at({vid + vi->get_id_inst(i),vjd+vj->get_id_inst(j)}));
987                                     }
988                                 }
989                             }
990                             else if(d2f->_is_vector){
991                                 for (size_t j = 0; j < d2f->get_nb_inst(); j++) {
992                                     if(_nnz_pairs.insert({{vid + vi->get_id_inst(j),vjd+vj->get_id_inst(j)}, idx}).second){
993                                         idx++;
994                                     };
995                                     _idx_it.push_back(_nnz_pairs.at({vid + vi->get_id_inst(j),vjd+vj->get_id_inst(j)}));
996                                 }
997                             }
998                             else {
999                                 if(_nnz_pairs.insert({{vid + vi->get_id_inst(inst),vjd+vj->get_id_inst(inst)}, idx}).second){
1000                                     idx++;
1001                                 };
1002                                 _idx_it.push_back(_nnz_pairs.at({vid + vi->get_id_inst(inst),vjd+vj->get_id_inst(inst)}));
1003                             }
1004                         }
1005                     }
1006                 }
1007             }
1008             _nnz_h = _nnz_pairs.size();
1009             return _nnz_h;
1010         };
1011 
1012 
1013 
get_constraint(const string & cname)1014         shared_ptr<Constraint<type>> get_constraint(const string& cname) const{
1015             return _cons_name.at(cname);
1016         }
1017 
1018 
get_var_ptr(const string & vname)1019         shared_ptr<param_> get_var_ptr(const string& vname) const{
1020             auto it = _vars_name.find(vname);
1021             if (it==_vars_name.end()) {
1022                 return nullptr;
1023             }
1024             return it->second;
1025         }
1026 
get_var_ptr(size_t idx)1027         shared_ptr<param_> get_var_ptr(size_t idx) const{
1028             return _vars.at(idx);
1029         }
1030 
1031 
1032         /* Modifiers */
1033 
1034         /** Reindexes the constraints after violated ones have been detected and added to the formulation */
reindex()1035         void reindex(){
1036             size_t cid = 0, new_cid = 0, nb_inst = 0;
1037             shared_ptr<Constraint<type>> c = nullptr;
1038             map<size_t, shared_ptr<Constraint<type>>>  new_cons;
1039             _idx_it.clear();
1040             _cons_vec.clear();
1041             _nnz_pairs.clear();
1042             _jac_vals.clear();
1043             _hess_vals.clear();
1044             _first_call_jac = true;
1045             _first_call_hess = true;
1046             _first_call_grad_obj = true;
1047             _obj_grad_vals.clear();
1048             _nl_funcs_map.clear();
1049             _nl_funcs.clear();
1050             _type = lin_m;
1051             for(auto& c_p: _cons_name)
1052             {
1053                 c = c_p.second;
1054                 c->allocate_mem();
1055                 nb_inst = c->get_nb_instances();
1056                 if (nb_inst==0) {
1057                     continue;
1058                 }
1059                 if (_type==lin_m && c->is_quadratic()) {
1060                     _type = quad_m;
1061                 }
1062                 else if ((_type==lin_m || _type==quad_m) && c->is_polynomial()) {
1063                     _type = pol_m;
1064                 }
1065                 else if (c->is_nonlinear()) {
1066                     _type = nlin_m;
1067                 }
1068                 update_convexity(*c);
1069                 cid = c->_id;
1070                 if (cid!=new_cid) {
1071                     c->_id = new_cid;
1072                 }
1073                 new_cons[c->_id] = c;
1074                 _cons_vec.push_back(c);
1075                 embed(c);
1076                 new_cid = c->_id+nb_inst;
1077             }
1078             _cons = new_cons;
1079             _nb_cons = get_nb_cons();
1080             embed(_obj);
1081             _first_run = true;
1082         }
1083 
1084 
reindex_vars()1085         void reindex_vars(){/**< Re-index all variables involved in the model and update total number of variables */
1086             shared_ptr<param_> v= nullptr;
1087             size_t idx = 0, vec_idx = 0;
1088             for(auto& v_p: _vars)
1089             {
1090                 v = v_p.second;
1091                 v->set_vec_id(vec_idx++);
1092                 v->set_id(idx);
1093                 idx += v->get_dim();
1094             }
1095             _nb_vars = idx;
1096         }
1097 
1098 
del_var(const param_ & v)1099         void del_var(const param_& v){
1100             auto it = _vars.find(v.get_id());
1101             if (it!=_vars.end()) {
1102                 _nb_vars -= v.get_dim();
1103                 _vars.erase(it);
1104             }
1105             reindex_vars();
1106         };
1107 
1108         //generic add constraint
1109         //INPUT: a constraint, lifting option boolean, disjunctive union methods from ("on/off", "lambda_II", "lambda_III")
1110         //OUTPUT: addition of this constraint in the model, if lift is selected, the convex relaxation and the partitioning of the individual variables will be also included to the mathematical formulation
1111         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
1112         void add(const Constraint<Cpx>& c, bool convexify = false, string method_type = "on/off", bool split=true){
1113             if (c.get_dim()==0) {
1114                 return;
1115             }
1116             auto real_imag = get_real_imag(c);
1117             Constraint<type> c_real;
1118             c_real += real_imag.first;
1119             c_real._name = "Real(" + c._name + ")";
1120             c_real._ctype = c._ctype;
1121             c_real._indices = c._indices;
1122             c_real._dim[0] = c._dim[0];
1123             Constraint<type> c_imag;
1124             c_imag += real_imag.second;
1125             c_imag._name = "Imag(" + c._name + ")";
1126             c_imag._ctype = c._ctype;
1127             c_imag._indices = c._indices;
1128             c_imag._dim[0] = c._dim[0];
1129             if(convexify){
1130                 c_real.check_soc();c_real.check_rotated_soc();
1131                 auto lifted_real = lift(c_real, method_type);
1132                 c_imag.check_soc();c_imag.check_rotated_soc();
1133                 auto lifted_imag = lift(c_imag, method_type);
1134                 lifted_real._ctype = c._ctype;
1135                 lifted_real._indices = c._indices;
1136                 lifted_real._dim[0] = c._dim[0];
1137                 lifted_imag._ctype = c._ctype;
1138                 lifted_imag._indices = c._indices;
1139                 lifted_imag._dim[0] = c._dim[0];
1140                 add_constraint(lifted_real);
1141                 add_constraint(lifted_imag);
1142                 if(c_real.func<type>::is_convex() && c_real._ctype==eq && split){
1143                     DebugOn("Convex left hand side of equation detected, splitting constraint into <= and ==" << endl);
1144                     Constraint<type> c_real_cvx(c_real._name+"_convex");
1145                     c_real_cvx = c_real;
1146                     c_real_cvx._relaxed = true;
1147                     add_constraint(c_real_cvx <= 0);
1148                 }
1149                 if(c_real.func<type>::is_concave() && c_real._ctype==eq && split){
1150                     DebugOn("Concave left hand side of equation detected, splitting constraint into >= and ==" << endl);
1151                     Constraint<type> c_real_ccve(c_real._name+"_concave");
1152                     c_real_ccve = c_real;
1153                     c_real_ccve._relaxed = true;
1154                     add_constraint(c_real_ccve >= 0);
1155                 }
1156                 if(c_imag.func<type>::is_convex() && c_real._ctype==eq && split){
1157                     DebugOn("Convex left hand side of equation detected, splitting constraint into <= and ==" << endl);
1158                     Constraint<type> c_imag_cvx(c_imag._name+"_convex");
1159                     c_imag_cvx = c_imag;
1160                     c_imag_cvx._relaxed = true;
1161                     add_constraint(c_imag_cvx <= 0);
1162                 }
1163                 if(c_imag.func<type>::is_concave() && c_real._ctype==eq && split){
1164                     DebugOn("Concave left hand side of equation detected, splitting constraint into >= and ==" << endl);
1165                     Constraint<type> c_imag_ccve(c_imag._name+"_concave");
1166                     c_imag_ccve = c_imag;
1167                     c_imag_ccve._relaxed = true;
1168                     add_constraint(c_imag_ccve >= 0);
1169                 }
1170             }
1171             else {
1172                 add_constraint(c_real);
1173                 add_constraint(c_imag);
1174             }
1175         }
1176 
1177         /** Reformulate rotated SOCs into standard SOCs and add linking constraints to the model.
1178          @param[in] c: constraint to reformulate
1179          @return the linearized constraint
1180          @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.
1181          **/
1182 //        template<typename T=type,typename std::enable_if<is_arithmetic<type>::value>::type* = nullptr>
1183 //        Constraint<type> get_standard_SOC(Constraint<type>& c){
1184 //            if(!c.is_rotated_soc()){
1185 //                return c;
1186 //            }
1187 //            Constraint<type> newc_standard(c._name+"_standard");
1188 //            if (!c.get_cst()->is_zero()) { //here we extract the constant part of the constraint
1189 //                if (c.get_cst()->is_number()) {
1190 //                    auto f_cst = static_pointer_cast<constant<type>>(c.get_cst());
1191 //                    newc_standard.add_cst(*f_cst);
1192 //                }
1193 //                else if (c.get_cst()->is_param()) {
1194 //                    auto f_cst = static_pointer_cast<param<type>>(c.get_cst());
1195 //                    newc_standard.add_cst(*f_cst);
1196 //                }
1197 //                else {
1198 //                    auto f_cst = static_pointer_cast<func<type>>(c.get_cst());
1199 //                    newc_standard.add_cst(*f_cst);
1200 //                }
1201 //                if (newc_standard._cst->is_function()) {
1202 //                    newc_standard.embed(*static_pointer_cast<func<type>>(newc_standard._cst));
1203 //                }
1204 //            }
1205 //            for (auto &pair:*c._lterms) { //here we extract the linear terms appearing in the constraint
1206 //                auto term = pair.second;
1207 //                newc_standard.insert(term);
1208 //            }
1209 //            //go over the quadratic terms (including the bilinears)
1210 //            //here we will introduce two auxiliary variables for the bilinear term
1211 //            for (auto &pair:*c._qterms) {
1212 //                if (pair.second._p->first!=pair.second._p->second) { //means it is bilinear term
1213 //                    qterm lt1;
1214 //                    qterm lt2;
1215 //                    lt1._sign = !(pair.second._sign);
1216 //                    lt2._sign = pair.second._sign;
1217 //                    if (pair.second._coef->is_function()) {
1218 //                        auto coef = *static_pointer_cast<func<type>>(pair.second._coef);
1219 //                        lt1._coef = func<type>(coef).copy();
1220 //                        lt2._coef = func<type>(coef).copy();
1221 //                    }
1222 //                    else if(pair.second._coef->is_param()) {
1223 //                        auto coef = *static_pointer_cast<param<type>>(pair.second._coef);
1224 //                        lt1._coef = param<type>(coef).copy();
1225 //                        lt2._coef = param<type>(coef).copy();
1226 //                    }
1227 //                    else if(pair.second._coef->is_number()) {
1228 //                        auto coef = *static_pointer_cast<constant<type>>(pair.second._coef);
1229 //                        lt1._coef = constant<type>(coef).copy();
1230 //                        lt2._coef = constant<type>(coef).copy();
1231 //                    }
1232 //
1233 //                    auto v1 = *static_pointer_cast<var<type>>(pair.second._p->first); //assign the pointers to the variables
1234 //                    auto v2 = *static_pointer_cast<var<type>>(pair.second._p->second);
1235 //
1236 //                    if((v1._name > v2._name)){    //get the variables in the alphabetical order
1237 //                        v2 = *static_pointer_cast<var<type>>(pair.second._p->first);
1238 //                        v1 = *static_pointer_cast<var<type>>(pair.second._p->second);
1239 //                    }
1240 //                    auto ids = combine(*v1._indices,*v2._indices); //get the combined index set
1241 //
1242 //                    // I am not sure that we need the following part or not!! ***************************************************************************************************************************************
1243 //                    auto unique_ids = ids.get_unique_keys(); /* In case of an indexed variable, keep the unique keys only */
1244 //                    auto v1_ids = *v1._indices;
1245 //                    auto v2_ids = *v2._indices;
1246 //                    if(unique_ids.size()!=ids.size()){/* If some keys are repeated, remove them from the refs of o1 and o2 */
1247 //                        auto keep_refs = ids.get_unique_refs();
1248 //                        v1_ids.filter_refs(keep_refs);
1249 //                        v2_ids.filter_refs(keep_refs);
1250 //                    }
1251 //                    // ***************************************************************************************************************************************
1252 //
1253 //                    param<type> lb1("lb1"), ub1("ub1");
1254 //                    lb1.in(unique_ids);ub1.in(unique_ids);
1255 //                    param<type> lb2("lb2"), ub2("ub2");
1256 //                    lb2.in(unique_ids);ub2.in(unique_ids);
1257 //
1258 //                    //get the bounds for the auxiliary variables y1 and y2
1259 //                    for (int i=0; i<unique_ids.size(); i++) {
1260 //                        size_t id1;
1261 //                        size_t id2;
1262 //                        if(v1_ids._ids == nullptr){
1263 //                            id1 = i;
1264 //                        }
1265 //                        else id1 = v1_ids._ids->at(0).at(i);
1266 //                        if(v2_ids._ids == nullptr){
1267 //                            id2 = i;
1268 //                        }
1269 //                        else id2 = v2_ids._ids->at(0).at(i);
1270 //                        auto key1 = v1_ids._keys->at(id1);
1271 //                        auto key2 = v2_ids._keys->at(id2);
1272 //
1273 //                        auto sum_b1 = v1.get_lb(key1) - v2.get_ub(key2);
1274 //                        auto sum_b2 = v1.get_ub(key1) - v2.get_lb(key2);
1275 //                        auto sum_b3 = v1.get_lb(key1) + v2.get_lb(key2);
1276 //                        auto sum_b4 = v1.get_ub(key1) + v2.get_ub(key2);
1277 //
1278 //                        lb1.set_val(key1+","+key2, sum_b1/2);
1279 //                        ub1.set_val(key1+","+key2, sum_b2/2);
1280 //
1281 //                        lb2.set_val(key1+","+key2, sum_b3/2);
1282 //                        ub2.set_val(key1+","+key2, sum_b4/2);
1283 //                    }
1284 //                    //define the names of auxiliary variables to check their prior existence in the model
1285 //                    string aux1_name = "aux1("+v1.get_name(true,true)+v2.get_name(true,true)+")";
1286 //                    string aux2_name = "aux2("+v1.get_name(true,true)+v2.get_name(true,true)+")";
1287 //
1288 //                    auto it1 = _vars_name.find(aux1_name);
1289 //                    auto it2 = _vars_name.find(aux2_name);
1290 //
1291 //                    if(it1==_vars_name.end()){ //means the variables have not been included in the model, so create new ones, and add them to the model
1292 //                        //define variables
1293 //                        var<type> y1(aux1_name, lb1, ub1);
1294 //                        add(y1.in(unique_ids));
1295 //                        *y1._num_partns = *v1._num_partns + *v2._num_partns;
1296 //                        lt1._p = make_shared<gravity::pair< shared_ptr<param_>,shared_ptr<param_> >>(make_pair(make_shared<var<type>>(y1.in(ids)), make_shared<var<type>>(y1.in(ids))));
1297 //                        var<type> y2(aux2_name, lb2, ub2);
1298 //                        add(y2.in(unique_ids));
1299 //                        lt2._p = make_shared<gravity::pair< shared_ptr<param_>,shared_ptr<param_> >>(make_pair(make_shared<var<type>>(y2.in(ids)), make_shared<var<type>>(y2.in(ids))));
1300 //                        //add constraints
1301 //                        Constraint<type> link1(pair.first+"_link1");
1302 //                        link1 = y1.in(unique_ids) - (v1.in(v1_ids) - v2.in(v2_ids))/2;
1303 //                        add(link1.in(unique_ids) == 0);
1304 //
1305 //                        Constraint<type> link2(pair.first+"_link2");
1306 //                        link2 = y2.in(unique_ids) - (v1.in(v1_ids) + v2.in(v2_ids))/2;
1307 //                        add(link2.in(unique_ids) == 0);
1308 //                    }
1309 //                    else{ //if the variables are already included in the model before, access the correct pointer and add the new indices/linkage
1310 //                        //get variables
1311 //                        auto y1 = static_pointer_cast<var<type>>(it1->second);
1312 //                        auto added1 = y1->add_bounds(lb1,ub1);
1313 //                        *y1->_num_partns = *v1._num_partns + *v2._num_partns;
1314 //                        lt1._p = make_shared<gravity::pair< shared_ptr<param_>,shared_ptr<param_> >>(make_pair(make_shared<var<type>>(y1->in(ids)), make_shared<var<type>>(y1->in(ids))));
1315 //                        if(!added1.empty()){
1316 //                            assert(v1._indices->size()==v2._indices->size());
1317 //                            if(added1.size()!=v1._indices->size()){/* If some keys are repeated, remove them from the refs of o1 and o2 */
1318 //                                auto keep_refs = ids.diff_refs(added1);
1319 //                                v1_ids.filter_refs(keep_refs);
1320 //                                v2_ids.filter_refs(keep_refs);
1321 //                            }
1322 //                            reindex_vars();
1323 //                        }
1324 //                        auto y2 = static_pointer_cast<var<type>>(it2->second);
1325 //                        auto added2 = y2->add_bounds(lb2,ub2);
1326 //                        lt2._p = make_shared<gravity::pair< shared_ptr<param_>,shared_ptr<param_> >>(make_pair(make_shared<var<type>>(y2->in(ids)), make_shared<var<type>>(y2->in(ids))));
1327 //                        if(!added2.empty()){
1328 //                            assert(v1._indices->size()==v2._indices->size());
1329 //                            if(added2.size()!=v1._indices->size()){/* If some keys are repeated, remove them from the refs of o1 and o2 */
1330 //                                auto keep_refs = ids.diff_refs(added2);
1331 //                                v1_ids.filter_refs(keep_refs);
1332 //                                v2_ids.filter_refs(keep_refs);
1333 //                            }
1334 //                            reindex_vars();
1335 //                        }
1336 //
1337 //                        //create constraints for linking the auxiliary variables to the bilinear term
1338 //                        Constraint<type> link1(pair.first+"_link1");
1339 //                        link1 = y1->in(added1) - (v1.in(v1_ids) - v2.in(v2_ids))/2;
1340 //                        add(link1.in(unique_ids) == 0);
1341 //
1342 //                        Constraint<type> link2(pair.first+"_link2");
1343 //                        link2 = y2->in(added2) - (v1.in(v1_ids) + v2.in(v2_ids))/2;
1344 //                        add(link2.in(unique_ids) == 0);
1345 //
1346 //                    }
1347 //                    //insert the standardized terms into the new constraint
1348 //                    newc_standard.insert(lt1);
1349 //                    newc_standard.insert(lt2);
1350 //                }
1351 //                else { /* simply insert the squared term (not bilinear) */
1352 //                    newc_standard.insert(pair.second);
1353 //                }
1354 //            }
1355 //            //set the other properties of the standardized constraint properly
1356 //            newc_standard._range = c._range;
1357 //            newc_standard._all_convexity = c._all_convexity;
1358 //            newc_standard._all_sign = c._all_sign;
1359 //            newc_standard._ftype = c._ftype;
1360 //            newc_standard._ctype = c._ctype;
1361 //            newc_standard._indices = c._indices;
1362 //            newc_standard._dim[0] = c._dim[0];
1363 //            newc_standard._dim[1] = c._dim[1];
1364 //            return newc_standard;
1365 //        }
1366 
1367 
1368         /** Lift and linearize the nonlinear constraint c, return the linearized form and add linking constraints to the model.
1369          @param[in] c: constraint to linearize
1370          @param[in] partition_model: formulation used for partitionning the nonconvex parts of the constraint
1371          @return the linearized constraint
1372          @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.
1373          **/
1374         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
1375         Constraint<type> lift(Constraint<type>& c, string model_type);
1376 
1377         template<class T=type, typename enable_if<is_same<T, Cpx>::value>::type* = nullptr>
1378         Constraint<type> lift(Constraint<type>& c, string model_type);
1379 
1380         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
add_real(const Constraint<Cpx> & c)1381         void add_real(const Constraint<Cpx>& c){
1382             if (c.get_dim()==0) {
1383                 return;
1384             }
1385             auto real_imag = get_real_imag(c);
1386             Constraint<type> c_real;
1387             c_real += real_imag.first;
1388             c_real._name = "Real(" + c._name + ")";
1389             c_real._ctype = c._ctype;
1390             c_real._indices = c._indices;
1391             c_real._dim[0] = c._dim[0];
1392             add_constraint(c_real);
1393         }
1394 
1395         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
add_imag(const Constraint<Cpx> & c)1396         void add_imag(const Constraint<Cpx>& c){
1397             if (c.get_dim()==0) {
1398                 return;
1399             }
1400             auto real_imag = get_real_imag(c);
1401             Constraint<type> c_imag;
1402             c_imag += real_imag.second;
1403             c_imag._name = "Imag(" + c._name + ")";
1404             c_imag._ctype = c._ctype;
1405             c_imag._indices = c._indices;
1406             c_imag._dim[0] = c._dim[0];
1407             add_constraint(c_imag);
1408         }
1409 
1410         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
1411         void add(Constraint<Cpx>&& c, bool convexify = false, string model_type = "on/off"){
1412             if (c.get_dim()==0) {
1413                 return;
1414             }
1415             auto real_imag = get_real_imag(c);
1416             Constraint<type> c_real = real_imag.first;
1417             c_real._name = "Real(" + c._name + ")";
1418             c_real._ctype = c._ctype;
1419             c_real._indices = c._indices;
1420             Constraint<type> c_imag = real_imag.first;
1421             c_imag._name = "Imag(" + c._name + ")";
1422             c_imag._ctype = c._ctype;
1423             c_imag._indices = c._indices;
1424             if(convexify){
1425                 add(lift(c_real, model_type));
1426                 add(lift(c_imag, model_type));
1427             }
1428             else {
1429                 add_constraint(c_real);
1430                 add_constraint(c_imag);
1431             }
1432         }
1433 
1434 
1435 
1436         void add(Constraint<type>& c, bool convexify = false, string method_type = "on/off", bool split=true){
1437             if (c.get_dim()==0) {
1438                 return;
1439             }
1440             add_constraint(c,convexify, method_type, split);
1441         }
1442 
1443 
1444         void add_lazy(Constraint<type>& c, bool convexify = false){
1445             if (c.get_dim()==0) {
1446                 return;
1447             }
1448             c.make_lazy();
1449             add_constraint(c, convexify);
1450             _has_lazy = true;
1451         }
1452 
add_callback(Constraint<type> & c)1453         void add_callback(Constraint<type>& c){/**<  Adds a callback to the model with given constraints (maybe parameters and variables are needed!). */
1454             if (c.get_dim()==0) {
1455                 return;
1456             }
1457             //            c.make_lazy();   //Should we do a similar thing like this?
1458             add_constraint(c);
1459             _has_callback = true;
1460         }
1461 
1462         /* Rescale functions to make sure all variables' bounds are in [-unit,unit] */
scale_func_vars(shared_ptr<expr<type>> & e,double unit)1463         void scale_func_vars(shared_ptr<expr<type>>& e, double unit){
1464             if(e->is_uexpr()){
1465                 auto ue = (uexpr<type>*)e.get();
1466                 if (ue->_son->is_function()) {
1467                     auto f = static_pointer_cast<func<type>>(ue->_son);
1468                     scale_func_vars(*f, unit);
1469                 }
1470                 else if (ue->_son->is_var()){
1471                     func<type> f = func<type>(*static_pointer_cast<var<type>>(ue->_son));
1472                     scale_func_vars(f, unit);
1473                     ue->_son = f.copy();
1474                 }
1475             }
1476             else{
1477                 auto be = (bexpr<type>*)e.get();
1478                 if (be->_lson->is_function()) {
1479                     auto f = static_pointer_cast<func<type>>(be->_lson);
1480                     scale_func_vars(*f, unit);
1481                 }
1482                 else if (be->_lson->is_var()){
1483                     func<type> f = func<type>(*static_pointer_cast<var<type>>(be->_lson));
1484                     scale_func_vars(f, unit);
1485                     be->_lson = f.copy();
1486                 }
1487                 if (be->_rson->is_function()) {
1488                     auto f = static_pointer_cast<func<type>>(be->_rson);
1489                     scale_func_vars(*f, unit);
1490                 }
1491                 else if (be->_rson->is_var()){
1492                     func<type> f = func<type>(*static_pointer_cast<var<type>>(be->_rson));
1493                     scale_func_vars(f, unit);
1494                     be->_rson = f.copy();
1495                 }
1496             }
1497         }
1498 
1499         /* Rescale functions to make sure all variables' bounds are in [-unit,unit] */
scale_func_vars(func<type> & f,double unit)1500         void scale_func_vars(func<type>& f, double unit){
1501             for (auto &lt:f.get_lterms()) {
1502                 auto vv = _vars.at(*lt.second._p->_vec_id);
1503                 double factor = 1;
1504                 if(!vv->is_lifted()) {
1505                     factor = vv->get_scale_factor(unit);
1506                 }
1507                 else {
1508                     auto vv1 = _vars.at(*vv->get_original_vars()[0]->_vec_id);
1509                     auto vv2 = _vars.at(*vv->get_original_vars()[1]->_vec_id);
1510                     factor = vv1->get_scale_factor(unit)*vv2->get_scale_factor(unit);
1511                 }
1512                 if(factor!=1){
1513                     lt.second._coef = f.multiply(lt.second._coef, constant<>(1./factor));
1514                     lt.second._coef->uneval();
1515                 }
1516 
1517             }
1518             for (auto &qt:f.get_qterms()) {
1519                 double factor1 = 1, factor2 = 1;
1520                 auto vv1 = _vars.at(*qt.second._p->first->_vec_id);
1521                 if(!vv1->is_lifted()) {
1522                     factor1 = vv1->get_scale_factor(unit);
1523                 }
1524                 else {
1525                     auto vv11 = _vars.at(*vv1->get_original_vars()[0]->_vec_id);
1526                     auto vv12 = _vars.at(*vv1->get_original_vars()[1]->_vec_id);
1527                     factor1 = vv11->get_scale_factor(unit)*vv12->get_scale_factor(unit);
1528                 }
1529                 auto vv2 = _vars.at(*qt.second._p->second->_vec_id);
1530                 if(!vv2->is_lifted()) {
1531                     factor2 = vv2->get_scale_factor(unit);
1532                 }
1533                 else {
1534                     auto vv21 = _vars.at(*vv2->get_original_vars()[0]->_vec_id);
1535                     auto vv22 = _vars.at(*vv2->get_original_vars()[1]->_vec_id);
1536                     factor2 = vv21->get_scale_factor(unit)*vv22->get_scale_factor(unit);
1537                 }
1538                 if(factor1!=1 || factor2!=1){
1539                     qt.second._coef = f.multiply(qt.second._coef, constant<>(1./(factor1*factor2)));
1540                     qt.second._coef->uneval();
1541                 }
1542             }
1543             for (auto &pt:f.get_pterms()) {
1544                 double factor = 1;
1545                 for (auto &vpair: *pt.second._l) {
1546                     auto vv = _vars.at(*vpair.first->_vec_id);
1547                     if(!vv->is_lifted()) {
1548                         factor *= vv->get_scale_factor(unit);
1549                     }
1550                     else {
1551                         auto vv1 = _vars.at(*vv->get_original_vars()[0]->_vec_id);
1552                         auto vv2 = _vars.at(*vv->get_original_vars()[1]->_vec_id);
1553                         factor *= vv1->get_scale_factor(unit)*vv2->get_scale_factor(unit);
1554                     }
1555                 }
1556                 if(factor!=1){
1557                     pt.second._coef = f.multiply(pt.second._coef, constant<>(1./factor));
1558                     pt.second._coef->uneval();
1559                 }
1560             }
1561             if(f._expr){
1562                 scale_func_vars(f._expr, unit);
1563             }
1564             f._cst->uneval();
1565         }
1566 
1567         /* Make sure all variables have bounds in [-unit,unit] */
1568         template<typename T=type,
1569         typename std::enable_if<is_same<T,double>::value>::type* = nullptr>
scale_vars(double unit)1570         void scale_vars(double unit){
1571             scale_func_vars(*_obj, unit);
1572             for (auto &c_p: _cons_name) {
1573                 if(c_p.first.find("_McCormick")==string::npos && c_p.first.find("_diag")==string::npos && c_p.first.find("_Secant")==string::npos)
1574                     scale_func_vars(*c_p.second, unit);
1575             }
1576             for(auto &vp: _vars){
1577                 if(!vp.second->is_lifted()){
1578                     vp.second->scale(unit);
1579                 }
1580             }
1581             for(auto &vp: _vars){
1582                 if(vp.second->is_lifted()){
1583                     vp.second->reset_bounds();
1584                 }
1585             }
1586             _obj->uneval();
1587             _obj->eval_all();
1588             for (auto &c_p: _cons_name) {
1589                 c_p.second->uneval();
1590                 c_p.second->eval_all();
1591             }
1592         }
1593 
1594         /* Make sure all coefficients in objective and constraints have values in [-unit,unit] */
1595         template<typename T=type,
1596         typename std::enable_if<is_same<T,double>::value>::type* = nullptr>
scale_coefs(double unit)1597         void scale_coefs(double unit){
1598 //            _obj->scale_coefs(unit);
1599             for (auto &c_p: _cons_name) {
1600                 c_p.second->scale_coefs(unit);
1601                 c_p.second->uneval();
1602                 c_p.second->eval_all();
1603             }
1604         }
1605 
1606         template<typename T=type>
replace(const var<T> & v,const func<T> & f,list<shared_ptr<Constraint<type>>> & eq_list)1607         void replace(const var<T>& v, const func<T>& f, list<shared_ptr<Constraint<type>>>& eq_list){/**<  Replace v with function f everywhere it appears */
1608             if(_obj->has_sym_var(v)){
1609                 *_obj = _obj->replace(v, f);
1610                 _obj->_dim[0] = 1;
1611                 _obj->_indices = nullptr;
1612                 _obj->_is_constraint = false;
1613             }
1614             for (auto &c_p: _cons_name) {
1615                 auto c = c_p.second;
1616                 if (!c->_is_constraint || !c->has_sym_var(v)) {
1617                     continue;
1618                 }
1619                 DebugOn("After replacing " << v.get_name(true,true) << " in " << c->get_name() << ": " << endl);
1620                 auto new_c = c->replace(v, f);
1621                 if(new_c.get_dim()>0 && new_c._indices && new_c._indices->size()!=c->_indices->size()){
1622                     DebugOn("Projected constraint: " << endl);
1623                     new_c.print();
1624                     if(new_c._ctype==eq){
1625                         eq_list.push_back(this->add_constraint(new_c));
1626                     }
1627                     else {
1628                         this->add_constraint(new_c);
1629                     }
1630                     auto diff_refs = c->_indices->get_diff_refs(*new_c._indices);
1631                     c->update_indices(diff_refs);
1632                     c->_indices->keep_unique_keys();
1633                     c->print();
1634                 }
1635                 else{
1636                     *c = new_c;
1637                     c->allocate_mem();
1638                     c->print();
1639                 }
1640             }
1641             if(v.is_bounded_below()){
1642                 Constraint<> v_lb(v.get_name(false,false)+"_LB");
1643                 v_lb = f - v.get_lb();
1644                 add(v_lb.in(*v._indices) >= 0);
1645             }
1646             if(v.is_bounded_above()) {
1647                 Constraint<> v_ub(v.get_name(false,false)+"_UB");
1648                 v_ub = f - v.get_ub();
1649                 add(v_ub.in(*v._indices) <= 0);
1650             }
1651 //            _vars_name.erase(v->_name);
1652 //            auto vid = *v->_vec_id;
1653 //            _vars.erase(vid);
1654 //            reindex_vars();
1655         }
1656 
1657         template<typename T=type,
1658         typename std::enable_if<is_same<T,double>::value>::type* = nullptr>
1659         shared_ptr<Model<T>> relax(unsigned determinant_level = 0, bool add_Kim_Kojima = false, bool add_SDP_3d = false, bool add_RLT = false) const{/*<  return a convex relaxation of the current model */
1660 
1661             auto g = get_interaction_graph();
1662             g.get_tree_decomp_bags();
1663             auto bags_3d=g.decompose_bags_3d();
1664             auto bag_size = bags_3d.size();
1665             DebugOn("bags \n");
1666             for(auto bag:bags_3d){
1667                 DebugOn(bag.second[0]->_name<<"\t"<<bag.second[1]->_name<<"\t"<<bag.second[2]->_name<<"\n");
1668             }
1669 
1670             auto indices=g.get_pairs_chord(bags_3d);
1671             auto pairs = indices[0];
1672             auto pairs_from = indices[1];
1673             auto pairs_to = indices[2];
1674             DebugOn("Node pairs of chordal graph\n");
1675             for(auto k: *pairs._keys){
1676                 DebugOn(k<<endl);
1677             }
1678 
1679             shared_ptr<Model<T>> relax = make_shared<Model<T>>(_name+"_relaxed");
1680             for(auto &vp: _vars){
1681                 switch (vp.second->get_intype()) {
1682                     case binary_: {
1683                         auto vv = *static_pointer_cast<var<bool>>(vp.second);
1684                         relax->add(vv.deep_copy());
1685                         break;
1686                     }
1687                     case short_: {
1688                         auto vv = *static_pointer_cast<var<short>>(vp.second);
1689                         relax->add(vv.deep_copy());
1690                         break;
1691                     }
1692                     case integer_: {
1693                         auto vv = *static_pointer_cast<var<int>>(vp.second);
1694                         relax->add(vv.deep_copy());
1695                         break;
1696                     }
1697                     case float_: {
1698                         auto vv = *static_pointer_cast<var<float>>(vp.second);
1699                         relax->add(vv.deep_copy());
1700                         break;
1701                     }
1702                     case double_: {
1703                         auto vv = *static_pointer_cast<var<double>>(vp.second);
1704                         relax->add(vv.deep_copy());
1705                         break;
1706                     }
1707                     case long_: {
1708                         auto vv = *static_pointer_cast<var<long double>>(vp.second);
1709                         relax->add(vv.deep_copy());
1710                         break;
1711                     }
1712                     case complex_: {
1713                         auto vv = *static_pointer_cast<var<Cpx>>(vp.second);
1714                         relax->add(vv.deep_copy());
1715                         break;
1716                     }
1717                 }
1718             }
1719             for(auto &cp: _cons_name){
1720                 auto c_cpy = make_shared<Constraint<T>>();
1721                 c_cpy->deep_copy(*cp.second);
1722                 relax->merge_vars(c_cpy,true);
1723                 c_cpy->uneval();
1724                 if(*c_cpy->_all_lazy){
1725                     if(c_cpy->is_convex()){
1726                         relax->add_lazy(*c_cpy);
1727                     }
1728                     else {
1729                         relax->add_lazy(*c_cpy,true);
1730                     }
1731                 }
1732                 else{
1733                     if(cp.second->is_convex()){
1734                         relax->add(*c_cpy);
1735                     }
1736                     else {
1737                         relax->add(*c_cpy,true);
1738                     }
1739                 }
1740             }
1741             if(_obj->is_convex()){
1742                 func<T> new_obj = *_obj;
1743                 relax->set_objective(new_obj, _objt);
1744             }
1745             else {
1746                 param<> obj_lb("obj_lb");
1747                 param<> obj_ub("obj_ub");
1748                 obj_lb = _obj->_range->first;
1749                 obj_ub = _obj->_range->second;
1750                 var<type> v_obj("v_obj",obj_lb,obj_ub);
1751                 relax->add(v_obj);
1752                 auto obj_cpy = make_shared<func<T>>();
1753                 obj_cpy->deep_copy(*_obj);
1754                 relax->merge_vars(obj_cpy,true);
1755                 Constraint<T> obj("obj");
1756                 obj = v_obj - *obj_cpy;
1757                 if(_objt==minimize){
1758                     if(obj.is_convex())
1759                         relax->add(obj >=0);
1760                     else
1761                         relax->add(obj >=0,true);
1762                 }
1763                 else if(_objt==maximize){
1764                     if(obj.is_concave())
1765                         relax->add(obj <=0);
1766                     else
1767                         relax->add(obj <=0,true);
1768                 }
1769                 relax->set_objective(v_obj, _objt);
1770             }
1771 
1772             if(determinant_level>1){
1773                 for(auto &vp: relax->_vars){
1774                     if (vp.second->get_intype()==double_) {
1775                         auto vv = static_pointer_cast<var<double>>(vp.second);
1776                         if(vv->_lift && vv->_name.find("^2")==string::npos){
1777                             DebugOn("OffDiagonal element: " << vv->get_name(true,true) << endl);
1778                             DebugOn("Original var1: " << vv->_original_vars[0]->get_name(true,true) << endl);
1779                             DebugOn("Original var2: " << vv->_original_vars[1]->get_name(true,true) << endl);
1780                             auto name1 = "Lift("+vv->_original_vars[0]->get_name(true,true)+"^2)";
1781                             DebugOn("Diagonal element 1: " << name1 << endl);
1782                             auto name2 = "Lift("+vv->_original_vars[1]->get_name(true,true)+"^2)";
1783                             DebugOn("Diagonal element 2: " << name2 << endl);
1784                             if (relax->_cons_name.count(name1+"_diag")==0) {
1785                                 auto v1 = relax->template get_var<double>(vv->_original_vars[0]->get_name(true,true));
1786                                 Constraint<> diag(name1+"_diag");
1787                                 if(v1.is_non_negative()){
1788                                     diag = pow(v1,2) - v1.get_ub()*v1;
1789                                 }
1790                                 else {
1791                                     diag = pow(v1,2) - pow(v1.get_ub(),2);
1792                                 }
1793                                 relax->add(diag.in(*v1._indices)<=0,true);
1794                             }
1795                             if (relax->_cons_name.count(name2+"_diag")==0) {
1796                                 auto v2 = relax->template get_var<double>(vv->_original_vars[1]->get_name(true,true));
1797                                 Constraint<> diag(name2+"_diag");
1798                                 if(v2.is_non_negative()){
1799                                     diag = pow(v2,2) - v2.get_ub()*v2;
1800                                 }
1801                                 else {
1802                                     diag = pow(v2,2) - pow(v2.get_ub(),2);
1803                                 }
1804                                 relax->add(diag.in(*v2._indices)<=0,true);
1805                             }
1806                             auto vdiag1 = relax->template get_var<double>(name1);
1807                             auto vdiag2 = relax->template get_var<double>(name2);
1808 //                            vdiag1.initialize_all(1e3);
1809 //                            vdiag2.initialize_all(1e3);
1810 //                            Constraint<> SOC(vv->_name+"_SOC_diag");
1811 //                            SOC = pow(*vv, 2) - vdiag1.in(*vv->_original_vars[0]->_indices)*vdiag2.in(*vv->_original_vars[1]->_indices);
1812 //                            relax->add(SOC.in(*vv->_indices) == 0, true);
1813 //                            SOC.print();
1814 //                            relax->print();
1815                         }
1816                     }
1817                 }
1818             }
1819             if(add_Kim_Kojima || add_SDP_3d){/* Need to extend Wij indices to include new chordal edges*/
1820                 for(auto &vp: relax->_vars){
1821                     if (vp.second->get_intype()==double_) {
1822                         auto vv = static_pointer_cast<var<double>>(vp.second);
1823                         if(vv->_lift && vv->_name.find("^2")==string::npos){
1824                             auto name1 = "Lift("+vv->_original_vars[0]->get_name(true,true)+"^2)";
1825                             auto Wii = relax->template get_var<type>(name1);
1826                             DebugOn("Diagonal element : " << Wii._name << endl);
1827                             auto Wij_ = vv->pairs_in_bags(bags_3d, 3);
1828                             auto Wii_ = Wii.in_bags(bags_3d, 3);
1829                             if(add_Kim_Kojima){
1830                                 Constraint<> SOC_Kojima1_0("SOC_Kojima1_0_diag");
1831                                 SOC_Kojima1_0 = pow(Wij_[0] + Wij_[2], 2) - Wii_[0]*(Wii_[1]+Wii_[2]+2*Wij_[1]);
1832                                 relax->add(SOC_Kojima1_0.in(range(0,bag_size-1)) <= 0);
1833 
1834                                 Constraint<> SOC_Kojima2_0("SOC_Kojima2_0_diag");
1835                                 SOC_Kojima2_0 = pow(Wij_[0] + Wij_[1], 2)  - Wii_[1]*(Wii_[0]+Wii_[2]+2*Wij_[2]);
1836                                 relax->add(SOC_Kojima2_0.in(range(0,bag_size-1)) <= 0);
1837 
1838                                 Constraint<> SOC_Kojima3_0("SOC_Kojima3_0_diag");
1839                                 SOC_Kojima3_0 = pow(Wij_[2] + Wij_[1], 2) - Wii_[2]*(Wii_[0]+Wii_[1]+2*Wij_[0]);
1840                                 relax->add(SOC_Kojima3_0.in(range(0,bag_size-1)) <= 0);
1841 
1842                                 Constraint<> SOC_Kojima1_90("SOC_Kojima1_90_diag");
1843                                  SOC_Kojima1_90 = pow(Wij_[0], 2) + pow(Wij_[2], 2) - Wii_[0]*(Wii_[1]+Wii_[2]);
1844                                  relax->add(SOC_Kojima1_90.in(range(0,bag_size-1)) <= 0);
1845 
1846                                 Constraint<> SOC_Kojima2_90("SOC_Kojima2_90_diag");
1847                                 SOC_Kojima2_90 = pow(Wij_[0], 2) + pow(Wij_[1], 2) - Wii_[1]*(Wii_[0]+Wii_[2]);
1848                                 relax->add(SOC_Kojima2_90.in(range(0,bag_size-1)) <= 0);
1849 
1850                                  Constraint<> SOC_Kojima3_90("SOC_Kojima3_90_diag");
1851                                  SOC_Kojima3_90 = pow(Wij_[2], 2) + pow(Wij_[1], 2) - Wii_[2]*(Wii_[0]+Wii_[1]);
1852                                  relax->add(SOC_Kojima3_90.in(range(0,bag_size-1)) <= 0);
1853                             }
1854 //
1855 //                            const double root2=std::sqrt(2.0);
1856 //                            Constraint<> SOC_Kojima1_45("SOC_Kojima1_45_diag");
1857 //                            SOC_Kojima1_45 = pow(root2*Wij_[0] + Wij_[2], 2) + pow(Wij_[2], 2) - 2.0*Wii_[0]*(Wii_[1]+Wii_[2]+root2*(Wij_[1]));
1858 //                            relax->add(SOC_Kojima1_45.in(range(0,bag_size-1)) <= 0);
1859 //
1860 //                            /* Second-order cone constraints */
1861 //                            Constraint<> SOC_Kojima2_45("SOC_Kojima2_45_diag");
1862 //                            SOC_Kojima2_45 = pow(root2*Wij_[0] + Wij_[1], 2) + pow(Wij_[1], 2) - 2.0*Wii_[1]*(Wii_[0]+Wii_[2]+root2*(Wij_[2]));
1863 //                            relax->add(SOC_Kojima2_45.in(range(0,bag_size-1)) <= 0);
1864 //
1865 //
1866 //                            Constraint<> SOC_Kojima3_45("SOC_Kojima3_45_diag");
1867 //                            SOC_Kojima3_45 = pow(root2*Wij_[2] + Wij_[1], 2) + pow(Wij_[1], 2) - 2.0*Wii_[2]*(Wii_[0]+Wii_[1]+root2*(Wij_[0]));
1868 //                            relax->add(SOC_Kojima3_45.in(range(0,bag_size-1)) <= 0);
1869 
1870                             if(add_SDP_3d){
1871                                 Constraint<> SDP3("SDP_3D_diag");
1872                                 SDP3 = 2 * Wij_[0] * Wij_[1] * Wij_[2];
1873                                 SDP3 -= pow(Wij_[0], 2) * Wii_[2];
1874                                 SDP3 -= pow(Wij_[1], 2) * Wii_[0];
1875                                 SDP3 -= pow(Wij_[2], 2) * Wii_[1];
1876                                 SDP3 += Wii_[0] * Wii_[1] * Wii_[2];
1877                                 relax->add(SDP3.in(range(1, bag_size)) >= 0);
1878                             }
1879                                    // SPP->add(SDP3.in(range(0, bag_size-1)) >= 0);
1880 //                            Constraint<> SOC_Kojima1_0_NC("SOC_Kojima1_0_NC_diag");
1881 //                            SOC_Kojima1_0_NC = pow(Wij_[0] + Wij_[2], 2) - Wii_[0]*(Wii_[1]+Wii_[2]+2*Wij_[1]);
1882 //                            relax->add(SOC_Kojima1_0_NC.in(range(0,bag_size-1)) >= 0, true);
1883 //
1884 //                            Constraint<> SOC_Kojima2_0_NC("SOC_Kojima2_0_NC_diag");
1885 //                            SOC_Kojima2_0_NC = pow(Wij_[0] + Wij_[1], 2)  - Wii_[1]*(Wii_[0]+Wii_[2]+2*Wij_[2]);
1886 //                            relax->add(SOC_Kojima2_0_NC.in(range(0,bag_size-1)) >= 0, true);
1887 ////
1888 //                            Constraint<> SOC_Kojima3_0_NC("SOC_Kojima3_0_NC_diag");
1889 //                            SOC_Kojima3_0_NC = pow(Wij_[2] + Wij_[1], 2) - Wii_[2]*(Wii_[0]+Wii_[1]+2*Wij_[0]);
1890 //                            relax->add(SOC_Kojima3_0_NC.in(range(0,bag_size-1)) >= 0, true);
1891 //
1892 //                            Constraint<> SOC_Kojima1_90_NC("SOC_Kojima1_90_NC_diag");
1893 //                            SOC_Kojima1_90_NC = pow(Wij_[0], 2) + pow(Wij_[2], 2) - Wii_[0]*(Wii_[1]+Wii_[2]);
1894 //                            relax->add(SOC_Kojima1_90_NC.in(range(0,bag_size-1)) >= 0, true);
1895 //
1896 //                            Constraint<> SOC_Kojima2_90_NC("SOC_Kojima2_90_NC_diag");
1897 //                            SOC_Kojima2_90_NC = pow(Wij_[0], 2) + pow(Wij_[1], 2) - Wii_[1]*(Wii_[0]+Wii_[2]);
1898 //                            relax->add(SOC_Kojima2_90_NC.in(range(0,bag_size-1)) >= 0, true);
1899 //
1900 //                            Constraint<> SOC_Kojima3_90_NC("SOC_Kojima3_90_NC_diag");
1901 //                            SOC_Kojima3_90_NC = pow(Wij_[2], 2) + pow(Wij_[1], 2) - Wii_[2]*(Wii_[0]+Wii_[1]);
1902 //                            relax->add(SOC_Kojima3_90_NC.in(range(0,bag_size-1)) >= 0, true);
1903 //                            break;
1904                         }
1905                     }
1906                 }
1907             }
1908             relax->print();
1909             return relax;
1910         }
1911 
1912 
1913 
1914 
1915         template<typename T=type,
1916         typename std::enable_if<is_same<T,double>::value>::type* = nullptr>
restructure()1917         void restructure() {/*<  group equations/inequalities that share the sparsity patterns */
1918             vector<string> delete_cstr;
1919             list<shared_ptr<Constraint<type>>> eq_list; /* list of equations */
1920             list<shared_ptr<Constraint<type>>> leq_list; /* list of <= inequalities */
1921             list<shared_ptr<Constraint<type>>> geq_list; /* list of >= inequalities */
1922             map<pair<int,int>,vector<shared_ptr<Constraint<type>>>> eq_sparsity, leq_sparsity, geq_sparsity;
1923             int nb_terms = 0, nb_int_vars = 0, nb_cont_vars = 0;
1924             /* start with linear constraints */
1925             for (auto& c_pair:_cons) {
1926                 if(c_pair.second->is_linear()){
1927                     nb_terms = c_pair.second->get_nb_vars();
1928                     nb_int_vars = c_pair.second->get_nb_int_vars();
1929                     nb_cont_vars = nb_terms - nb_int_vars;
1930                     if (c_pair.second->is_eq()) {
1931                         eq_list.push_back(c_pair.second);
1932                         eq_sparsity[{nb_cont_vars,nb_int_vars}].push_back(c_pair.second);
1933                     }
1934                     else if (c_pair.second->is_leq()) {
1935                         leq_list.push_back(c_pair.second);
1936                         leq_sparsity[{nb_cont_vars,nb_int_vars}].push_back(c_pair.second);
1937                     }
1938                     else {
1939                         geq_list.push_back(c_pair.second);
1940                         geq_sparsity[{nb_cont_vars,nb_int_vars}].push_back(c_pair.second);
1941                     }
1942                 }
1943             }
1944             int index = 0;
1945             var<> x, y;
1946             if(has_var("x"))
1947                 x = get_var<double>("x");
1948             if(has_var("y"))
1949                 y = get_var<double>("y");
1950             for( const auto & iter: eq_sparsity){
1951                 auto con_vec = iter.second;
1952                 if(con_vec.size()>1){
1953                     auto con0 = con_vec[0];
1954                     Constraint<> eq("lin_eq_"+to_string(index));
1955                     param<> c0("c0_eq_"+to_string(index));
1956                     c0 = con0->eval_cst(0);
1957                     nb_cont_vars = iter.first.first;
1958                     nb_int_vars = iter.first.second;
1959                     vector<indices> x_ids(nb_cont_vars);/* indices for each symbolic continuous variable */
1960                     vector<indices> y_ids(nb_int_vars);/* indices for each symbolic integer variable */
1961                     vector<param<>> x_coefs(nb_cont_vars);/* coefficient multiplying each symbolic continuous variable */
1962                     vector<param<>> y_coefs(nb_int_vars);/* coefficient multiplying each symbolic integer variable */
1963                     indices eq_ids("lin_eq_"+to_string(index));
1964                     eq_ids.insert("inst_1");
1965                     for(int i = 0; i<nb_cont_vars;i++){
1966                         x_ids[i].set_name("lin_eq_x_ids"+to_string(index)+"_"+to_string(i));
1967                         x_ids[i] = *x._indices;
1968                         x_ids[i].add_ref(con0->get_lterm_cont_var_id(i));
1969                         x_coefs[i] = param<>("coef_lin_eq_x_coefs"+to_string(index)+"_"+to_string(i));
1970                         x_coefs[i] = con0->eval_lterm_cont_coef(i);
1971                         eq += x_coefs[i].in(eq_ids)*x.in(x_ids[i]);
1972                     }
1973                     for(int i = 0; i<nb_int_vars;i++){
1974                         y_ids[i].set_name("lin_eq_y_ids"+to_string(index)+"_"+to_string(i));
1975                         y_ids[i] = *y._indices;
1976                         y_ids[i].add_ref(con0->get_lterm_int_var_id(i));
1977                         y_coefs[i] = param<>("coef_lin_eq_y_coefs"+to_string(index)+"_"+to_string(i));
1978                         y_coefs[i] = con0->eval_lterm_int_coef(i);
1979                         eq += y_coefs[i].in(eq_ids)*y.in(y_ids[i]);
1980                     }
1981                     eq += c0.in(eq_ids);
1982                     eq.in(eq_ids);
1983                     for(int i = 1; i<con_vec.size();i++){/* iterate over linear constraints with first sparsity degree */
1984                         auto con = con_vec[i];
1985                         eq.add_linear_row(con);
1986                         delete_cstr.push_back(con->get_name());
1987                     }
1988                     add(eq==0);
1989                     delete_cstr.push_back(con0->get_name());
1990                 }
1991                 index++;
1992             }
1993             index = 0;
1994             for( const auto & iter: leq_sparsity){
1995                 auto con_vec = iter.second;
1996                 if(con_vec.size()>1){
1997                     auto con0 = con_vec[0];
1998                     Constraint<> leq("lin_leq_"+to_string(index));
1999                     param<> c0("c0_leq_"+to_string(index));
2000                     c0 = con0->eval_cst(0);
2001                     nb_cont_vars = iter.first.first;
2002                     nb_int_vars = iter.first.second;
2003                     vector<indices> x_ids(nb_cont_vars);/* indices for each symbolic continuous variable */
2004                     vector<indices> y_ids(nb_int_vars);/* indices for each symbolic integer variable */
2005                     vector<param<>> x_coefs(nb_cont_vars);/* coefficient multiplying each symbolic continuous variable */
2006                     vector<param<>> y_coefs(nb_int_vars);/* coefficient multiplying each symbolic integer variable */
2007                     indices leq_ids("lin_leq_"+to_string(index));
2008                     leq_ids.insert("inst_1");
2009                     for(int i = 0; i<nb_cont_vars;i++){
2010                         x_ids[i].set_name("lin_leq_x_ids"+to_string(index)+"_"+to_string(i));
2011                         x_ids[i] = *x._indices;
2012                         x_ids[i].add_ref(con0->get_lterm_cont_var_id(i));
2013                         x_coefs[i] = param<>("coef_lin_leq_x_coefs"+to_string(index)+"_"+to_string(i));
2014                         x_coefs[i] = con0->eval_lterm_cont_coef(i);
2015                         leq += x_coefs[i].in(leq_ids)*x.in(x_ids[i]);
2016                     }
2017                     for(int i = 0; i<nb_int_vars;i++){
2018                         y_ids[i].set_name("lin_leq_y_ids"+to_string(index)+"_"+to_string(i));
2019                         y_ids[i] = *y._indices;
2020                         y_ids[i].add_ref(con0->get_lterm_int_var_id(i));
2021                         y_coefs[i] = param<>("coef_lin_leq_y_coefs"+to_string(index)+"_"+to_string(i));
2022                         y_coefs[i] = con0->eval_lterm_int_coef(i);
2023                         leq += y_coefs[i].in(leq_ids)*y.in(y_ids[i]);
2024                     }
2025                     leq += c0.in(leq_ids);
2026                     leq.in(leq_ids);
2027                     for(int i = 1; i<con_vec.size();i++){/* iterate over linear constraints with first sparsity degree */
2028                         auto con = con_vec[i];
2029                         leq.add_linear_row(con);
2030                         delete_cstr.push_back(con->get_name());
2031                     }
2032                     add(leq<=0);
2033                     delete_cstr.push_back(con0->get_name());
2034                 }
2035                 index++;
2036             }
2037             index = 0;
2038             for( const auto & iter: geq_sparsity){
2039                 auto con_vec = iter.second;
2040                 if(con_vec.size()>1){
2041                     auto con0 = con_vec[0];
2042                     Constraint<> geq("lin_geq_"+to_string(index));
2043                     param<> c0("c0_geq_"+to_string(index));
2044                     c0 = con0->eval_cst(0);
2045                     nb_cont_vars = iter.first.first;
2046                     nb_int_vars = iter.first.second;
2047                     vector<indices> x_ids(nb_cont_vars);/* indices for each symbolic continuous variable */
2048                     vector<indices> y_ids(nb_int_vars);/* indices for each symbolic integer variable */
2049                     vector<param<>> x_coefs(nb_cont_vars);/* coefficient multiplying each symbolic continuous variable */
2050                     vector<param<>> y_coefs(nb_int_vars);/* coefficient multiplying each symbolic integer variable */
2051                     indices geq_ids("lin_geq_"+to_string(index));
2052                     geq_ids.insert("inst_1");
2053 
2054                     for(int i = 0; i<nb_cont_vars;i++){
2055                         x_ids[i].set_name("lin_geq_x_ids"+to_string(index)+"_"+to_string(i));
2056                         x_ids[i] = *x._indices;
2057                         x_ids[i].add_ref(con0->get_lterm_cont_var_id(i));
2058                         x_coefs[i] = param<>("coef_lin_geq_x_coefs"+to_string(index)+"_"+to_string(i));
2059                         x_coefs[i] = con0->eval_lterm_cont_coef(i);
2060                         geq += x_coefs[i].in(geq_ids)*x.in(x_ids[i]);
2061                     }
2062                     for(int i = 0; i<nb_int_vars;i++){
2063                         y_ids[i].set_name("lin_geq_y_ids"+to_string(index)+"_"+to_string(i));
2064                         y_ids[i] = *y._indices;
2065                         y_ids[i].add_ref(con0->get_lterm_int_var_id(i));
2066                         y_coefs[i] = param<>("coef_lin_geq_y_coefs"+to_string(index)+"_"+to_string(i));
2067                         y_coefs[i] = con0->eval_lterm_int_coef(i);
2068                         geq += y_coefs[i].in(geq_ids)*y.in(y_ids[i]);
2069                     }
2070                     geq += c0.in(geq_ids);
2071                     geq.in(geq_ids);
2072                     for(int i = 1; i<con_vec.size();i++){/* iterate over linear constraints with first sparsity degree */
2073                         auto con = con_vec[i];
2074                         geq.add_linear_row(con);
2075                         delete_cstr.push_back(con->get_name());
2076                     }
2077                     add(geq>=0);
2078                 }
2079                 index++;
2080             }
2081             for(const auto cstr_name: delete_cstr){
2082                 remove(cstr_name);
2083             }
2084             DebugOn("Model after restructure: " << endl);
2085             print();
2086         }
2087 
2088         template<typename T=type,
2089         typename std::enable_if<is_same<T,double>::value>::type* = nullptr>
project()2090         void project() {/*<  Use the equations where at least one variable appears linearly to express it as a function of other variables in the problem */
2091             vector<string> delete_cstr;
2092             list<shared_ptr<Constraint<type>>> eq_list; /* sorted list of equations */
2093             for (auto& c_pair:_cons) {
2094                 if (c_pair.second->is_eq()) {
2095                     eq_list.push_back(c_pair.second);
2096                 }
2097             }
2098             while(!eq_list.empty()){
2099                 eq_list.sort(cstr_compare<type>);
2100                 shared_ptr<Constraint<type>> c = eq_list.front();
2101                 eq_list.pop_front();
2102                 if(c->_lterms->empty()){
2103                     break;
2104                 }
2105                 if(c->has_matrix_indexed_vars()){
2106                     continue;
2107                 }
2108                 /* Find a real (continuous) variable that only appears in the linear part of c and has an invertible coefficient */
2109                 list<pair<string,shared_ptr<param_>>> var_list; /* sorted list of <lterm name,variable> appearing linearly in c (sort in decreasing number of rows of variables). */
2110                 for (auto& lterm: c->get_lterms()) {
2111                     auto v = lterm.second._p;
2112                     if(v->get_intype()==double_ && c->_vars->at(v->_name).second==1 /* only appears in linear part of c*/ && (lterm.second._coef->is_negative() || lterm.second._coef->is_positive()) /* invertible */){
2113                         var_list.push_back({lterm.first,v});
2114                     }
2115                 }
2116                 var_list.sort(var_compare);
2117                 auto v = var_list.front().second;
2118                 auto lterm = c->_lterms->at(var_list.front().first);
2119                 if(v->get_intype()==double_ && c->_vars->at(v->_name).second==1 /* only appears in linear part of c*/ && (lterm._coef->is_negative() || lterm._coef->is_positive()) /* invertible */){
2120                     func<T> f = *c;
2121                     if(!lterm._coef->is_negative() && !lterm._coef->is_positive()){/* cannot invert */
2122                         continue;/* TODO: find smallest invertible set */
2123                     }
2124                     auto vv = *static_pointer_cast<var<double>>(v);
2125                     if(vv.is_matrix_indexed()){
2126                         pair<vector<bool>,vector<bool>> z_nnz_rows = vv.get_nnz_rows();
2127                         auto c_split = *c;
2128                         c_split._name += "_split";
2129                         c_split.insert(!lterm._sign,*lterm._coef,*lterm._p);/* Remove coef*v from f */
2130                         c_split.update_rows(z_nnz_rows.first);// rows where v is zero
2131                         if(c_split.get_dim()!=0){
2132                             eq_list.push_back(add_constraint(c_split));
2133 //                            c_split.print();
2134                             c->update_rows(z_nnz_rows.second);
2135                         }
2136                         auto column_0 = v->delete_column(0);
2137                         if(v->_indices->nb_keys()==0){
2138                             c->insert(!lterm._sign,*lterm._coef,*v);/* Remove coef*v from c */
2139                         }
2140                         else {
2141                             vv.index_in(column_0);
2142                         }
2143                         f = *c;
2144                     }
2145                     else {
2146                         f.insert(!lterm._sign,*lterm._coef,*lterm._p);/* Remove coef*v from f */
2147                     }
2148                     if (lterm._coef->is_function()) {
2149                         auto coef = *static_pointer_cast<func<T>>(lterm._coef);
2150                         if(coef._indices){
2151                             f *= (-1./coef).in(*coef._indices);
2152                         }
2153                         else {
2154                             f *= -1./coef;
2155                         }
2156                     }
2157                     else if(lterm._coef->is_param()) {
2158                         auto coef = *static_pointer_cast<param<T>>(lterm._coef);
2159                         if(coef._indices){
2160                             f *= (-1./coef).in(*coef._indices);
2161                         }
2162                         else {
2163                             f *= -1./coef;
2164                         }
2165 
2166                     }
2167                     else if(lterm._coef->is_number()) {
2168                         auto coef = *static_pointer_cast<constant<T>>(lterm._coef);
2169                         f *= -1./coef;
2170                     }
2171                     delete_cstr.push_back(c->_name);
2172                     c->_is_constraint = false;
2173                     replace(vv,f,eq_list);/* Add bound constraints */
2174                 }
2175             }
2176             for(const auto cstr_name: delete_cstr){
2177                 remove(cstr_name);
2178             }
2179             DebugOn("Model after projetion: " << endl);
2180             print();
2181         }
2182 
2183 
2184         /** Add constraint to model
2185          @param[in] c: constraint to add
2186          @param[in] lift_flag: if true, add a linearized version of this constraint and the constraints linking the lifted variables to the original ones.
2187          @return a pointer to the added constraint
2188          @note If lift = true, 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. Note also that if this is an equation f(x) = 0 s.t. f(x)<=0 or f(x)>=0 is convex, will add the convex inequality to the model.
2189          **/
2190         shared_ptr<Constraint<type>> add_constraint(Constraint<type>& c, bool lift_flag = false, string method_type = "on/off", bool split=true){
2191             if (c.get_dim()==0) {
2192                 return nullptr;
2193             }
2194             if (_cons_name.count(c.get_name())==0) {
2195                 auto newc = make_shared<Constraint<type>>(c);
2196                 for (auto &vp: *newc->_vars) {
2197                     _v_in_cons[vp.second.first->_name].insert(newc);
2198                 }
2199                 newc->_val = c._val;
2200                 newc->check_soc();
2201                 newc->check_rotated_soc();
2202                 if (newc->is_constant()) {
2203                     switch (newc->_ctype) {
2204                         case leq:
2205                             if (newc->is_positive()) {
2206                                 throw invalid_argument("Adding violated constant constraint!\n");
2207                             }
2208                             break;
2209                         case geq:
2210                             if (newc->is_negative()) {
2211                                 throw invalid_argument("Adding violated constant constraint!\n");
2212                             }
2213                             break;
2214                         case eq:
2215                             if (newc->is_positive() || newc->is_negative()) {
2216                                 throw invalid_argument("Adding violated constant equation!\n");
2217                             }
2218                             break;
2219                         default:
2220                             break;
2221                     }
2222                     Warning("WARNING: Adding redundant constant constraint, Gravity will be ignoring it.\n");
2223                     newc->print();
2224                     return newc;
2225                 }
2226                 newc->update_str();
2227                 embed(newc, false);
2228                 if(lift_flag && !newc->is_linear()){
2229                     if(newc->func<type>::is_convex() && newc->_ctype==eq && split){
2230                         DebugOn("Convex left hand side of equation detected, splitting constraint into <= and ==" << endl);
2231                         Constraint<type> c_cvx(*newc);
2232                         c_cvx._name = newc->_name+"_convex";
2233                         c_cvx._relaxed = true;
2234                         add_constraint(c_cvx <= 0);
2235                     }
2236                     if(newc->func<type>::is_concave() && newc->_ctype==eq && split){
2237                         DebugOn("Concave left hand side of equation detected, splitting constraint into >= and ==" << endl);
2238                         Constraint<type> c_ccve(*newc);
2239                         c_ccve._name = newc->_name+"_concave";
2240                         c_ccve._relaxed = true;
2241                         add_constraint(c_ccve >= 0);
2242                     }
2243 
2244                     /* Call the lift function and add corresponding constraints */
2245                     auto lifted = lift(*newc, method_type);
2246                     return add_constraint(lifted);
2247                 }
2248 
2249 
2250                 update_convexity(*newc);
2251                 newc->_violated.resize(newc->get_nb_inst(),true);
2252                 _cons_name[c.get_name()] = newc;
2253                 if(*newc->_all_lazy && newc->_lazy.size()==0){
2254                     newc->_lazy.resize(newc->get_nb_inst(),true);
2255                     newc->allocate_mem();
2256                     return newc;
2257                 }
2258                 size_t nb_inst = c.get_nb_instances();
2259                 if (nb_inst>0) {
2260                     newc->_id = _nb_cons;
2261                     _cons[newc->_id] = newc;
2262                     _cons_vec.push_back(newc);
2263                     _built = false;
2264                     _nb_cons += nb_inst;
2265                     for (size_t inst = 0; inst<nb_inst; inst++) {
2266                         _nnz_g += c.get_nb_vars(inst);
2267                     }
2268                     if (_type==lin_m && c.is_quadratic()) {
2269                         _type = quad_m;
2270                     }
2271                     else if ((_type==lin_m || _type==quad_m) && c.is_polynomial()) {
2272                         _type = pol_m;
2273                     }
2274                     else if(c.is_nonlinear()){
2275                         _type = nlin_m;
2276                     }
2277                     newc->allocate_mem();
2278                     c.allocate_mem();
2279                 }
2280                 return newc;
2281             }
2282             else {
2283                 DebugOn("constraint with same name: " << c.get_name() << endl);
2284                 c.update_str();
2285                 if(!_cons_name.at(c.get_name())->equal(c)) {
2286                     throw invalid_argument("rename constraint as this name has been used by another one: " + c.get_name());
2287                 }
2288                 else{
2289                     DebugOn("Both constraints are identical, ignoring this one." << endl);
2290                     return nullptr;
2291                 }
2292             }
2293         };
2294 
2295 
2296         /**
2297          Delete constraint from model and reindex remaining constraints
2298          @param[in] c_name: constraint name
2299          */
remove(const string & c_name)2300         void remove(const string& c_name){
2301             _cons_name.erase(c_name);
2302             _built = false;
2303             _cons_vec.clear();
2304             _cons.clear();
2305             _cons_vec.resize(_cons_name.size());
2306             size_t i = 0;
2307             for(auto& c_p: _cons_name)
2308             {
2309                 _cons[i] = c_p.second;
2310                 _cons_vec[i++] = c_p.second;
2311             }
2312             reindex();
2313         };
2314 
2315         /**
2316          Update the convexity of the current model after adding f (either objective or constraint)
2317          @param[in] f function/constraint to be added to the model
2318          */
2319         template<typename T1>
update_convexity(const func<T1> & f)2320         void update_convexity(const func<T1>& f){
2321             if(f.is_linear()){
2322                 return;
2323             }
2324             if(_convexity==linear_){
2325                 if (((_objt==minimize || f._is_constraint) && f.is_convex()) || ((_objt==maximize || f._is_constraint) && f.is_concave())) {
2326                     _convexity = convex_;
2327                 }
2328                 else if (((_objt==minimize || f._is_constraint) && f.is_concave()) || ((_objt==maximize || f._is_constraint) && f.is_convex())) {
2329                     _convexity = concave_;
2330                 }
2331                 else {
2332                     _convexity = undet_;
2333                 }
2334             }
2335             else if(_convexity==convex_){
2336                 if (!(((_objt==minimize || f._is_constraint) && f.is_convex()) || ((_objt==maximize || f._is_constraint) && f.is_concave()))) {
2337                     _convexity = undet_;
2338                 }
2339             }
2340             else if(_convexity==concave_){
2341                 if (!(((_objt==minimize || f._is_constraint) && f.is_concave()) || ((_objt==maximize || f._is_constraint) && f.is_convex()))) {
2342                     _convexity = undet_;
2343                 }
2344             }
2345             else {
2346                 _convexity = undet_;
2347             }
2348         }
2349 
2350 
2351         template<typename T=type>
set_objective(const func<type> & f,ObjectiveType t)2352         void set_objective(const func<type>& f, ObjectiveType t) {
2353             _obj = make_shared<func<T>>(f);
2354             _obj->_val = f._val;
2355             _objt = t;
2356             _obj->_indices = nullptr;
2357             update_convexity(f);
2358             if (_type==lin_m && f.is_quadratic()) {
2359                 _type = quad_m;
2360             }
2361             else if ((_type==lin_m || _type==quad_m) && f.is_polynomial()) {
2362                 _type = pol_m;
2363             }
2364             else if(f.is_nonlinear()){
2365                 _type = nlin_m;
2366             }
2367             embed(_obj);
2368             _obj->allocate_mem();
2369         }
2370 
2371         template<typename T1>
min(const param<T1> & p)2372         void min(const param<T1>& p){
2373             set_objective(func<T1>(p), minimize);
2374         }
2375 
2376         template<typename T1>
min(T1 c)2377         void min(T1 c){
2378             set_objective(func<T1>(c), minimize);
2379         }
2380 
2381         template<typename T1>
min(const var<T1> & v)2382         void min(const var<T1>& v){
2383             set_objective(func<T1>(v), minimize);
2384         }
2385 
2386         template<typename T1>
min(const func<T1> & f)2387         void min(const func<T1>& f){
2388             set_objective(f, minimize);
2389         }
2390 
2391 
2392         template<typename T1>
max(const param<T1> & p)2393         void max(const param<T1>& p){
2394             set_objective(func<T1>(p), maximize);
2395         }
2396 
2397         template<typename T1>
max(T1 c)2398         void max(T1 c){
2399             set_objective(func<T1>(c), maximize);
2400         }
2401 
2402         template<typename T1>
max(const var<T1> & v)2403         void max(const var<T1>& v){
2404             set_objective(func<T1>(v), maximize);
2405         }
2406 
2407         template<typename T1>
max(const func<T1> & f)2408         void max(const func<T1>& f){
2409             set_objective(f, maximize);
2410         }
2411 
2412 
set_objective_type(ObjectiveType t)2413         void set_objective_type(ObjectiveType t) {
2414             _objt = t;
2415         }
2416 
2417         /** Prints the constraints that have a non-zero value at the current solution where zero <= tol
2418          @param[tol] zero value tolerance
2419          @param[only_relaxed] if set to true, only print non-zero values of inequalities that were generated by relaxing non-convex equations
2420          */
2421         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
2422         void print_nonzero_constraints(double tol, bool only_relaxed = false) const{
2423             size_t nb_inst = 0;
2424             shared_ptr<Constraint<type>> c = nullptr;
2425             for(auto& c_p: _cons_name)
2426             {
2427                 c = c_p.second;
2428                 if(only_relaxed && !c->_relaxed){
2429                     continue;
2430                 }
2431                 nb_inst = c->get_nb_inst();
2432                 switch (c->get_ctype()) {
2433                     case eq:
2434                         for (size_t inst=0; inst<nb_inst; inst++) {
2435                             auto diff = std::abs(c->eval(inst));
2436                             if(diff>tol){
2437                                 DebugOn(c->_name << " Non-zero equation: " << to_string(inst) << ", value = "<< diff << endl);
2438                             }
2439                         }
2440                         break;
2441                     case leq:
2442                         for (size_t inst=0; inst<nb_inst; inst++) {
2443                             auto diff = c->eval(inst);
2444                             if(diff < -tol) {
2445                                 DebugOn(c->_name << " Non-zero <= inequality: " << to_string(inst) << ", value = "<< diff << endl);
2446                             }
2447                         }
2448                         break;
2449                     case geq:
2450                         for (size_t inst=0; inst<nb_inst; inst++) {
2451                             auto diff = c->eval(inst);
2452                             if(diff > tol) {
2453                                 DebugOn(c->_name << " Non-zero >= inequality: " << to_string(inst) << ", value = "<< diff << endl);
2454                             }
2455                         }
2456                         break;
2457 
2458                     default:
2459                         break;
2460                 }
2461             }
2462         }
2463 
2464         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
2465         vector<tuple<double, int, int>> sorted_nonzero_constraints(double tol, bool only_relaxed = false, bool print_name = false) const{
2466             // the tuple has the following form <value, constraint_id, instance_id>
2467 
2468             vector<tuple<double, int, int>> v;
2469             size_t nb_inst = 0;
2470             shared_ptr<Constraint<type>> c = nullptr;
2471             for(auto& c_p: _cons_name)
2472             {
2473                 c = c_p.second;
2474                 if(only_relaxed && !c->_relaxed){
2475                     continue;
2476                 }
2477                 nb_inst = c->get_nb_inst();
2478                 switch (c->get_ctype()) {
2479                     case eq:
2480                         for (size_t inst=0; inst<nb_inst; inst++) {
2481                             auto diff = std::abs(c->eval(inst));
2482                             if(diff>tol){
2483                                 v.push_back(make_tuple(std::abs(diff), c->_id, inst));
2484                                 if(print_name) DebugOn(" Non-zero >= inequality: " << c->_name << " instance: " << to_string(inst) << ", value = "<< std::abs(diff) << endl);
2485                             }
2486                         }
2487                         break;
2488                     case leq:
2489                         for (size_t inst=0; inst<nb_inst; inst++) {
2490                             auto diff = c->eval(inst);
2491                             if(diff < -tol) {
2492                                 v.push_back(make_tuple(std::abs(diff), c->_id, inst));
2493                                 if(print_name) DebugOn(" Non-zero >= inequality: " << c->_name << " instance: " << to_string(inst) << ", value = "<< std::abs(diff) << endl);
2494                             }
2495                         }
2496                         break;
2497                     case geq:
2498                         for (size_t inst=0; inst<nb_inst; inst++) {
2499                             auto diff = c->eval(inst);
2500                             if(diff > tol) {
2501                                 v.push_back(make_tuple(std::abs(diff), c->_id, inst));
2502                                 if(print_name) DebugOn(" Non-zero >= inequality: " << c->_name << " instance: " << to_string(inst) << ", value = "<< std::abs(diff) << endl);
2503                             }
2504                         }
2505                         break;
2506 
2507                     default:
2508                         break;
2509                 }
2510             }
2511             sort(v.begin(), v.end(), std::greater<tuple<double,int,int>>());
2512             return v;
2513         }
2514 
sorted_nonzero_constraint_indices(double tol,bool print_name,string constraint_name)2515         indices sorted_nonzero_constraint_indices(double tol, bool print_name, string constraint_name) const{
2516             // returns the indices of the constraint given a constraint_name
2517 
2518             vector<tuple<double, string>> v; //violation amount & the index of a given constraint
2519             size_t nb_inst = 0;
2520             shared_ptr<Constraint<type>> c = nullptr;
2521             for(auto& c_p: _cons_name)
2522             {
2523                 c = c_p.second;
2524                 if(c->_name != constraint_name){
2525                     continue;
2526                 }
2527 
2528                 nb_inst = c->get_nb_inst();
2529                 switch (c->get_ctype()) {
2530                     case eq:
2531                         for (size_t inst=0; inst<nb_inst; inst++) {
2532                             auto diff = std::abs(c->eval(inst));
2533                             if(diff>tol){
2534                                 v.push_back(make_tuple(std::abs(diff), c->_indices->_keys->at(inst)));
2535                                 if(print_name) DebugOn(" Non-zero >= inequality: " << c->_name << " instance: " << to_string(inst) << ", value = "<< std::abs(diff) << endl);
2536                             }
2537                         }
2538                         break;
2539                     case leq:
2540                         for (size_t inst=0; inst<nb_inst; inst++) {
2541                             auto diff = c->eval(inst);
2542                             if(diff < -tol) {
2543                                 v.push_back(make_tuple(std::abs(diff), c->_indices->_keys->at(inst)));
2544                                 if(print_name) DebugOn(" Non-zero >= inequality: " << c->_name << " instance: " << to_string(inst) << ", value = "<< std::abs(diff) << endl);
2545                             }
2546                         }
2547                         break;
2548                     case geq:
2549                         for (size_t inst=0; inst<nb_inst; inst++) {
2550                             auto diff = c->eval(inst);
2551                             if(diff > tol) {
2552                                 v.push_back(make_tuple(std::abs(diff), c->_indices->_keys->at(inst)));
2553                                 if(print_name) DebugOn(" Non-zero >= inequality: " << c->_name << " instance: " << to_string(inst) << ", value = "<< std::abs(diff) << endl);
2554                             }
2555                         }
2556                         break;
2557 
2558                     default:
2559                         break;
2560                 }
2561             }
2562             sort(v.begin(), v.end(), std::greater<tuple<double,string>>());
2563 
2564             // HERE IS THE PART TO COLLECT THE SORTED INDEX SET
2565             indices nonzero_idx("nonzero_idx"); //the indices of the nonzero_constraint instances
2566             for (int i = 0; i < v.size(); i++)
2567                 nonzero_idx.add(get<1>(v[i]));
2568 
2569             return nonzero_idx;
2570         }
2571 
2572 
2573         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
print_constraints_stats(type tol)2574         bool print_constraints_stats(type tol){/*<< Prints stats on constraints active status with the current solution and tolerance tol */
2575             size_t nb_inst = 0, nb_viol = 0, nb_viol_all = 0;
2576             size_t nb_active = 0, nb_active_all = 0;
2577             double diff = 0;
2578             shared_ptr<Constraint<type>> c = nullptr;
2579             bool violated = false;
2580             for(auto& c_p: _cons_name)
2581             {
2582                 c = c_p.second;
2583                 //        cid = c->_id;
2584                 nb_inst = c->get_nb_inst();
2585                 nb_viol = 0;
2586                 nb_active = 0;
2587                 c->_all_satisfied = true;
2588                 c->_violated.resize(nb_inst);
2589                 c->_active.resize(nb_inst);
2590                 switch (c->get_ctype()) {
2591                     case eq:
2592                         for (size_t inst=0; inst<nb_inst; inst++) {
2593                             diff = std::abs(c->eval(inst));
2594                             if(diff > tol) {
2595                                 DebugOn("Violated equation: " << c->to_str(inst,3));
2596                                 //                        c->print(inst);
2597                                 DebugOn(", violation = "<< diff << endl);
2598                                 nb_viol++;
2599                                 //                        violated = true;
2600                                 if (*c->_all_lazy) {
2601                                     c->_all_satisfied = false;
2602                                     c->_violated[inst] = true;
2603                                     violated = true;
2604                                     c->_lazy[inst] = false;
2605                                 }
2606                                 else {
2607                                     //                            throw runtime_error("Non-lazy constraint is violated, solution declared optimal by solver!\n" + c->to_str(inst));
2608                                 }
2609                                 //                        c->_violated[inst] = true;
2610                             }
2611                             else {
2612                                 //                        c->_violated[inst] = false;
2613                             }
2614                             //                    nb_active++;
2615                         }
2616                         break;
2617                     case leq:
2618                         for (size_t inst=0; inst<nb_inst; inst++) {
2619                             c->_violated[inst] = false;
2620                             diff = c->eval(inst);
2621                             if(diff > tol) {
2622                                 DebugOn(c->_name<<" Violated inequality: " << c->to_str(inst,3));
2623                                 //                                c->print(inst);
2624                                 DebugOn(", violation = "<< diff << endl);
2625                                 nb_viol++;
2626                                 //                        violated = true;
2627                                 if (*c->_all_lazy) {
2628                                     //                                    *c->_all_lazy = false;
2629                                     c->_all_satisfied = false;
2630                                     c->_violated[inst] = true;
2631                                     violated = true;
2632                                     c->_lazy[inst] = false;
2633                                 }
2634                                 else {
2635                                     //                            throw runtime_error("Non-lazy constraint is violated, solution declared optimal by solver!\n" + c->to_str(inst));
2636                                 }
2637                             }
2638                             else if (std::abs(diff)>tol) {
2639                                 c->_active[inst] = false;
2640                                 //                        if (*c->_all_lazy) {
2641                                 //                            c->_lazy[inst] = true;
2642                                 //                        }
2643                             }
2644                             else {
2645                                 nb_active++;
2646                             }
2647                         }
2648                         break;
2649                     case geq:
2650                         for (size_t inst=0; inst<nb_inst; inst++) {
2651                             c->_violated[inst] = false;
2652                             diff = c->eval(inst);
2653                             if(diff < -tol) {
2654                                 DebugOn(c->_name<<" Violated inequality: " << c->to_str(inst,3));
2655                                 //                   c->print(inst);
2656                                 DebugOn(", violation = "<< diff << endl);
2657                                 nb_viol++;
2658                                 //                        violated = true;
2659                                 if (*c->_all_lazy) {
2660                                     //                                    *c->_all_lazy = false;
2661                                     c->_all_satisfied = false;
2662                                     c->_violated[inst] = true;
2663                                     violated = true;
2664                                     c->_lazy[inst] = false;
2665                                 }
2666                                 else {
2667                                     //                            throw runtime_error("Non-lazy constraint is violated, solution declared optimal by solver!\n" + c->to_str(inst));
2668                                 }
2669                             }
2670                             else if (std::abs(diff)> tol) {
2671                                 c->_active[inst] = false;
2672                                 //                        if (*c->_all_lazy) {
2673                                 //                            c->_lazy[inst] = true;
2674                                 //                        }
2675                             }
2676                             else {
2677                                 nb_active++;
2678                             }
2679                         }
2680                         break;
2681 
2682                     default:
2683                         break;
2684                 }
2685                 //        *c->_all_lazy = false;
2686                 nb_viol_all += nb_viol;
2687                 nb_active_all += nb_active;
2688                 if (nb_viol>0 && c->get_ctype()!=eq) {
2689                     DebugOn("Percentage of violated constraints for " << c->get_name() << " = (" << nb_viol << "/" << nb_inst << ") " << to_string_with_precision(100.*nb_viol/nb_inst,3) << "%\n");
2690                 }
2691                 if (c->get_ctype()!=eq) {
2692                     DebugOn("Percentage of active constraints for " << c->get_name() << " = (" << nb_active << "/" << nb_inst << ") " << to_string_with_precision(100.*nb_active/nb_inst,3) << "%\n");
2693                 }
2694             }
2695             auto nb_ineq = get_nb_ineq();
2696             DebugOn("Total percentage of violated constraints = (" << nb_viol_all << "/" << nb_ineq << ") " << to_string_with_precision(100.*nb_viol_all/nb_ineq,3) << "%\n");
2697             DebugOn("Total percentage of active constraints = (" << nb_active_all << "/" << nb_ineq << ") "  << to_string_with_precision(100.*nb_active_all/nb_ineq,3) << "%\n");
2698             return violated;
2699         }
2700 
2701         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
has_violated_constraints(type tol)2702         bool has_violated_constraints(type tol){/*<< Returns true if some constraints are violated by the current solution with tolerance tol */
2703             //    if (!_has_lazy) {
2704             //        return false;
2705             //    }
2706             //    int cid = 0;
2707             size_t nb_inst = 0, nb_viol = 0, nb_viol_all = 0;
2708             size_t nb_active = 0, nb_active_all = 0;
2709             double diff = 0;
2710             shared_ptr<Constraint<type>> c = nullptr;
2711             bool violated = false;
2712             for(auto& c_p: _cons_name)
2713             {
2714                 c = c_p.second;
2715                 //        cid = c->_id;
2716                 nb_inst = c->get_nb_inst();
2717                 nb_viol = 0;
2718                 nb_active = 0;
2719                 c->_all_satisfied = true;
2720                 c->_violated.resize(nb_inst);
2721                 c->_active.resize(nb_inst);
2722                 switch (c->get_ctype()) {
2723                     case eq:
2724                         for (size_t inst=0; inst<nb_inst; inst++) {
2725                             diff = std::abs(c->eval(inst));
2726                             if(diff > tol) {
2727                                 DebugOff("Violated equation: ");
2728                                 //                        c->print(inst);
2729                                 DebugOff(", violation = "<< diff << endl);
2730                                 nb_viol++;
2731                                 //                        violated = true;
2732                                 if (*c->_all_lazy) {
2733                                     c->_all_satisfied = false;
2734                                     c->_violated[inst] = true;
2735                                     violated = true;
2736                                     c->_lazy[inst] = false;
2737                                 }
2738                                 else {
2739                                     //                            throw runtime_error("Non-lazy constraint is violated, solution declared optimal by solver!\n" + c->to_str(inst));
2740                                 }
2741                                 //                        c->_violated[inst] = true;
2742                             }
2743                             else {
2744                                 //                        c->_violated[inst] = false;
2745                             }
2746                             //                    nb_active++;
2747                         }
2748                         break;
2749                     case leq:
2750                         for (size_t inst=0; inst<nb_inst; inst++) {
2751                             c->_violated[inst] = false;
2752                             diff = c->eval(inst);
2753                             if(diff > tol) {
2754                                 DebugOff("Violated inequality: ");
2755                                 //                                c->print(inst);
2756                                 DebugOff(", violation = "<< diff << endl);
2757                                 nb_viol++;
2758                                 //                        violated = true;
2759                                 if (*c->_all_lazy) {
2760                                     //                                    *c->_all_lazy = false;
2761                                     c->_all_satisfied = false;
2762                                     c->_violated[inst] = true;
2763                                     violated = true;
2764                                     c->_lazy[inst] = false;
2765                                 }
2766                                 else {
2767 //                                    violated = true;
2768                                     //                            throw runtime_error("Non-lazy constraint is violated, solution declared optimal by solver!\n" + c->to_str(inst));
2769                                 }
2770                             }
2771                             else if (std::abs(diff)>tol) {
2772                                 c->_active[inst] = false;
2773                                 //                        if (*c->_all_lazy) {
2774                                 //                            c->_lazy[inst] = true;
2775                                 //                        }
2776                             }
2777                             else {
2778                                 nb_active++;
2779                             }
2780                         }
2781                         break;
2782                     case geq:
2783                         for (size_t inst=0; inst<nb_inst; inst++) {
2784                             c->_violated[inst] = false;
2785                             diff = c->eval(inst);
2786                             if(diff < -tol) {
2787                                 DebugOff("Violated inequality: ");
2788                                 //                        c->print(inst);
2789                                 DebugOff(", violation = "<< diff << endl);
2790                                 nb_viol++;
2791                                 //                        violated = true;
2792                                 if (*c->_all_lazy) {
2793                                     //                                    *c->_all_lazy = false;
2794                                     c->_all_satisfied = false;
2795                                     c->_violated[inst] = true;
2796                                     violated = true;
2797                                     c->_lazy[inst] = false;
2798                                 }
2799                                 else {
2800 //                                    violated = true;
2801                                     //                            throw runtime_error("Non-lazy constraint is violated, solution declared optimal by solver!\n" + c->to_str(inst));
2802                                 }
2803                             }
2804                             else if (std::abs(diff)> tol) {
2805                                 c->_active[inst] = false;
2806                                 //                        if (*c->_all_lazy) {
2807                                 //                            c->_lazy[inst] = true;
2808                                 //                        }
2809                             }
2810                             else {
2811                                 nb_active++;
2812                             }
2813                         }
2814                         break;
2815 
2816                     default:
2817                         break;
2818                 }
2819                 //        *c->_all_lazy = false;
2820                 nb_viol_all += nb_viol;
2821                 nb_active_all += nb_active;
2822                 if (nb_viol>0 && c->get_ctype()!=eq) {
2823                     DebugOff("Percentage of violated constraints for " << c->get_name() << " = (" << nb_viol << "/" << nb_inst << ") " << to_string_with_precision(100.*nb_viol/nb_inst,3) << "%\n");
2824                 }
2825                 if (c->get_ctype()!=eq) {
2826                     DebugOff("Percentage of active constraints for " << c->get_name() << " = (" << nb_active << "/" << nb_inst << ") " << to_string_with_precision(100.*nb_active/nb_inst,3) << "%\n");
2827                 }
2828             }
2829             auto nb_ineq = get_nb_ineq();
2830             DebugOff("Total percentage of violated constraints = (" << nb_viol_all << "/" << nb_ineq << ") " << to_string_with_precision(100.*nb_viol_all/nb_ineq,3) << "%\n");
2831             DebugOff("Total percentage of active constraints = (" << nb_active_all << "/" << nb_ineq << ") "  << to_string_with_precision(100.*nb_active_all/nb_ineq,3) << "%\n");
2832             return violated;
2833         }
2834 
2835 
2836 
2837         /**
2838          Returns true if the current solution satisfies bounds and constraints upt to tolerance tol
2839          @param[in] tol tolerance for constraint satisfaction
2840          */
is_feasible(type tol)2841         bool is_feasible(type tol){
2842             shared_ptr<param_> v;
2843             double viol = 0;
2844             bool feasible = true;
2845             for(auto& v_p: _vars)
2846             {
2847                 v = v_p.second;
2848                 for (size_t i = 0; i < v->get_dim(); i++) {
2849                     viol = v->get_lb_violation(i);
2850                     if(viol  > tol){
2851                         clog << "Violated lower bound for var: " << v->_name << ", index: "<< i << ", violation = " << viol << endl;
2852                         feasible = false;
2853 
2854                     }
2855                     viol = v->get_ub_violation(i);
2856                     if(viol > tol){
2857                         clog << "Violated upper bound for var: " << v->_name << ", index: "<< i << ", violation = " << viol << endl;
2858                         feasible = false;
2859                     }
2860                 }
2861             }
2862             feasible = feasible && !has_violated_constraints(tol);
2863             return feasible;
2864         }
2865 
2866 
2867 #ifdef USE_BONMIN
fill_in_var_types(Bonmin::TMINLP::VariableType * var_types)2868         void fill_in_var_types(Bonmin::TMINLP::VariableType* var_types){
2869             size_t vid;
2870             param_* v;
2871             for(auto& v_p: _vars)
2872             {
2873                 v = v_p.second;
2874                 vid = v->get_id();
2875                 auto bonmin_type = Bonmin::TMINLP::CONTINUOUS;
2876                 auto type = v->get_intype();
2877                 if(type==short_ || type==integer_){
2878                     bonmin_type = Bonmin::TMINLP::INTEGER;
2879                 }
2880                 else if(type==binary_){
2881                     bonmin_type = Bonmin::TMINLP::BINARY;
2882                 }
2883                 for (size_t i = 0; i < v->get_dim(); i++) {
2884                     var_types[vid+i] = bonmin_type;
2885                 }
2886             }
2887 
2888         }
2889 #endif
2890 
2891         /**
2892          Fill the lower and upper bound values in x_l and x_u
2893          @param[out] x_l lower bound values to fill
2894          @param[out] x_u upper bound values to fill
2895          */
fill_in_var_bounds(double * x_l,double * x_u)2896         void fill_in_var_bounds(double* x_l ,double* x_u) {
2897             for(auto &v_p: _vars)
2898             {
2899                 v_p.second->get_double_lb(x_l);
2900                 v_p.second->get_double_ub(x_u);
2901             }
2902         }
2903 
2904 
2905         /**
2906          Initialize the model variables using values from x
2907          @param[in] x values to initialize to
2908          */
set_x(const double * x)2909         void set_x(const double* x){
2910             for(auto &v_p: _vars)
2911             {
2912                 v_p.second->set_double_val(x);
2913             }
2914         }
2915 
2916         /**
2917          Initialize the model variables using values from x
2918          @param[in] x values to initialize to
2919          */
set_solution(const vector<double> & x)2920         void set_solution(const vector<double>& x){
2921             for(auto &v_p: _vars)
2922             {
2923                 v_p.second->set_solution(x);
2924             }
2925         }
2926 
2927         /**
2928          Evaluate all nonlinear functions at current point
2929          */
compute_funcs()2930         void compute_funcs() {
2931             if(_type!=nlin_m){
2932                 return;
2933             }
2934             //            size_t tot_evals = 0;
2935             auto it = _nl_funcs.begin();
2936             while (it!=_nl_funcs.end()) {
2937                 auto f = (*it++);
2938                 DebugOff(f->to_str() << endl);
2939                 if (f->is_constant() && f->_evaluated) {
2940                     continue;
2941                 }
2942                 if (f->is_matrix_indexed()) {
2943 
2944 
2945                     f->_evaluated = false;
2946                     DebugOff(f->to_str()<<endl);
2947 
2948                     //                    cout << " | nb_instances = " << f->get_dim() << endl;
2949                     for (size_t i = 0; i < f->_indices->_ids->size(); i++) {
2950 
2951                         for (size_t j = 0; j < f->_indices->_ids->at(i).size(); j++) {
2952                             f->eval(i,j);
2953                         }
2954                         //                        tot_evals++;
2955                     }
2956                 }
2957                 else if (!f->is_matrix()) {
2958                     f->_evaluated = false;
2959                     DebugOff(f->to_str()<<endl);
2960                     //                    cout << " | nb_instances = " << f->get_dim() << endl;
2961                     for (size_t inst = 0; inst < f->get_nb_inst(); inst++) {
2962                         f->eval(inst);
2963                         //                        tot_evals++;
2964                     }
2965                 }
2966                 else {
2967                     DebugOff(f->to_str()<<endl);
2968                     f->_evaluated = false;
2969                     f->eval_matrix();
2970                 }
2971                 //                if (f->is_constant()) {
2972                 f->_evaluated = true;
2973 
2974                 //                }
2975             }
2976             //            cout << "tot_evals = " << tot_evals << endl;
2977         }
2978 
2979         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
fill_in_obj(const double * x,double & res,bool new_x)2980         void fill_in_obj(const double* x , double& res, bool new_x){
2981             if (new_x) {
2982                 set_x(x);
2983                 compute_funcs();
2984             }
2985             if(!_obj->is_constant()){
2986                 _obj->_evaluated = false;
2987             }
2988             res = _obj->eval();
2989             _obj->_new = false;
2990             DebugOff("Objective = " << to_string(res) << endl);
2991         }
2992 
2993         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
fill_in_grad_obj(const double * x,double * res,bool new_x)2994         void fill_in_grad_obj(const double* x , double* res, bool new_x){
2995             param_* v;
2996             shared_ptr<func<type>> df;
2997             size_t vid, vid_inst, index = 0;
2998             unique_id v_unique;
2999             if (new_x) {
3000                 set_x(x);
3001                 compute_funcs();
3002             }
3003             for (size_t i = 0; i<_nb_vars; i++) {
3004                 res[i] = 0;
3005             }
3006             if (_first_call_grad_obj) {
3007                 _obj_grad_vals.resize(_obj->get_nb_vars());
3008                 _first_call_grad_obj = false;
3009             }
3010             else if (_obj->is_linear()) {
3011                 //    else if (false) { /* No need to recompute jacobian for linear objectives */
3012                 for(auto& vi_p: _obj->get_vars())
3013                 {
3014                     v = vi_p.second.first.get();
3015                     vid = v->get_id();
3016                     if (v->_is_vector) {
3017                         for (size_t i = 0; i < v->get_dim(); i++) {
3018                             vid_inst = vid + v->get_id_inst(i);
3019                             res[vid_inst] = _obj_grad_vals[index++];
3020                         }
3021                     }
3022                     else {
3023                         vid_inst = vid + v->get_id_inst();
3024                         res[vid_inst] = _obj_grad_vals[index++];
3025                     }
3026                 }
3027                 return;
3028             }
3029             for(auto& vi_p: _obj->get_vars()) {
3030                 v = vi_p.second.first.get();
3031                 vid = v->get_id();
3032                 df = _obj->get_stored_derivative(v->_name);
3033                 if(!df->is_constant()){
3034                     df->_evaluated = false;
3035                 }
3036                 if (v->is_matrix()) {
3037                     //Unsupported yet.
3038                     throw invalid_argument("Matrices in the objective unsupported");
3039                 }
3040                 else if (v->_is_vector) {
3041                     for (size_t i = 0; i < v->get_dim(); i++) {
3042                         vid_inst = vid + v->get_id_inst(i);
3043                         res[vid_inst] = df->eval(i);
3044                         _obj_grad_vals[index++] =res[vid_inst];
3045                     }
3046                 }
3047                 else {
3048                     vid_inst = vid + v->get_id_inst();
3049                     res[vid_inst] = df->eval();
3050                     _obj_grad_vals[index++] =res[vid_inst];
3051                 }
3052             }
3053         }
3054 
3055         template<typename T1>
partition(std::string name,std::string model_type,const var<T1> & vlift,const var<T1> & v1,const var<T1> & v2,const vector<T1> & p1,const vector<T1> & p2)3056         void partition(std::string name, std::string model_type, const var<T1>& vlift, const var<T1>& v1, const var<T1>& v2, const vector<T1>& p1, const vector<T1>& p2) {
3057             // *************SHOULD I USE SOMETHING LIKE THIS??? add(MC4.in(*vlift._indices)); ***************//
3058             if(v1._name!=v2._name)
3059             {
3060                 if ((model_type == "Model_II") || (model_type == "Model_III")) {
3061 
3062                     // Obtain the number of partitions
3063                     int num_partition1 = p1.size()-1;  // number of partition on v1
3064                     int num_partition2 = p2.size()-1;  // number of partition on v2
3065 
3066                     /** Parameters */
3067                     // Bounds on variable v1
3068                     param<double> bounds1(name+"_bounds on variable v1");
3069                     bounds1.in(R(num_partition1+1));
3070                     for (int i=0; i<num_partition1+1; ++i) {
3071                         bounds1.set_val(i,p1[i]);
3072                     }
3073                     // Bounds on variable v2
3074                     param<double> bounds2(name+"_bounds on variable v2");
3075                     bounds2.in(R(num_partition2+1));
3076                     for (int i=0; i<num_partition2+1; ++i) {
3077                         bounds2.set_val(i,p2[i]);
3078                     }
3079                     // Function values on the extreme points
3080                     param<double> EP(name+"_function values in the grid");
3081                     EP.in(R((num_partition1+1)*(num_partition2+1)));
3082                     for (int j=0; j<num_partition2+1; ++j) {
3083                         for (int i=0; i<num_partition1+1; ++i) {
3084                             EP.set_val(i+j*(num_partition1+1),p1[i]*p2[j]);
3085                         }
3086                     }
3087 
3088                     // Lambda coefficient matrix when linking with partition variables
3089                     param<type> lambdaCOEF(name+"_lambda linking coefficients");
3090 
3091                     // Partition coefficient matrix when linking with lambda variables
3092                     param<type> zCOEF(name+"_partition linking coefficients");
3093 
3094                     // Partition assignment coefficients
3095                     param<type> zASGNCOEF(name+"_partition assignment coefficients");
3096 
3097                     if (model_type == "Model_II"){
3098                         // check if both of the variables involve a partition
3099                         if ((num_partition1>1) && (num_partition2>1)) {
3100 
3101                             // Lambda coefficient matrix when linking with partition variables
3102                             lambdaCOEF.set_size(num_partition1+num_partition2+2,((num_partition1+1)*(num_partition2+1)));
3103 
3104                             // Coefficients related to partition on x2
3105                             for (int i=0; i<num_partition2+1; ++i) {
3106                                 for (int j=0; j<num_partition1+1; ++j) {
3107                                     lambdaCOEF.set_val(i,j+i*(num_partition1+1),1);
3108                                 }
3109                             }
3110 
3111                             // Collect the corresponding indices of lambda related to partition on v1
3112                             vector<int> myIndexVector(num_partition2+1); //initial index vector for lambdas bounding partition variables for v1
3113                             for (int i = 0; i<myIndexVector.size(); ++i) {
3114                                 myIndexVector[i] = (i*(num_partition1+1));
3115                             }
3116 
3117                             // Coefficients related to partition on v1
3118                             for (int i=0; i<num_partition1+1; ++i) {
3119                                 for (int j=0; j<num_partition2+1; ++j) {
3120                                     lambdaCOEF.set_val(i+num_partition2+1,myIndexVector[j]+i,1);
3121                                 }
3122                             }
3123 
3124                             // Partition coefficient matrix when linking with lambda variables
3125                             zCOEF.set_size(num_partition1+num_partition2+2, num_partition1+num_partition2);
3126 
3127                             // Coefficients related to partition on v2
3128                             zCOEF.set_val(0, num_partition1, 1);
3129                             for (int i=1; i<num_partition2; ++i) {
3130                                 zCOEF.set_val(i,num_partition1+i-1,1);
3131                                 zCOEF.set_val(i,num_partition1+i, 1);
3132                             }
3133                             zCOEF.set_val(num_partition2, num_partition1+num_partition2-1, 1);
3134 
3135                             // Coefficients related to partition on v1
3136                             zCOEF.set_val(num_partition2+1, 0, 1);
3137                             for (int i=2; i<num_partition1+1; ++i) {
3138                                 zCOEF.set_val(i+num_partition2,i-2,1);
3139                                 zCOEF.set_val(i+num_partition2,i-1,1);
3140                             }
3141                             zCOEF.set_val(num_partition1+num_partition2+1, num_partition1-1, 1);
3142 
3143                             // Partition assignment coefficients
3144                             zASGNCOEF.set_size(2,num_partition1+num_partition2);
3145 
3146                             // Coefficients related to partition on v1
3147                             for (int i=0; i<num_partition1; ++i) {
3148                                 zASGNCOEF.set_val(0,i,1);
3149                             }
3150 
3151                             // Coefficients related to partition on v2
3152                             for (int i=0; i<num_partition2; ++i) {
3153                                 zASGNCOEF.set_val(1,i+num_partition1,1);
3154                             }
3155                         }
3156                         // check if the first variable v1 involves a partition
3157                         else if (num_partition1>1){
3158                             // Lambda coefficient matrix when linking with partition variables
3159                             lambdaCOEF.set_size(num_partition1+1,((num_partition1+1)*(num_partition2+1)));
3160 
3161                             // Collect the corresponding indices of lambda related to partition on v1
3162                             vector<int> myIndexVector(num_partition2+1); //initial index vector for lambdas bounding partition variables for x1
3163                             for (int i = 0; i<myIndexVector.size(); ++i) {
3164                                 myIndexVector[i] = (i*(num_partition1+1));
3165                             }
3166 
3167                             // Coefficients related to partition on v1
3168                             for (int i=0; i<num_partition1+1; ++i) {
3169                                 for (int j=0; j<num_partition2+1; ++j) {
3170                                     lambdaCOEF.set_val(i,myIndexVector[j]+i,1);
3171                                 }
3172                             }
3173 
3174                             // Partition coefficient matrix when linking with lambda variables
3175                             zCOEF.set_size(num_partition1+1, num_partition1);
3176 
3177                             // Coefficients related to partition on v1
3178                             zCOEF.set_val(0, 0, 1);
3179                             for (int i=1; i<num_partition1; ++i) {
3180                                 zCOEF.set_val(i,i-1,1);
3181                                 zCOEF.set_val(i,i,1);
3182                             }
3183                             zCOEF.set_val(num_partition1, num_partition1-1, 1);
3184 
3185                             // Partition assignment coefficients
3186                             zASGNCOEF.set_size(num_partition1);
3187 
3188                             // Coefficients related to partition on v1
3189                             for (int i=0; i<num_partition1; ++i) {
3190                                 zASGNCOEF.set_val(i,1);
3191                             }
3192                         }
3193                         // check if the first variable v2 involves a partition
3194                         else if (num_partition2>1){
3195                             // Lambda coefficient matrix when linking with partition variables
3196                             lambdaCOEF.set_size(num_partition2+1,((num_partition1+1)*(num_partition2+1)));
3197 
3198                             // Coefficients related to partition on v2
3199                             for (int i=0; i<num_partition2+1; ++i) {
3200                                 for (int j=0; j<num_partition1+1; ++j) {
3201                                     lambdaCOEF.set_val(i,j+i*(num_partition1+1),1);
3202                                 }
3203                             }
3204 
3205                             // Partition coefficient matrix when linking with lambda variables
3206                             zCOEF.set_size(num_partition2+1, num_partition2);
3207 
3208                             // Coefficients related to partition on v2
3209                             zCOEF.set_val(0, 0, 1);
3210                             for (int i=1; i<num_partition2; ++i) {
3211                                 zCOEF.set_val(i,i-1,1);
3212                                 zCOEF.set_val(i,i, 1);
3213                             }
3214                             zCOEF.set_val(num_partition2, num_partition2-1, 1);
3215 
3216                             // Partition assignment coefficients
3217                             zASGNCOEF.set_size(num_partition2);
3218 
3219                             // Coefficients related to partition on v2
3220                             for (int i=0; i<num_partition2; ++i) {
3221                                 zASGNCOEF.set_val(i,1);
3222                             }
3223                         }
3224                     }
3225                     else if (model_type == "Model_III"){
3226                         // check if both of the variables involve a partition
3227                         if ((num_partition1>1) && (num_partition2>1)) {
3228 
3229                             // Lambda coefficient matrix when linking with partition variables
3230                             lambdaCOEF.set_size((num_partition1-2)*2+2+(num_partition2-2)*2+2,((num_partition1+1)*(num_partition2+1)));
3231 
3232                             // Coefficients related to partition on v2
3233                             for (int i=0; i<num_partition1+1; i++) {
3234                                 lambdaCOEF.set_val(0,i,1);
3235                             }
3236 
3237                             for (int i=1; i<(num_partition2-2)*2+1; i=i+2) {
3238                                 for (int j=(i-1)/2 + 2; j<num_partition2+1; ++j) {
3239                                     for(int k=0; k<num_partition1+1; ++k){
3240                                         lambdaCOEF.set_val(i,k+j*(num_partition1+1),1);
3241                                         lambdaCOEF.set_val(i+1,k+j*(num_partition1+1),-1);
3242                                     }
3243                                 }
3244                             }
3245 
3246                             for (int i=0; i<num_partition1+1; i++) {
3247                                 lambdaCOEF.set_val((num_partition2-2)*2+1,i+(num_partition2)*(num_partition1+1),1);
3248                             }
3249 
3250 
3251                             // Collect the corresponding indices of lambda related to partition on v1
3252                             vector<int> myIndexVector(num_partition2+1); //initial index vector for lambdas bounding partition variables for x1
3253                             for (int i = 0; i<myIndexVector.size(); ++i) {
3254                                 myIndexVector[i] = (i*(num_partition1+1));
3255                             }
3256 
3257                             // Coefficients related to partition on v1
3258                             for (int i=0; i<num_partition2+1; i++) {
3259                                 lambdaCOEF.set_val((num_partition2-2)*2+2,myIndexVector[i],1);
3260                             }
3261 
3262                             for (int i=1; i<(num_partition1-2)*2+1; i=i+2) {
3263                                 for (int j=(i-1)/2 + 2; j<num_partition1+1; ++j) {
3264                                     for(int k=0; k<num_partition2+1; ++k){
3265                                         lambdaCOEF.set_val(i+(num_partition2-2)*2+2,myIndexVector[k]+j,1);
3266                                         lambdaCOEF.set_val(i+(num_partition2-2)*2+3,myIndexVector[k]+j,-1);
3267                                     }
3268                                 }
3269                             }
3270 
3271                             for (int i=0; i<num_partition2+1; i++) {
3272                                 lambdaCOEF.set_val((num_partition1-2)*2+2+(num_partition2-2)*2+1,myIndexVector[i]+num_partition1,1);
3273                             }
3274 
3275                             // Partition coefficient matrix when linking with lambda variables
3276                             zCOEF.set_size((num_partition1-2)*2+2+(num_partition2-2)*2+2, num_partition1+num_partition2);
3277 
3278                             // Coefficients related to partition on v2
3279                             zCOEF.set_val(0, num_partition1, 1);
3280                             for (int i=1; i<num_partition2; ++i) {
3281                                 zCOEF.set_val(1, num_partition1+i, 1);
3282                             }
3283 
3284                             for (int i=2; i<(num_partition2-2)*2+2; i=i+2) {
3285                                 for (int j=i/2+1; j<num_partition2; ++j) {
3286                                     zCOEF.set_val(i, num_partition1+j, -1);
3287                                     zCOEF.set_val(i+1, num_partition1+j, 1);
3288                                 }
3289                             }
3290 
3291                             // Coefficients related to partition on v1
3292                             zCOEF.set_val((num_partition2-2)*2+2, 0, 1);
3293                             for (int i=1; i<num_partition1; ++i) {
3294                                 zCOEF.set_val((num_partition2-2)*2+3, i, 1);
3295                             }
3296 
3297                             for (int i=2; i<(num_partition1-2)*2+2; i=i+2) {
3298                                 for (int j=i/2+1; j<num_partition1; ++j) {
3299                                     zCOEF.set_val(i+(num_partition2-2)*2+2, j, -1);
3300                                     zCOEF.set_val(i+(num_partition2-2)*2+3, j, 1);
3301                                 }
3302                             }
3303 
3304                             // Partition assignment coefficients
3305                             zASGNCOEF.set_size(2,num_partition1+num_partition2);
3306 
3307                             // Coefficients related to partition on v1
3308                             for (int i=0; i<num_partition1; ++i) {
3309                                 zASGNCOEF.set_val(0,i,1);
3310                             }
3311 
3312                             // Coefficients related to partition on v2
3313                             for (int i=0; i<num_partition2; ++i) {
3314                                 zASGNCOEF.set_val(1,i+num_partition1,1);
3315                             }
3316                         }
3317                         // check if the first variable v1 involves a partition
3318                         else if (num_partition1>1){
3319                             // Lambda coefficient matrix when linking with partition variables
3320                             lambdaCOEF.set_size((num_partition1-2)*2+2,((num_partition1+1)*(num_partition2+1)));
3321 
3322                             // Collect the corresponding indices of lambda related to partition on v1
3323                             vector<int> myIndexVector(num_partition2+1); //initial index vector for lambdas bounding partition variables for x1
3324                             for (int i = 0; i<myIndexVector.size(); ++i) {
3325                                 myIndexVector[i] = (i*(num_partition1+1));
3326                             }
3327 
3328                             // Coefficients related to partition on v1
3329                             for (int i=0; i<num_partition2+1; i++) {
3330                                 lambdaCOEF.set_val(0,myIndexVector[i],1);
3331                             }
3332 
3333                             for (int i=1; i<(num_partition1-2)*2+1; i=i+2) {
3334                                 for (int j=(i-1)/2 + 2; j<num_partition1+1; ++j) {
3335                                     for(int k=0; k<num_partition2+1; ++k){
3336                                         lambdaCOEF.set_val(i,myIndexVector[k]+j,1);
3337                                         lambdaCOEF.set_val(i+1,myIndexVector[k]+j,-1);
3338                                     }
3339                                 }
3340                             }
3341 
3342                             for (int i=0; i<num_partition2+1; i++) {
3343                                 lambdaCOEF.set_val((num_partition1-2)*2+1,myIndexVector[i]+num_partition1,1);
3344                             }
3345 
3346                             // Partition coefficient matrix when linking with lambda variables
3347                             zCOEF.set_size((num_partition1-2)*2+2, num_partition1);
3348 
3349                             // Coefficients related to partition on v1
3350                             zCOEF.set_val(0, 0, 1);
3351                             for (int i=1; i<num_partition1; ++i) {
3352                                 zCOEF.set_val(1, i, 1);
3353                             }
3354 
3355                             for (int i=2; i<(num_partition1-2)*2+2; i=i+2) {
3356                                 for (int j=i/2+1; j<num_partition1; ++j) {
3357                                     zCOEF.set_val(i, j, -1);
3358                                     zCOEF.set_val(i+1, j, 1);
3359                                 }
3360                             }
3361 
3362                             // Partition assignment coefficients
3363                             zASGNCOEF.set_size(num_partition1);
3364 
3365                             // Coefficients related to partition on v1
3366                             for (int i=0; i<num_partition1; ++i) {
3367                                 zASGNCOEF.set_val(i,1);
3368                             }
3369                         }
3370                         // check if the first variable v2 involves a partition
3371                         else if (num_partition2>1){
3372                             // Lambda coefficient matrix when linking with partition variables
3373                             lambdaCOEF.set_size((num_partition2-2)*2+2,((num_partition1+1)*(num_partition2+1)));
3374 
3375                             // Coefficients related to partition on v2
3376                             for (int i=0; i<num_partition1+1; i++) {
3377                                 lambdaCOEF.set_val(0,i,1);
3378                             }
3379 
3380                             for (int i=1; i<(num_partition2-2)*2+1; i=i+2) {
3381                                 for (int j=(i-1)/2 + 2; j<num_partition2+1; ++j) {
3382                                     for(int k=0; k<num_partition1+1; ++k){
3383                                         lambdaCOEF.set_val(i,k+j*(num_partition1+1),1);
3384                                         lambdaCOEF.set_val(i+1,k+j*(num_partition1+1),-1);
3385                                     }
3386                                 }
3387                             }
3388 
3389                             for (int i=0; i<num_partition1+1; i++) {
3390                                 lambdaCOEF.set_val((num_partition2-2)*2+1,i+(num_partition2)*(num_partition1+1),1);
3391                             }
3392 
3393 
3394                             // Partition coefficient matrix when linking with lambda variables
3395                             zCOEF.set_size((num_partition2-2)*2+2, num_partition2);
3396 
3397                             // Coefficients related to partition on v2
3398                             zCOEF.set_val(0, 0, 1);
3399                             for (int i=1; i<num_partition2; ++i) {
3400                                 zCOEF.set_val(1, i, 1);
3401                             }
3402 
3403                             for (int i=2; i<(num_partition2-2)*2+2; i=i+2) {
3404                                 for (int j=i/2+1; j<num_partition2; ++j) {
3405                                     zCOEF.set_val(i, j, -1);
3406                                     zCOEF.set_val(i+1, j, 1);
3407                                 }
3408                             }
3409 
3410                             // Partition assignment coefficients
3411                             zASGNCOEF.set_size(num_partition2);
3412 
3413                             // Coefficients related to partition on v2
3414                             for (int i=0; i<num_partition2; ++i) {
3415                                 zASGNCOEF.set_val(i,1);
3416                             }
3417                         }
3418                     }
3419 
3420 
3421                     /** Variables */
3422                     // Convex combination variables
3423                     var<type> lambda(name+"_lambda",pos_);
3424                     lambda.in(R((num_partition1+1)*(num_partition2+1)));
3425                     add(lambda);
3426 
3427                     /** Constraints */
3428                     // Representation of the bilinear term with convex combination
3429                     Constraint<type> BLNREP(name+"_Representation of bilinear term");
3430                     BLNREP = EP.tr()*lambda - vlift;
3431                     add(BLNREP == 0);
3432 
3433                     // Representation of v1 with convex combination
3434                     Constraint<type> v1REP(name+"_Representation of v1");
3435                     for(int i=0;i<num_partition2+1;i++)
3436                     {
3437                         v1REP += bounds1.tr()*lambda.in(range(i*(num_partition1+1),i*(num_partition1+1)+num_partition1));
3438                     }
3439                     v1REP -= v1;
3440                     add(v1REP == 0);
3441 
3442                     // Representation of v2 with convex combination
3443                     Constraint<type> v2REP(name+"_Representation of v2");
3444                     for(int i=0;i<num_partition2+1;i++)
3445                     {
3446                         v2REP += bounds2.eval(i)*sum(lambda.in(range(i*(num_partition1+1),i*(num_partition1+1)+num_partition1)));
3447                     }
3448                     v2REP -= v2;
3449                     add(v2REP == 0);
3450 
3451                     // check if any of the variables involve a partition
3452                     if ((num_partition1>1) || (num_partition2>1)){
3453                         // Partition variables
3454                         var<int> z(name+"_z",0,1);
3455                         z.in(R((num_partition1)*(num_partition1>1)+(num_partition2)*(num_partition2>1)));
3456                         add(z);
3457 
3458                         // Partition Assignment
3459                         Constraint<type> zASGN(name+"_Partition assignment");
3460                         zASGN = product(zASGNCOEF,z);
3461                         add(zASGN == 1);
3462 
3463                         // Linking partition variables with lambda
3464                         Constraint<type> zLINKlambda(name+"_Linking the partition variables to lambda");
3465                         zLINKlambda = product(lambdaCOEF,lambda) - product(zCOEF,z);
3466                         add(zLINKlambda<= 0);
3467                     }
3468 
3469                     // Summation over lambda is 1
3470                     Constraint<type> lambdaSUM(name+"_Summation over lambda");
3471                     lambdaSUM = sum(lambda);
3472                     add(lambdaSUM == 1);
3473 
3474 
3475                 }
3476                 else{
3477                     throw invalid_argument("Model type is invalid. The options are 'Model_II' and 'Model_III'!\n");
3478                 }
3479             }
3480             else{ //this case means the two variables are same and the approximation is now on quadratic term
3481                 if ((model_type == "Model_II") || (model_type == "Model_III")) {
3482 
3483                     if (p1 == p2) //the partition vectors must be same since the variables are same
3484                     {
3485                         // Obtain the number of partitions
3486                         int num_partition1 = p1.size()-1;  // number of partition on v1 & v2 (they are same)
3487 
3488                         /** Parameters */
3489                         // Bounds on variable v1 & v2
3490                         param<double> bounds1(name+"_bounds on variables v1&v2");
3491                         bounds1.in(R(num_partition1+1));
3492                         for (int i=0; i<num_partition1+1; ++i) {
3493                             bounds1.set_val(i,p1[i]);
3494                         }
3495 
3496                         // Function values on the extreme points
3497                         param<double> EP(name+"_function values in the grid");
3498                         EP.in(R((num_partition1+1)));
3499                         for (int i=0; i<num_partition1+1; ++i) {
3500                             EP.set_val(i,p1[i]*p1[i]);
3501                         }
3502 
3503                         // Lambda coefficient matrix when linking with partition variables
3504                         param<type> lambdaCOEF(name+"_lambda linking coefficients");
3505 
3506                         // Partition coefficient matrix when linking with lambda variables
3507                         param<type> zCOEF(name+"_partition linking coefficients");
3508 
3509                         // Partition assignment coefficients
3510                         param<type> zASGNCOEF(name+"_partition assignment coefficients");
3511 
3512                         if (model_type == "Model_II"){
3513                             // check if the the variables involve a partition
3514                             if (num_partition1>1){
3515                                 // Lambda coefficient matrix when linking with partition variables
3516                                 lambdaCOEF.set_size(num_partition1+1,num_partition1+1);
3517 
3518                                 // Coefficients related to partition on variables v1 & v2
3519                                 for (int i=0; i<num_partition1+1; ++i) {
3520                                     lambdaCOEF.set_val(i,i,1);
3521                                 }
3522 
3523                                 // Partition coefficient matrix when linking with lambda variables
3524                                 zCOEF.set_size(num_partition1+1, num_partition1);
3525 
3526                                 // Coefficients related to partition on variables v1 & v2
3527                                 zCOEF.set_val(0, 0, 1);
3528                                 for (int i=1; i<num_partition1; ++i) {
3529                                     zCOEF.set_val(i,i-1,1);
3530                                     zCOEF.set_val(i,i,1);
3531                                 }
3532                                 zCOEF.set_val(num_partition1, num_partition1-1, 1);
3533 
3534                                 // Partition assignment coefficients
3535                                 zASGNCOEF.set_size(num_partition1);
3536 
3537                                 // Coefficients related to partition on variables v1 & v2
3538                                 for (int i=0; i<num_partition1; ++i) {
3539                                     zASGNCOEF.set_val(i,1);
3540                                 }
3541                             }
3542                         }
3543                         else if (model_type == "Model_III"){
3544                             // check if both of the variables involve a partition
3545                             if (num_partition1>1){
3546                                 // Lambda coefficient matrix when linking with partition variables
3547                                 lambdaCOEF.set_size((num_partition1-2)*2+2,num_partition1+1);
3548 
3549                                 // Coefficients related to partition on variables v1 & v2
3550                                 lambdaCOEF.set_val(0,0,1);
3551 
3552                                 for (int i=1; i<(num_partition1-2)*2+1; i=i+2) {
3553                                     for (int j=(i-1)/2 + 2; j<num_partition1+1; ++j) {
3554                                         lambdaCOEF.set_val(i,j,1);
3555                                         lambdaCOEF.set_val(i+1,j,-1);
3556                                     }
3557                                 }
3558                                 lambdaCOEF.set_val((num_partition1-2)*2+1,num_partition1,1);
3559 
3560                                 // Partition coefficient matrix when linking with lambda variables
3561                                 zCOEF.set_size((num_partition1-2)*2+2, num_partition1);
3562 
3563                                 // Coefficients related to partition on v1
3564                                 zCOEF.set_val(0, 0, 1);
3565                                 for (int i=1; i<num_partition1; ++i) {
3566                                     zCOEF.set_val(1, i, 1);
3567                                 }
3568 
3569                                 for (int i=2; i<(num_partition1-2)*2+2; i=i+2) {
3570                                     for (int j=i/2+1; j<num_partition1; ++j) {
3571                                         zCOEF.set_val(i, j, -1);
3572                                         zCOEF.set_val(i+1, j, 1);
3573                                     }
3574                                 }
3575 
3576                                 // Partition assignment coefficients
3577                                 zASGNCOEF.set_size(num_partition1);
3578 
3579                                 // Coefficients related to partition on v1
3580                                 for (int i=0; i<num_partition1; ++i) {
3581                                     zASGNCOEF.set_val(i,1);
3582                                 }
3583                             }
3584                         }
3585 
3586                         /** Variables */
3587                         // Convex combination variables
3588                         var<type> lambda(name+"_lambda",pos_);
3589                         lambda.in(R((num_partition1+1)));
3590                         add(lambda);
3591 
3592                         /** Constraints */
3593                         // Representation of the bilinear term with secant
3594                         Constraint<type> BLNREP_UB(name+"_Representation of quadratic term (UB)");
3595                         BLNREP_UB = EP.tr()*lambda - vlift;
3596                         add(BLNREP_UB >= 0); /*using it as the upper bound to be valid*/
3597 
3598                         Constraint<type> BLNREP_LB(name+"_Representation of quadratic term (LB)");
3599                         BLNREP_LB = pow(v1,2) - vlift;
3600                         add(BLNREP_LB <= 0); /*using it as the lower bound to be valid*/
3601 
3602                         // Representation of v1=v2 with convex combination
3603                         Constraint<type> v1REP(name+"_Representation of v1");
3604                         v1REP == bounds1.tr()*lambda - v1;
3605                         add(v1REP == 0);
3606 
3607                         // check if any of the variables involve a partition
3608                         if (num_partition1>1) {
3609                             // Partition variables
3610                             var<int> z(name+"_z",0,1);
3611                             z.in(R((num_partition1)));
3612                             add(z);
3613 
3614                             // Partition Assignment
3615                             Constraint<type> zASGN(name+"_Partition assignment");
3616                             zASGN = product(zASGNCOEF,z);
3617                             add(zASGN == 1);
3618 
3619                             // Linking partition variables with lambda
3620                             Constraint<type> zLINKlambda(name+"_Linking the partition variables to lambda");
3621                             zLINKlambda = product(lambdaCOEF,lambda) - product(zCOEF,z);
3622                             add(zLINKlambda<= 0);
3623                         }
3624 
3625                         // Summation over lambda is 1
3626                         Constraint<type> lambdaSUM(name+"_Summation over lambda");
3627                         lambdaSUM = sum(lambda);
3628                         add(lambdaSUM == 1);
3629                     }
3630                     else
3631                     {
3632                         throw invalid_argument("Partition bounds must be same since the two varibles are same.\n");
3633                     }
3634                 }
3635                 else{
3636                     throw invalid_argument("Model type is invalid. The options are 'Model_II' and 'Model_III'!\n");
3637                 }
3638             }
3639         }
3640 
3641 
3642         template<typename T1>
add_McCormick(std::string name,const var<T1> & vlift,const var<T1> & v1,const var<T1> & v2)3643         void add_McCormick(std::string name, const var<T1>& vlift, const var<T1>& v1, const var<T1>& v2) {
3644             Constraint<type> MC1(name+"_McCormick1");
3645             param<T1> lb1_ = v1.get_lb(), lb2_ = v2.get_lb(), ub1_ = v1.get_ub(), ub2_= v2.get_ub();
3646             if(!v1._lb->func_is_number()){
3647                 lb1_ = v1.get_lb().in(*v1._indices);
3648             }
3649             else{
3650                 lb1_ = v1.get_lb().in(*v1._indices);
3651                 lb1_ = v1.get_lb(0);
3652             }
3653             if(!v2._lb->func_is_number()){
3654                 lb2_ = v2.get_lb().in(*v2._indices);
3655             }
3656             else{
3657                 lb2_ = v2.get_lb().in(*v2._indices);
3658                 lb2_ = v2.get_lb(0);
3659             }
3660             if(!v1._ub->func_is_number()){
3661                 ub1_ = v1.get_ub().in(*v1._indices);
3662             }
3663             else{
3664                 ub1_ = v1.get_ub().in(*v1._indices);
3665                 ub1_ = v1.get_ub(0);
3666             }
3667             if(!v2._ub->func_is_number()){
3668                 ub2_ = v2.get_ub().in(*v2._indices);
3669             }
3670             else{
3671                 ub2_ = v2.get_ub().in(*v2._indices);
3672                 ub2_ = v2.get_ub(0);
3673             }
3674             bool var_equal=false;
3675             if(v1._name==v2._name)
3676                 var_equal=true;
3677             if(!var_equal)
3678             {
3679                 Constraint<type> MC1(name+"_McCormick1");
3680                 MC1 += vlift;
3681                 MC1 -= lb1_*v2 + lb2_*v1 - lb1_*lb2_;
3682                 MC1 >= 0;
3683                 //                MC1._relaxed = true; /* MC1 is a relaxation of a non-convex constraint */
3684                 add(MC1.in(*vlift._indices));
3685                 //    MC1.print();
3686                 Constraint<type> MC2(name+"_McCormick2");
3687                 MC2 += vlift;
3688                 MC2 -= ub1_*v2 + ub2_*v1 - ub1_*ub2_;
3689                 MC2 >= 0;
3690                 //                MC2._relaxed = true; /* MC2 is a relaxation of a non-convex constraint */
3691                 add(MC2.in(*vlift._indices));
3692 
3693                 //    //    MC2.print();
3694                 Constraint<type> MC3(name+"_McCormick3");
3695                 MC3 += vlift;
3696                 MC3 -= lb1_*v2 + ub2_*v1 - lb1_*ub2_;
3697                 MC3 <= 0;
3698                 //                MC3._relaxed = true; /* MC3 is a relaxation of a non-convex constraint */
3699                 add(MC3.in(*vlift._indices));
3700                 //    //    MC3.print();
3701                 Constraint<type> MC4(name+"_McCormick4");
3702                 MC4 += vlift;
3703                 MC4 -= ub1_*v2 + lb2_*v1 - ub1_*lb2_;
3704                 MC4 <= 0;
3705                 //                MC4._relaxed = true; /* MC4 is a relaxation of a non-convex constraint */
3706                 add(MC4.in(*vlift._indices));
3707             }
3708             else {
3709                 if (vlift._lift_ub){
3710                 Constraint<type> MC4(name+"_Secant");
3711                 MC4 += vlift;
3712                 MC4 -= (ub1_+lb1_)*v1 - ub1_*lb1_;
3713                 MC4 <= 0;
3714                 add(MC4.in(*vlift._indices));
3715                 }
3716 
3717                 if (vlift._lift_lb){
3718                 Constraint<type> MC5(name+"_McCormick_squared");
3719                     MC5 += vlift;
3720 
3721                     MC5 -= v1*v1;
3722 
3723                     MC5 >= 0;
3724                     MC5._relaxed = true; /* MC5 is a relaxation of a non-convex constraint */
3725                     add(MC5.in(*vlift._indices));
3726                 }
3727 
3728                 }
3729             }
3730             //    MC4.print();
3731 
3732 
3733         //        template<typename T1>
3734         //        void add_McCormick(std::string name, const var<T1>& vlift, const var<T1>& v1, const var<T1>& v2) {
3735         //            Constraint<type> MC1(name+"_McCormick1");
3736         //            auto lb1 = v1.get_lb(v1.get_id_inst());
3737         //            auto lb2 = v2.get_lb(v2.get_id_inst());
3738         //            auto ub1 = v1.get_ub(v1.get_id_inst());
3739         //            auto ub2 = v2.get_ub(v2.get_id_inst());
3740         //            auto lb1_ = v1.get_lb().in(*v1._indices);
3741         //            auto lb2_ = v2.get_lb().in(*v2._indices);
3742         //            auto ub1_ = v1.get_ub().in(*v1._indices);
3743         //            auto ub2_ = v2.get_ub().in(*v2._indices);
3744         //            bool template_cstr = v1._dim[0]>1;
3745         //            MC1 += vlift;
3746         //            if(template_cstr){//Template constraint
3747         //                MC1 -= lb1_*v2 + lb2_*v1 - lb1_*lb2_;
3748         //            }
3749         //            else {
3750         //                MC1 -= lb1*v2 + lb2*v1 - lb1*lb2;
3751         //            }
3752         //            MC1 >= 0;
3753         //            add(MC1.in(*vlift._indices));
3754         //            //    MC1.print();
3755         //            Constraint<type> MC2(name+"_McCormick2");
3756         //            MC2 += vlift;
3757         //            if(template_cstr){//Template constraint
3758         //                MC2 -= ub1_*v2 + ub2_*v1 - ub1_*ub2_;
3759         //            }
3760         //            else {
3761         //                MC2 -= ub1*v2 + ub2*v1 - ub1*ub2;
3762         //            }
3763         //            MC2 >= 0;
3764         //            add(MC2.in(*vlift._indices));
3765         //            //    //    MC2.print();
3766         //            Constraint<type> MC3(name+"_McCormick3");
3767         //            MC3 += vlift;
3768         //            if(template_cstr){//Template constraint
3769         //                MC3 -= lb1_*v2 + ub2_*v1 - lb1_*ub2_;
3770         //            }
3771         //            else {
3772         //                MC3 -= lb1*v2 + ub2*v1 - lb1*ub2;
3773         //            }
3774         //            MC3 <= 0;
3775         //            add(MC3.in(*vlift._indices));
3776         //            //    //    MC3.print();
3777         //            Constraint<type> MC4(name+"_McCormick4");
3778         //            MC4 += vlift;
3779         //            if(template_cstr){//Template constraint
3780         //                MC4 -= ub1_*v2 + lb2_*v1 - ub1_*lb2_;
3781         //            }
3782         //            else{
3783         //                MC4 -= ub1*v2 + lb2*v1 - ub1*lb2;
3784         //            }
3785         //            MC4 <= 0;
3786         //            add(MC4.in(*vlift._indices));
3787         //            //    MC4.print();
3788         //        }
3789 
3790 
3791         /** Build the sequential McCormick relaxation for polynomial programs **/
3792 
build_McCormick()3793         shared_ptr<Model<type>> build_McCormick(){
3794             //            replace_integers();
3795             if (_type==nlin_m) {
3796                 cerr << "Can only build a McCormick relaxation for polynomial programs, returning null" << endl;
3797                 return nullptr;
3798             }
3799             if (_type==lin_m) {
3800                 cerr << "No need to build a McCormick relaxation for a linear program, returning null" << endl;
3801                 return nullptr;
3802             }
3803             shared_ptr<Model> Mc = make_shared<Model>("McCormick Relaxation");
3804             //    shared_ptr<Constraint<type>> cstr;
3805             //    param_* v;
3806             //    for (auto &var_p:_vars) {
3807             //        v = var_p.second;
3808             //
3809             //        switch (v->get_intype()) {// TODO check for other types
3810             //            case double_:
3811             //                Mc->add(*(var<double>*)v);
3812             //                break;
3813             //            case integer_:
3814             //                Mc->add(*(var<int>*)v);
3815             //                break;
3816             //            case binary_:
3817             //                Mc->add(*(var<bool>*)v);
3818             //                break;
3819             //            default:
3820             //                break;
3821             //        }
3822             //    }
3823             //    if ((_obj->is_convex() && _objt==minimize) || (_obj->is_concave() && _objt==maximize)) {
3824             //        Mc->_obj = _obj;
3825             //    }
3826             //    else{
3827             //        func<double> new_obj = *_obj->get_cst();
3828             //        Mc->_objt = _objt;
3829             //        for(auto &lt : _obj->get_lterms()){
3830             //            new_obj->insert(lt.second);
3831             //        }
3832             //        for(auto &qt : _obj->get_qterms()){
3833             //
3834             //            if ((_objt==maximize && _obj->get_convexity(qt.second)==concave_) || (_objt==minimize && _obj->get_convexity(qt.second)==convex_)) {
3835             //                new_obj->insert(qt.second);
3836             //            }
3837             //            else {//Lift products
3838             //                string new_name;
3839             //                if(qt.second._p->first==qt.second._p->second){
3840             //                    new_name  = qt.second._p->first->_name+"²_lifted";
3841             //                }
3842             //                else {
3843             //                    new_name = qt.second._p->first->_name+"_"+qt.second._p->second->_name+"_lifted";
3844             //                }
3845             //                var<> v(new_name);
3846             //                auto new_v = Mc->get_var_ptr(new_name);
3847             //                if(new_v == nullptr){
3848             //                    Mc->add(v);
3849             //                    new_v = (param_*)&v;
3850             //                }
3851             //                Mc->add_McCormick(new_v->_name, new_v, qt.second._p->first, qt.second._p->second);
3852             //                if(qt.second._sign) {
3853             //                    new_obj += *new_v;
3854             //                }
3855             //                else {
3856             //                    new_obj -= *new_v;
3857             //                }
3858             //            }
3859             //        }
3860             //    }
3861             //    for (auto &cst_p:_cons) {
3862             //        cstr = cst_p.second;
3863             //        if (cstr->is_convex()) {
3864             //            Mc->add(*cstr);
3865             //        }
3866             //        else if(cstr->func_::is_convex()){
3867             //            Constraint new_cstr(*cstr);
3868             //            new_cstr._ctype = leq;
3869             //            Mc->add(new_cstr);
3870             //        }
3871             //        else if(cstr->func_::is_concave()){
3872             //            Constraint new_cstr(*cstr);
3873             //            new_cstr._ctype = geq;
3874             //            Mc->add(new_cstr);
3875             //        }
3876             //        else{
3877             //            Constraint new_cstr(cstr->get_name()+"_lifted");
3878             //            new_cstr += *cstr->get_cst();
3879             //            new_cstr._ctype = cstr->_ctype;
3880             //            new_cstr._rhs = cstr->get_rhs();
3881             //            for(auto &lt : cstr->get_lterms()){
3882             //                new_cstr.insert(lt.second);
3883             //            }
3884             //            for(auto &qt : cstr->get_qterms()){
3885             //
3886             //                if ((cstr->_ctype==geq && cstr->get_convexity(qt.second)==concave_) || (cstr->_ctype==leq && cstr->get_convexity(qt.second)==convex_)) {
3887             //                    new_cstr.insert(qt.second);
3888             //                }
3889             //                else {//Lift products
3890             //                    string new_name;
3891             //                    if(qt.second._p->first==qt.second._p->second){
3892             //                        new_name  = qt.second._p->first->_name+"²_lifted";
3893             //                    }
3894             //                    else {
3895             //                        new_name = qt.second._p->first->_name+"_"+qt.second._p->second->_name+"_lifted";
3896             //                    }
3897             //                    var<> v(new_name);
3898             //                    auto new_v = Mc->get_var_ptr(new_name);
3899             //                    if(new_v == nullptr){
3900             //                        Mc->add(v);
3901             //                        new_v = (param_*)&v;
3902             //                    }
3903             //                    Mc->add_McCormick(new_v->_name, new_v, qt.second._p->first, qt.second._p->second);
3904             //                    if(qt.second._sign) {
3905             //                        new_cstr += *new_v;
3906             //                    }
3907             //                    else {
3908             //                        new_cstr -= *new_v;
3909             //                    }
3910             //                }
3911             //            }
3912             //            //            for(auto &qt : cstr->get_qterms()){
3913             //            //TODO polynomial part
3914             //            Mc->add(new_cstr);
3915             //        }
3916             //    }
3917             return Mc;
3918         }
3919 
get_obj_val()3920         type get_obj_val() const{
3921             return _obj->_val->at(0);
3922         }
3923 
3924         void print_obj_val(int prec = 5) const{
3925             cout << "Objective = " << to_string_with_precision(_obj->eval(),prec) << endl;
3926         }
3927 
3928         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
fill_in_cstr(const double * x,double * res,bool new_x)3929         void fill_in_cstr(const double* x , double* res, bool new_x){
3930             if (new_x) {
3931                 set_x(x);
3932                 compute_funcs();
3933             }
3934             compute_constrs<type>(_cons_vec, res, 0, _cons_vec.size());
3935             return;
3936             unsigned nr_threads = std::thread::hardware_concurrency();
3937             if(nr_threads>_cons_vec.size()){
3938                 nr_threads=_cons_vec.size();
3939             }
3940             if (nr_threads==0) {
3941                 nr_threads = 1;
3942             }
3943             vector<thread> threads;
3944             /* Split cons into nr_threads parts */
3945             vector<size_t> limits = bounds(nr_threads, _cons_vec.size());
3946             /* Launch all threads in parallel */
3947             for (unsigned i = 0; i < nr_threads; ++i) {
3948                 threads.push_back(thread(compute_constrs<type>, ref(_cons_vec), res, limits[i], limits[i+1]));
3949             }
3950             /* Join the threads with the main thread */
3951             for(auto &t : threads){
3952                 t.join();
3953             }
3954         }
3955 
3956 
3957 
fill_in_jac_nnz(int * iRow,int * jCol)3958         void fill_in_jac_nnz(int* iRow , int* jCol){
3959             size_t idx=0, id = 0;
3960             size_t cid = 0;
3961             size_t vid = 0;
3962             Constraint<type>* c = NULL;
3963             param_* v = NULL;
3964             /* return the structure of the jacobian */
3965             for(auto& c_p :_cons)
3966             {
3967                 c = c_p.second.get();
3968                 c->_jac_cstr_idx = idx;
3969                 auto nb_ins = c->get_nb_inst();
3970                 for (auto &v_p: c->get_vars()){
3971                     v = v_p.second.first.get();
3972                     vid = v->get_id();
3973                     id = 0;
3974                     for (size_t inst = 0; inst< nb_ins; inst++){
3975                         if (!*c->_all_lazy || !c->_lazy[inst]) {
3976                             cid = c->_id+id++;
3977                             if (v->_is_vector || v->is_matrix_indexed()) {
3978                                 auto dim = v->get_dim(inst);
3979                                 for (size_t j = 0; j<dim; j++) {
3980                                     iRow[idx] = cid;
3981                                     jCol[idx] = vid + v->get_id_inst(inst, j);
3982                                     idx++;
3983                                 }
3984                             }
3985                             else {
3986                                 iRow[idx] = cid;
3987                                 jCol[idx] = vid + v->get_id_inst(inst);
3988                                 idx++;
3989                             }
3990                         }
3991                     }
3992                 }
3993             }
3994             if (idx!=_nnz_g) {
3995                 throw invalid_argument("idx!=_nnz_g");
3996             }
3997         }
3998 
3999 
4000 
4001         /* Fill the nonzero values in the jacobian */
4002         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
fill_in_jac(const double * x,double * res,bool new_x)4003         void fill_in_jac(const double* x , double* res, bool new_x){
4004             //    if (!_first_call_jac && (!new_x || _type==lin_m)) { /* No need to recompute jacobian for linear models */
4005             if (new_x) {
4006                 set_x(x);
4007                 compute_funcs();
4008             }
4009             for (size_t i = 0; i<_nnz_g; i++) {
4010                 res[i] = 0;
4011             }
4012             if (!_first_call_jac && (_type==lin_m)) { /* No need to recompute jacobian for linear models */
4013                 //            if (false) { /* No need to recompute jacobian for linear models */
4014                 for (size_t i = 0; i< _nnz_g; i++) {
4015                     res[i] = _jac_vals[i];
4016                 }
4017                 return;
4018             }
4019             size_t idx=0;
4020             size_t cid = 0;
4021             string vid;
4022             Constraint<type>* c = NULL;
4023             param_* v = NULL;
4024             shared_ptr<func<type>> dfdx;
4025             //    vector<Constraint*> cons;
4026             if (_type!=nlin_m) {//Polynomial, Quadratic or Linear
4027 
4028                 compute_jac(_cons_vec, res, 0, _cons_vec.size(), _first_call_jac, _jac_vals);
4029                 return;
4030                 unsigned nr_threads = std::thread::hardware_concurrency();
4031                 if(nr_threads>_cons_vec.size()){
4032                     nr_threads=_cons_vec.size();
4033                 }
4034                 if (nr_threads==0) {
4035                     nr_threads = 1;
4036                 }
4037 #ifdef USE_MPI
4038                 nr_threads = 1;
4039 #endif
4040                 vector<thread> threads;
4041                 /* Split cons into nr_threads parts */
4042                 vector<size_t> limits = bounds(nr_threads, _cons_vec.size());
4043 
4044                 /* Launch all threads in parallel */
4045                 for (unsigned i = 0; i < nr_threads; ++i) {
4046                     threads.push_back(thread(compute_jac<type>, ref(_cons_vec), res, limits[i], limits[i+1], _first_call_jac, ref(_jac_vals)));
4047                 }
4048                 /* Join the threads with the main thread */
4049                 for(auto &t : threads){
4050                     t.join();
4051                 }
4052                 _first_call_jac = false;
4053                 return;
4054             }
4055             for(auto& c_p :_cons)
4056             {
4057                 c = c_p.second.get();
4058                 auto nb_ins = c->get_nb_inst();
4059                 size_t id = 0;
4060                 if (c->is_linear() && !_first_call_jac) {
4061                     //        if (false) {
4062                     DebugOff("Linear constraint, using stored jacobian!\n");
4063                     for (size_t i = 0; i<nb_ins; i++) {
4064                         if (!*c->_all_lazy || !c->_lazy[i]) {
4065                             for (size_t j = 0; j<c->get_nb_vars(i); j++) {
4066                                 res[idx] = _jac_vals[idx];
4067                                 idx++;
4068                             }
4069                         }
4070                     }
4071                 }
4072                 else {
4073                     //            if (_type==nlin_m) {
4074                     for (auto &v_p: c->get_vars()){
4075                         v = v_p.second.first.get();
4076                         vid = v->_name;
4077                         dfdx = c->get_stored_derivative(vid);
4078                         if (dfdx->func_is_number()) {
4079 
4080 
4081 
4082 
4083                             for (size_t inst = 0; inst< nb_ins; inst++){
4084                                 if (!*c->_all_lazy || !c->_lazy[inst]) {
4085                                     cid = c->_id+id++;
4086 
4087                                     if (v->_is_vector || v->is_matrix_indexed()) {
4088                                         auto dim = v->get_dim(inst);
4089                                         for (size_t j = 0; j<dim; j++) {
4090 
4091 
4092 
4093 
4094 
4095 
4096 
4097                                             res[idx] = dfdx->_val->at(0);
4098                                             _jac_vals[idx] = res[idx];
4099                                             idx++;
4100                                         }
4101                                     }
4102                                     else {
4103                                         res[idx] = dfdx->_val->at(0);
4104                                         _jac_vals[idx] = res[idx];
4105                                         idx++;
4106                                     }
4107                                 }
4108                             }
4109                         }
4110                         else {
4111                             for (size_t inst = 0; inst< nb_ins; inst++){
4112                                 if (!*c->_all_lazy || !c->_lazy[inst]) {
4113                                     cid = c->_id+id++;
4114                                     if (v->_is_vector || v->is_matrix_indexed()) {
4115                                         auto dim = v->get_dim(inst);
4116                                         if (dfdx->is_matrix()) {
4117                                             for (size_t j = 0; j<dim; j++) {
4118                                                 res[idx] += dfdx->eval(j,inst);
4119                                                 _jac_vals[idx] = res[idx];
4120                                                 idx++;
4121                                             }
4122                                         }
4123                                         else {
4124                                             for (size_t j = 0; j<dim; j++) {
4125                                                 res[idx] += dfdx->eval(inst,j);
4126                                                 //                                                res[idx] += dfdx->_val->at(j);//TODO check double indexed funcs
4127                                                 _jac_vals[idx] = res[idx];
4128                                                 DebugOff("jac_val["<< idx <<"] = " << _jac_vals[idx] << endl);
4129                                                 idx++;
4130                                             }
4131                                         }
4132                                     }
4133                                     else {
4134                                         res[idx] = dfdx->_val->at(inst);
4135                                         _jac_vals[idx] = res[idx];
4136                                         idx++;
4137                                     }
4138                                 }
4139                             }
4140                         }
4141                     }
4142                     //            }
4143                     //            else {
4144                     //                for (auto &v_p: c->get_vars()){
4145                     //                    v = v_p.second.first.get();
4146                     //                    vid = v->_name;
4147                     //                    dfdx = c->get_stored_derivative(vid);
4148                     //                    for (size_t inst = 0; inst< nb_ins; inst++){
4149                     //                        cid = c->_id+inst;
4150                     //                        if (v->_is_vector) {
4151                     //                            auto dim = v->get_dim(inst);
4152                     //                            for (size_t j = 0; j<dim; j++) {
4153                     //                                res[idx] = dfdx->eval(inst,j);
4154                     //                                _jac_vals[idx] = res[idx];
4155                     //                                idx++;
4156                     //                            }
4157                     //                        }
4158                     //                        else {
4159                     //                            res[idx] = dfdx->eval(inst);
4160                     //                            _jac_vals[idx] = res[idx];
4161                     //                            idx++;
4162                     //                        }
4163                     //                    }
4164                     //                }
4165                     //            }
4166                 }
4167             }
4168             _first_call_jac = false;
4169         }
4170 
4171 
4172 
4173 
4174 #ifdef USE_IPOPT
4175 
fill_in_var_linearity(Ipopt::TNLP::LinearityType * param_types)4176         void fill_in_var_linearity(Ipopt::TNLP::LinearityType* param_types){
4177             size_t vid = 0;
4178             bool linear = true;
4179             for(auto& vi: _vars)
4180             {
4181                 vid = vi.second->get_id();
4182                 for (size_t i = 0; i < vi.second->get_dim(); i++) {
4183                     //            linear = true;
4184                     //            for(auto &c: _v_in_cons[vid + i])
4185                     //            {
4186                     //                if (!c->is_linear()) {
4187                     //                    linear=false;
4188                     //                }
4189                     //            }
4190                     if (linear) param_types[vid + i]=Ipopt::TNLP::LINEAR;
4191                     else param_types[vid + i] = Ipopt::TNLP::NON_LINEAR;
4192                 }
4193             }
4194         }
4195 
4196 
fill_in_cstr_linearity(Ipopt::TNLP::LinearityType * const_types)4197         void fill_in_cstr_linearity(Ipopt::TNLP::LinearityType* const_types){
4198             Constraint<type>* c = nullptr;
4199             bool lin = false;
4200             size_t cid = 0, id = 0;
4201             for(auto& c_p :_cons)
4202             {
4203                 c = c_p.second.get();
4204                 id = 0;
4205                 if (c->is_linear() || c->is_constant()) {
4206                     lin = true;
4207                 }
4208                 else {
4209                     lin = false;
4210                 }
4211                 auto nb_ins = c->get_nb_inst();
4212                 for (size_t i = 0; i< nb_ins; i++){
4213                     if (!*c->_all_lazy || !c->_lazy[i]) {
4214                         cid = c->_id+id++;
4215                         if (lin) {
4216                             const_types[cid]=Ipopt::TNLP::LINEAR;
4217                         }
4218                         else {
4219                             const_types[cid] = Ipopt::TNLP::NON_LINEAR;
4220                         }
4221                     }
4222                 }
4223             }
4224         }
4225 #endif
4226 
4227 
fill_in_hess_nnz(int * iRow,int * jCol)4228         void fill_in_hess_nnz(int* iRow , int* jCol){
4229             size_t idx = 0, idx_all=0, idx_pair=0, vid, vjd;
4230             string vi_name, vj_name;
4231             shared_ptr<param_> vi;
4232             shared_ptr<param_> vj;
4233             shared_ptr<Constraint<type>> c;
4234             for (auto &pairs: _hess_link) {
4235                 vi_name = pairs.first.first;
4236                 vj_name = pairs.first.second;
4237                 vi = (pairs.second.begin())->second.first->get_var(vi_name);
4238                 vj = (pairs.second.begin())->second.first->get_var(vj_name);
4239                 if (vi_name.compare(vj_name) > 0) {//ONLY STORE LOWER TRIANGULAR PART OF HESSIAN
4240                     throw invalid_argument("SHOULD BE SORTED CORRECTLY IN FILL_MAPS");
4241                 }
4242                 vid = vi->get_id();
4243                 vjd = vj->get_id();
4244                 idx_pair = idx;
4245                 //        auto max_f_idx = 0;
4246                 for (auto &f_pair:pairs.second) {
4247                     //            auto f_idx = 0;
4248                     //            idx = idx_pair;
4249                     //        auto f_pair = *pairs.second.begin();
4250                     auto f = f_pair.second.first;
4251                     if (f->_is_constraint) {
4252                         c = static_pointer_cast<Constraint<type>>(f);
4253                     }
4254                     auto d2f = f_pair.second.second;
4255                     size_t nb_inst = f->get_nb_inst();
4256                     for (size_t inst = 0; inst<nb_inst; inst++) {
4257                         if (!(f->_is_constraint && *c->_all_lazy && c->_lazy[inst])) {
4258                             if(d2f->is_matrix_indexed()){
4259                                 auto dim = d2f->get_dim(inst);
4260                                 for (size_t j = 0; j<dim; j++) {
4261                                     idx_all++;
4262                                     iRow[_idx_it[idx]] = vid + vi->get_id_inst(inst,j);
4263                                     jCol[_idx_it[idx]] = vjd + vj->get_id_inst(inst,j);
4264                                     idx++;
4265                                 }
4266                             }
4267                             else  if (d2f->is_matrix()) {
4268                                 for (size_t i = 0; i < d2f->get_nb_inst(); i++) {
4269                                     for (size_t j = i; j < d2f->_dim[1]; j++) {
4270                                         idx_all++;
4271                                         iRow[_idx_it[idx]] = vid + vi->get_id_inst(i);
4272                                         jCol[_idx_it[idx]] = vjd + vj->get_id_inst(j);
4273                                         idx++;
4274                                         //                                f_idx++;
4275                                     }
4276                                 }
4277                             }
4278                             else if(d2f->_is_vector){
4279                                 //                    if (d2f->get_nb_inst() != d2f->get_nb_inst()) {
4280                                 //                        throw invalid_argument("error");
4281                                 //                    }
4282                                 for (size_t j = 0; j < d2f->get_nb_inst(); j++) {
4283                                     idx_all++;
4284                                     iRow[_idx_it[idx]] = vid + vi->get_id_inst(j);
4285                                     jCol[_idx_it[idx]] = vjd + vj->get_id_inst(j);
4286                                     idx++;
4287                                     //                            f_idx++;
4288                                 }
4289                             }
4290                             else {
4291                                 idx_all++;
4292                                 iRow[_idx_it[idx]] = vid + vi->get_id_inst(inst);
4293                                 jCol[_idx_it[idx]] = vjd + vj->get_id_inst(inst);
4294                                 idx++;
4295                                 //                        f_idx++;
4296                             }
4297                         }
4298                     }
4299                     //            if(max_f_idx < f_idx){
4300                     //                max_f_idx = f_idx;
4301                     //            }
4302                 }
4303                 //        idx = idx_pair+max_f_idx;
4304             }
4305             //            if (idx!=_nnz_h) {
4306             //                throw invalid_argument("idx!=_nnz_h");
4307             //            }
4308             _hess_vals.resize(idx_all);
4309         }
4310 
4311         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
fill_in_hess(const double * x,double obj_factor,const double * lambda,double * res,bool new_x)4312         void fill_in_hess(const double* x , double obj_factor, const double* lambda, double* res, bool new_x){
4313             size_t idx = 0, idx_in = 0, c_inst = 0, idx_pair=0;
4314             shared_ptr<Constraint<type>> c;
4315             double hess = 0;
4316             if (new_x) {
4317                 set_x(x);
4318                 compute_funcs();
4319             }
4320             for (size_t i = 0; i<_nnz_h; i++) {
4321                 res[i] = 0;
4322             }
4323             if (_first_call_hess) {
4324                 for (auto &pairs: _hess_link) {
4325                     idx_pair = idx;
4326                     for (auto &f_pair:pairs.second) {
4327                         auto f = f_pair.second.first;
4328                         if (f->_is_constraint) {
4329                             c = static_pointer_cast<Constraint<type>>(f);
4330                         }
4331                         auto d2f = f_pair.second.second;
4332                         size_t nb_inst = f->get_nb_inst();
4333                         size_t id_inst = 0;
4334                         for (size_t inst = 0; inst<nb_inst; inst++) {
4335                             if (f->_is_constraint) {
4336                                 if (!*c->_all_lazy || !c->_lazy[inst]) {
4337                                     if (c->is_nonlinear()) {
4338                                         c_inst = c->get_id_inst(id_inst++);
4339                                         if(d2f->is_matrix_indexed()){
4340                                             auto dim = d2f->get_dim(inst);
4341                                             for (size_t j = 0; j<dim; j++) {
4342                                                 hess = d2f->eval(inst,j);
4343                                                 _hess_vals[idx_in++] = hess;
4344                                                 res[_idx_it[idx++]] += lambda[c->_id + c_inst] * hess;
4345                                             }
4346                                         }
4347                                         else if (d2f->is_matrix()) {
4348                                             for (size_t i = 0; i < d2f->get_nb_inst(); i++) {
4349                                                 for (size_t j = i; j < d2f->_dim[1]; j++) {
4350                                                     hess = d2f->eval(i,j);
4351                                                     _hess_vals[idx_in++] = hess;
4352                                                     res[_idx_it[idx++]] += lambda[c->_id + c_inst] * hess;
4353                                                 }
4354                                             }
4355                                         }
4356                                         else if(d2f->_is_vector){
4357                                             for (size_t j = 0; j < d2f->get_nb_inst(); j++) {
4358                                                 if (d2f->func_is_number()) {
4359                                                     hess = d2f->_val->at(0);
4360                                                 }
4361                                                 else {
4362                                                     hess = d2f->_val->at(j);
4363                                                 }
4364                                                 _hess_vals[idx_in++] = hess;
4365                                                 res[_idx_it[idx++]] += lambda[c->_id + c_inst] * hess;
4366                                             }
4367                                         }
4368                                         else {
4369                                             if (d2f->func_is_number()) {
4370                                                 hess = d2f->_val->at(0);
4371                                             }
4372                                             else {
4373                                                 hess = d2f->_val->at(inst);
4374                                             }
4375                                             _hess_vals[idx_in++] = hess;
4376                                             res[_idx_it[idx++]] += lambda[c->_id + c_inst] * hess;
4377                                         }
4378                                     }
4379                                     else {
4380                                         if(!d2f->is_constant()){
4381                                             d2f->_evaluated=false;
4382                                         }
4383                                         c_inst = c->get_id_inst(id_inst++);
4384                                         if(d2f->is_matrix_indexed()){
4385                                             auto dim = d2f->get_dim(inst);
4386                                             for (size_t j = 0; j<dim; j++) {
4387                                                 hess = d2f->eval(inst,j);
4388                                                 _hess_vals[idx_in++] = hess;
4389                                                 res[_idx_it[idx++]] += lambda[c->_id + c_inst] * hess;
4390                                             }
4391                                         }
4392                                         else if (d2f->is_matrix()) {
4393                                             for (size_t i = 0; i < d2f->get_nb_inst(); i++) {
4394                                                 for (size_t j = i; j < d2f->_dim[1]; j++) {
4395                                                     hess = d2f->eval(i,j);
4396                                                     _hess_vals[idx_in++] = hess;
4397                                                     res[_idx_it[idx++]] += lambda[c->_id + c_inst] * hess;
4398                                                 }
4399                                             }
4400                                         }
4401                                         else if(d2f->_is_vector){
4402                                             for (size_t j = 0; j < d2f->get_nb_inst(); j++) {
4403                                                 if (d2f->func_is_number()) {
4404                                                     hess = d2f->eval(0);
4405                                                 }
4406                                                 else {
4407                                                     hess = d2f->eval(j);
4408                                                 }
4409                                                 _hess_vals[idx_in++] = hess;
4410                                                 res[_idx_it[idx++]] += lambda[c->_id + c_inst] * hess;
4411                                             }
4412                                         }
4413                                         else {
4414                                             if (d2f->func_is_number()) {
4415                                                 hess = d2f->eval(0);
4416                                             }
4417                                             else {
4418                                                 hess = d2f->eval(inst);
4419                                             }
4420                                             _hess_vals[idx_in++] = hess;
4421                                             res[_idx_it[idx++]] += lambda[c->_id + c_inst] * hess;
4422                                         }
4423                                     }
4424                                 }
4425                             }
4426                             else {
4427                                 if (d2f->is_matrix()) {
4428                                     for (size_t i = 0; i < d2f->get_nb_inst(); i++) {
4429                                         for (size_t j = i; j < d2f->_dim[1]; j++) {
4430                                             hess = d2f->eval(i,j);
4431                                             _hess_vals[idx_in++] = hess;
4432                                             res[_idx_it[idx++]] += obj_factor * hess;
4433                                         }
4434                                     }
4435                                 }
4436                                 else if(d2f->_is_vector){
4437                                     for (size_t j = 0; j < d2f->get_nb_inst(); j++) {
4438                                         if (d2f->func_is_number()) {
4439                                             hess = d2f->eval(0);
4440                                         }
4441                                         else {
4442                                             hess = d2f->eval(j);
4443                                         }
4444                                         _hess_vals[idx_in++] = hess;
4445                                         res[_idx_it[idx++]] += obj_factor * hess;
4446                                     }
4447                                 }
4448                                 else {
4449                                     hess = d2f->eval(0);
4450                                     _hess_vals[idx_in++] = hess;
4451                                     res[_idx_it[idx++]] += obj_factor * hess;
4452                                 }
4453                             }
4454                         }
4455                     }
4456                 }
4457                 _first_call_hess = false;
4458                 return;
4459             }
4460             if ((_type==lin_m || _type==quad_m)) { /* No need to recompute Hessian for quadratic models, used stored values */
4461                 size_t id_inst = 0;
4462                 for (auto &pairs: _hess_link) {
4463                     idx_pair = idx;
4464                     for (auto &f_pair:pairs.second) {
4465                         auto f = f_pair.second.first;
4466                         if (f->_is_constraint) {
4467                             c = static_pointer_cast<Constraint<type>>(f);
4468                         }
4469                         auto d2f = f_pair.second.second;
4470                         size_t nb_inst = f->get_nb_inst();
4471                         id_inst = 0;
4472                         for (size_t inst = 0; inst<nb_inst; inst++) {
4473                             if (f->_is_constraint) {
4474                                 if (!*c->_all_lazy || !c->_lazy[inst]) {
4475                                     c_inst = c->get_id_inst(id_inst++);
4476                                     if(d2f->is_matrix_indexed()){
4477                                         auto dim = d2f->get_dim(inst);
4478                                         for (size_t j = 0; j<dim; j++) {
4479                                             res[_idx_it[idx++]] += lambda[c->_id + c_inst] * _hess_vals[idx_in++];
4480                                         }
4481                                     }
4482                                     else if (d2f->is_matrix()) {
4483                                         for (size_t i = 0; i < d2f->get_nb_inst(); i++) {
4484                                             for (size_t j = i; j < d2f->_dim[1]; j++) {
4485                                                 res[_idx_it[idx++]] += lambda[c->_id + c_inst] * _hess_vals[idx_in++];
4486                                             }
4487                                         }
4488                                     }
4489                                     else if(d2f->_is_vector){
4490                                         for (size_t j = 0; j < d2f->get_nb_inst(); j++) {
4491                                             res[_idx_it[idx++]] += lambda[c->_id + c_inst] * _hess_vals[idx_in++];
4492                                         }
4493                                     }
4494                                     else {
4495                                         res[_idx_it[idx++]] += lambda[c->_id + c_inst] * _hess_vals[idx_in++];
4496                                     }
4497                                 }
4498                             }
4499                             else {
4500                                 if(d2f->_is_vector){
4501                                     for (size_t j = 0; j < d2f->get_nb_inst(); j++) {
4502                                         res[_idx_it[idx++]] += obj_factor * _hess_vals[idx_in++];
4503                                     }
4504                                 }
4505                                 else {
4506                                     res[_idx_it[idx++]] += obj_factor * _hess_vals[idx_in++];
4507                                 }
4508                             }
4509                         }
4510                     }
4511                 }
4512                 return;
4513             }
4514             for (auto &pairs: _hess_link) {
4515                 idx_pair = idx;
4516                 for (auto &f_pair:pairs.second) {
4517                     auto f = f_pair.second.first;
4518                     if (f->_is_constraint) {
4519                         c = static_pointer_cast<Constraint<type>>(f);
4520                     }
4521                     auto d2f = f_pair.second.second;
4522                     if(!d2f->is_constant()){
4523                         d2f->_evaluated=false;
4524                     }
4525                     size_t nb_inst = f->get_nb_inst();
4526                     size_t id_inst = 0;
4527                     for (size_t inst = 0; inst<nb_inst; inst++) {
4528                         if (f->_is_constraint) {
4529                             if (!*c->_all_lazy || !c->_lazy[inst]) {
4530                                 c_inst = c->get_id_inst(id_inst++);
4531                                 if (c->is_quadratic()) {
4532                                     if(d2f->is_matrix_indexed()){
4533                                         auto dim = d2f->get_dim(inst);
4534                                         for (size_t j = 0; j<dim; j++) {
4535                                             res[_idx_it[idx++]] += lambda[c->_id + c_inst] * _hess_vals[idx_in++];
4536                                         }
4537                                     }
4538                                     else if (d2f->is_matrix()) {
4539                                         for (size_t i = 0; i < d2f->get_nb_inst(); i++) {
4540                                             for (size_t j = i; j < d2f->_dim[1]; j++) {
4541                                                 res[_idx_it[idx++]] += lambda[c->_id + c_inst] * _hess_vals[idx_in++];
4542                                             }
4543                                         }
4544                                     }
4545                                     else if(d2f->_is_vector){
4546                                         for (size_t j = 0; j < d2f->get_nb_inst(); j++) {
4547                                             res[_idx_it[idx++]] += lambda[c->_id + c_inst] * _hess_vals[idx_in++];
4548                                         }
4549                                     }
4550                                     else {
4551                                         res[_idx_it[idx++]] += lambda[c->_id + c_inst] * _hess_vals[idx_in++];
4552                                     }
4553                                 }
4554                                 else if(d2f->is_matrix_indexed()){
4555                                     auto dim = d2f->get_dim(inst);
4556                                     for (size_t j = 0; j<dim; j++) {
4557                                         hess = d2f->eval(inst,j);
4558                                         _hess_vals[idx_in++] = hess;
4559                                         res[_idx_it[idx++]] += lambda[c->_id + c_inst] * hess;
4560                                     }
4561                                 }
4562                                 else if (d2f->is_matrix()) {
4563                                     for (size_t i = 0; i < d2f->get_nb_inst(); i++) {
4564                                         for (size_t j = i; j < d2f->_dim[1]; j++) {
4565                                             hess = d2f->eval(i,j);
4566                                             _hess_vals[idx_in++] = hess;
4567                                             res[_idx_it[idx++]] += lambda[c->_id + c_inst] * hess;
4568                                         }
4569                                     }
4570                                 }
4571                                 else if(d2f->_is_vector){
4572                                     for (size_t j = 0; j < d2f->get_nb_inst(); j++) {
4573                                         if (d2f->func_is_number()) {
4574                                             hess = d2f->_val->at(0);
4575                                         }
4576                                         else {
4577                                             hess = d2f->_val->at(j);
4578                                         }
4579                                         _hess_vals[idx_in++] = hess;
4580                                         res[_idx_it[idx++]] += lambda[c->_id + c_inst] * hess;
4581                                     }
4582                                 }
4583                                 else {
4584                                     if (d2f->func_is_number()) {
4585                                         hess = d2f->eval(0);
4586                                     }
4587                                     else {
4588                                         if (c->is_nonlinear()) {
4589                                             hess = d2f->_val->at(inst);
4590                                         }
4591                                         else {
4592                                             hess = d2f->eval(inst);
4593                                         }
4594                                     }
4595                                     _hess_vals[idx_in++] = hess;
4596                                     res[_idx_it[idx++]] += lambda[c->_id + c_inst] * hess;
4597                                 }
4598                             }
4599                         }
4600                         else {
4601                             if (_obj->is_quadratic()) {
4602                                 if(d2f->_is_vector){
4603                                     for (size_t j = 0; j < d2f->get_nb_inst(); j++) {
4604                                         res[_idx_it[idx++]] += obj_factor * _hess_vals[idx_in++];
4605                                     }
4606                                 }
4607                                 else {
4608                                     res[_idx_it[idx++]] += obj_factor * _hess_vals[idx_in++];
4609                                 }
4610                             }
4611                             else if(d2f->_is_vector){
4612                                 for (size_t j = 0; j < d2f->get_nb_inst(); j++) {
4613                                     hess = d2f->eval(j);
4614                                     _hess_vals[idx_in++] = hess;
4615                                     res[_idx_it[idx++]] += obj_factor * hess;
4616                                 }
4617                             }
4618                             else {
4619                                 hess = d2f->eval(0);
4620                                 _hess_vals[idx_in++] = hess;
4621                                 res[_idx_it[idx++]] += obj_factor * hess;
4622                             }
4623                         }
4624                     }
4625 
4626 
4627 
4628                 }
4629 
4630             }
4631         }
4632 
4633 
initialize_zero()4634         void initialize_zero(){
4635             for (auto &vp: _vars) {
4636                 vp.second->initialize_zero();
4637             }
4638         }
4639 
4640         /** Initialize all variables using midpoint in bounds
4641         */
initialize_midpoint()4642         void initialize_midpoint(){
4643             for (auto &vp: _vars) {
4644                 vp.second->initialize_midpoint();
4645             }
4646         };
4647 
copy_bounds(const shared_ptr<Model<type>> & relaxation)4648         void copy_bounds(const shared_ptr<Model<type>>& relaxation){
4649             for (auto &vpr: relaxation->_vars) {
4650                 auto it = _vars.find(vpr.first);
4651                 if (it != _vars.end()){
4652                     it->second->copy_bounds(vpr.second);
4653                 }
4654             }
4655         }
4656 
copy_solution(const shared_ptr<Model<type>> & relaxation)4657         void copy_solution(const shared_ptr<Model<type>>& relaxation){
4658             for (auto &vpr: relaxation->_vars) {
4659                 auto it = _vars.find(vpr.first);
4660                 if (it != _vars.end()){
4661                     it->second->copy_vals(vpr.second);
4662                 }
4663             }
4664         }
4665 
4666         /** Initialize all variables using random value in bounds drawn from a uniform distribution
4667         */
initialize_uniform()4668         void initialize_uniform(){
4669             for (auto &vp: _vars) {
4670                 vp.second->initialize_uniform();
4671             }
4672         }
4673 
4674 
fill_in_maps()4675         void fill_in_maps() {/*< Fill the _hess and _v_in_ maps to link variables with their constraints and compute the Jacobian & Hessian matrices */
4676             string vi_name, vj_name;
4677             param_* vi;
4678             param_* vj;
4679             _built = true;
4680             if (_obj->_new) {
4681                 _obj->allocate_mem();
4682                 _obj->compute_derivatives();
4683                 for (auto &df_p:*_obj->get_dfdx()) {
4684                     auto df = static_pointer_cast<func<type>>(df_p.second);
4685                     DebugOff(df->to_str() << endl);
4686                     for (auto &df2_p:*df_p.second->get_dfdx()) {
4687                         if (df2_p.second->get_expr() || _type==nlin_m) {
4688                             df2_p.second = embed(df2_p.second);
4689                         }
4690                     }
4691                     if (df->get_expr() || _type==nlin_m) {
4692                         df_p.second = embed(df);
4693                     }
4694                     else {
4695                         embed(df);
4696                     }
4697                 }
4698                 if (!_obj->is_linear()) {
4699                     for (auto &vi_p: _obj->get_vars()) {
4700                         vi = vi_p.second.first.get();
4701                         vi_name = vi_p.first;
4702                         //            vid = vi->get_id();
4703                         auto df = _obj->get_stored_derivative(vi->_name);
4704                         for (auto &vj_p: df->get_vars()) {
4705                             vj = vj_p.second.first.get();
4706                             vj_name = vj_p.first;
4707                             //                vjd = vj->get_id();
4708                             if (vi_name.compare(vj_name) < 0) {//ONLY STORE LOWER TRIANGULAR PART OF HESSIAN
4709                                 _hess_link[make_pair<>(vi_name,vj_name)][-1] = (make_pair<>(_obj,_obj->get_stored_derivative(vi->_name)->get_stored_derivative(vj->_name)));
4710                             }
4711                             else {
4712                                 _hess_link[make_pair<>(vj_name,vi_name)][-1] = (make_pair<>(_obj,_obj->get_stored_derivative(vj->_name)->get_stored_derivative(vi->_name)));
4713                             }
4714                             //                for (int inst = 0; inst<vi->get_dim(); inst++) {
4715                             //                    vid = vi->get_id();
4716                             //                    vid_inst = vid + vi->get_id_inst(inst);
4717                             //                    vjd = vj->get_id();
4718                             //                    vjd_inst = vjd + vj->get_id_inst(inst);
4719                             //                    _hess.insert(make_pair<>(vid_inst, vjd_inst));
4720                             //                }
4721                         }
4722                     }
4723                 }
4724             }
4725             shared_ptr<Constraint<type>> c = nullptr;
4726             for(auto& c_p :_cons)
4727             {
4728                 c = c_p.second;
4729 //                        c->print();
4730                 if (c->_new) {
4731                     c->compute_derivatives();
4732                     if (_type==nlin_m) {
4733                         for (auto &df_p:*c->get_dfdx()) {
4734                             auto df = static_pointer_cast<func<type>>(df_p.second);
4735                             DebugOff(df->to_str() << endl);
4736                             for (auto &df2_p:*df_p.second->get_dfdx()) {
4737                                 if (df2_p.second->get_expr() || _type==nlin_m) {
4738                                     df2_p.second = embed(df2_p.second);
4739                                 }
4740                             }
4741                             if (df->get_expr() || _type==nlin_m) {
4742                                 df_p.second = embed(df);
4743                             }
4744                             else {
4745                                 embed(df);
4746                             }
4747                         }
4748                     }
4749                     if (!c->is_linear()) {
4750 
4751                         for (auto &vi_p: c->get_vars()) {
4752                             vi = vi_p.second.first.get();
4753                             vi_name = vi_p.first;
4754                             auto df = c->get_stored_derivative(vi->_name);
4755                             for (auto &vj_p: df->get_vars()) {
4756                                 vj = vj_p.second.first.get();
4757                                 vj_name = vj_p.first;
4758                                 if (vi_name.compare(vj_name) <= 0) {//ONLY STORE LOWER TRIANGULAR PART OF HESSIAN
4759                                     _hess_link[make_pair<>(vi_name,vj_name)][c->_id] = (make_pair<>(c, c->get_stored_derivative(vi->_name)->get_stored_derivative(vj->_name)));
4760                                 }
4761                                 else {
4762                                     _hess_link[make_pair<>(vj_name,vi_name)][c->_id] = (make_pair<>(c, c->get_stored_derivative(vj->_name)->get_stored_derivative(vi->_name)));
4763                                 }
4764                             }
4765                         }
4766                     }
4767                     DebugOff(c->to_str() << endl);
4768                 }
4769             }
4770             //                print_nl_functions();
4771         }
4772 
4773 
fill_in_duals(double * lambda,double * z_L,double * z_U)4774         void fill_in_duals(double* lambda, double* z_L, double* z_U){
4775             for (auto &cp: _cons) {
4776 //                if(cp.second->_new){
4777 //                    continue;
4778 //                }
4779                 size_t idx = 0;
4780                 //        for (size_t inst = 0; inst < cp.second->get_nb_inst(); inst++) {
4781                 //            if (!*cp.second->_all_lazy || !cp.second->_lazy[inst]) {
4782                 //                lambda[cp.second->_id + idx++] = 0;
4783                 //            }
4784                 //
4785                 //        }
4786 //                idx = 0;
4787                 auto nb_ins = cp.second->get_nb_inst();
4788                 for (size_t inst = 0; inst < nb_ins; inst++) {
4789                     if (!*cp.second->_all_lazy || !cp.second->_lazy[inst]) {
4790                         auto index = cp.second->_id + idx++;
4791                         lambda[index] = cp.second->_dual[inst];
4792                     }
4793                     //            else
4794                     //            {
4795                     //                lambda[index] = 100;
4796                     //            }
4797                 }
4798             }
4799             for (auto &vp: _vars) {
4800 //                if(vp.second->_new){
4801 //                    continue;
4802 //                }
4803                 auto nb_inst = vp.second->get_dim();
4804                 auto vid = vp.second->get_id();
4805                 for (size_t inst = 0; inst < nb_inst; inst++) {
4806                     auto id_inst = vp.second->get_id_inst(inst);
4807                     z_L[vid + id_inst] = vp.second->_l_dual[inst];
4808                     z_U[vid + id_inst] = vp.second->_u_dual[inst];
4809                 }
4810             }
4811 
4812         }
4813         /**
4814          Initialize x with model variables values
4815          @param[out] x values to initialize
4816          */
4817         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
fill_in_var_init(double * x)4818         void fill_in_var_init(double* x) {
4819             for(auto& v_p: _vars){
4820                 v_p.second->get_double_val(x);
4821             }
4822         }
4823 
4824         /**
4825          Initialize x with model variables values
4826          @param[out] x values to initialize
4827          */
4828         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
get_solution(vector<double> & x)4829         void get_solution(vector<double>& x) const{
4830             if(x.size()!=get_nb_vars()){
4831                 x.resize(_nb_vars);
4832             }
4833             for(auto& v_p: _vars){
4834                 v_p.second->get_solution(x);
4835             }
4836         }
4837 
recompute_var_bounds()4838         void recompute_var_bounds(){
4839             for(auto &v_p: _vars)
4840             {
4841                 v_p.second->reset_bounds();
4842             }
4843         }
reset()4844         void reset() {
4845             _built = false; /**< Indicates if this model has been already built. */
4846             _first_run = true; /**< Indicates if a solver was ran on this model. */
4847 
4848             for(auto& c_p :_cons)
4849             {
4850 //                c_p.second->uneval();
4851                 c_p.second->_new = true;
4852                 c_p.second->_dfdx->clear();
4853             }
4854             _obj->_new = true;
4855             _obj->_dfdx->clear();
4856 //            for(auto &v_p: _vars)
4857 //            {
4858 //                v_p.second->reset_bounds();
4859 //            }
4860         }
4861 
4862         /* Build the interaction graph with symbolic variables as nodes, an edge links two variables if they appear together in a constraint or if they are linked in a nonlinear fashion in the objective
4863             @param[degree] degree of the relaxation, if degree = 2 and the model is quadratic, do not account for linear relationships.
4864          */
4865         Net get_symbolic_interaction_graph(int degree=2) const{
4866             if(this->_type==nlin_m){
4867                 throw invalid_argument("get_symbolic_interaction_graph currently only supports polynomial, quadratic and linear models");
4868             }
4869             Net g;
4870             Node* n1 = nullptr;
4871             Node* n2 = nullptr;
4872             for(auto& c_p :_cons) {
4873                 auto c = c_p.second;
4874                 if(this->_type==quad_m && degree==2){ /* In the quadratic case, the degree 2 relaxation, no need to account for linear relationships */
4875                     for (auto &pair:*c->_qterms) {
4876                         auto coef = pair.second._coef;
4877                         auto p1 = pair.second._p->first;
4878                         auto p2 = pair.second._p->second;
4879                         auto p1_name = p1->get_name(false,false);
4880                         auto p2_name = p2->get_name(false,false);
4881                         n1 = g.get_node(p1_name);
4882                         if(n1==nullptr){
4883                             n1 = new Node(p1_name);
4884                             g.add_node(n1);
4885                         }
4886                         n2 = g.get_node(p2_name);
4887                         if(n2==nullptr){
4888                             n2 = new Node(p2_name);
4889                             g.add_node(n2);
4890                         }
4891                         if(g.get_arc(n1, n2)==nullptr){
4892                             auto a = new Arc(n1,n2);
4893                             g.add_arc(a);
4894                             a->connect();
4895 
4896                         }
4897                     }
4898                 }
4899                 else { /* All variables appearing in the constraint are linked in the interaction graph */
4900                     for(auto it = c->_vars->begin(); it != c->_vars->end(); it++) {
4901                         n1 = g.get_node(it->first);
4902                         if(n1==nullptr){
4903                             n1 = new Node(it->first);
4904                             g.add_node(n1);
4905                         }
4906                         if(it->second.first->_is_vector){/* There's a sum linking all instances of this variable together */
4907                             if(g.get_arc(n1, n1)==nullptr){
4908                                 auto a = new Arc(n1,n1);
4909                                 g.add_arc(a);
4910                                 a->connect();
4911                             }
4912                         }
4913                         for(auto it2 = next(it); it2 != c->_vars->end(); it2++) {
4914                             n2 = g.get_node(it2->first);
4915                             if(n2==nullptr){
4916                                 n2 = new Node(it2->first);
4917                                 g.add_node(n2);
4918                             }
4919                             if(g.get_arc(n1, n2)==nullptr){
4920                                 auto a = new Arc(n1,n2);
4921                                 g.add_arc(a);
4922                                 a->connect();
4923                             }
4924                         }
4925                     }
4926                 }
4927             }
4928             if(!_obj->is_constant()){
4929                 for (auto &pair:*_obj->_qterms) {
4930                     auto coef = pair.second._coef;
4931                     auto p1 = pair.second._p->first;
4932                     auto p2 = pair.second._p->second;
4933                     auto p1_name = p1->get_name(false,false);
4934                     auto p2_name = p1->get_name(false,false);
4935                     n1 = g.get_node(p1_name);
4936                     if(n1==nullptr){
4937                         n1 = new Node(p1_name);
4938                         g.add_node(n1);
4939                     }
4940                     n2 = g.get_node(p2_name);
4941                     if(n2==nullptr){
4942                         n2 = new Node(p2_name);
4943                         g.add_node(n2);
4944                     }
4945                     if(g.get_arc(n1, n2)==nullptr){
4946                         auto a = new Arc(n1,n2);
4947                         g.add_arc(a);
4948                         a->connect();
4949                     }
4950                     assert(p1->_is_vector == p2->_is_vector); /* They're both vectors or they're both not */
4951                     if(p1->_is_vector){
4952                         if(g.get_arc(n1, n1)==nullptr){
4953                             auto a = new Arc(n1,n1);
4954                             g.add_arc(a);
4955                             a->connect();
4956                         }
4957                         if(g.get_arc(n2, n2)==nullptr){
4958                             auto a = new Arc(n2,n2);
4959                             g.add_arc(a);
4960                             a->connect();
4961                         }
4962                     }
4963                 }
4964                 for (auto &pair:*_obj->_pterms) {
4965                     for(auto it = pair.second._l->begin(); it != pair.second._l->end(); it++) {
4966                         auto n1_name = it->first->get_name(false,false);
4967                         n1 = g.get_node(n1_name);
4968                         if(n1==nullptr){
4969                             n1 = new Node(n1_name);
4970                             g.add_node(n1);
4971                         }
4972                         for(auto it2 = next(it); it2 != pair.second._l->end(); it2++) {
4973                             auto n2_name = it2->first->get_name(false,false);
4974                             n2 = g.get_node(n2_name);
4975                             if(n2==nullptr){
4976                                 n2 = new Node(n2_name);
4977                                 g.add_node(n2);
4978                             }
4979                             if(g.get_arc(n1, n2)==nullptr){
4980                                 auto a = new Arc(n1,n2);
4981                                 g.add_arc(a);
4982                                 a->connect();
4983                             }
4984                         }
4985                     }
4986                 }
4987             }
4988             return g;
4989         }
4990 
4991         /* Build the interaction graph with all indexed variables as nodes, an edge links two variables if they appear together in a constraint or if they are linked in a nonlinear fashion in the objective
4992            @param[degree] degree of the relaxation, if degree = 2 and the model is quadratic, do not account for linear relationships.
4993         */
4994         Net get_interaction_graph(int degree=2) const{
4995             if(this->_type==nlin_m){
4996                 throw invalid_argument("get_interaction_graph currently only supports polynomial, quadratic and linear models");
4997             }
4998             Net g;
4999             Node* n1 = nullptr;
5000             Node* n2 = nullptr;
5001             for(auto& c_p :_cons) {
5002                 auto c = c_p.second;
5003                 if(this->_type<=quad_m && degree==2){ /* In the quadratic case, the degree 2 relaxation, no need to account for linear relationships */
5004                     auto nb_inst = c->get_nb_inst();
5005                     for(int inst = 0; inst<nb_inst; inst++){
5006                         for (auto &pair:*c->_qterms) {
5007                             auto coef = pair.second._coef;
5008                             auto p1 = pair.second._p->first;
5009                             auto p2 = pair.second._p->second;
5010                             if(p1->is_matrix_indexed()){
5011                                 auto dim = p1->get_dim(inst);
5012                                 for (size_t j = 0; j<dim; j++) {
5013                                     auto p1_name = p1->get_name(inst,j);
5014                                     auto p2_name = p2->get_name(inst,j);
5015                                     n1 = g.get_node(p1_name);
5016                                     if(n1==nullptr){
5017                                         n1 = new Node(p1_name);
5018                                         g.add_node(n1);
5019                                     }
5020                                     n2 = g.get_node(p2_name);
5021                                     if(n2==nullptr){
5022                                         n2 = new Node(p2_name);
5023                                         g.add_node(n2);
5024                                     }
5025                                     if(n1!=n2 && g.get_arc(n1, n2)==nullptr){
5026                                         auto a = new Arc(n1,n2);
5027                                         g.add_arc(a);
5028                                         a->connect();
5029                                     }
5030                                 }
5031                             }
5032                             else {
5033                                 auto p1_name = p1->get_name(inst);
5034                                 auto p2_name = p2->get_name(inst);
5035                                 n1 = g.get_node(p1_name);
5036                                 if(n1==nullptr){
5037                                     n1 = new Node(p1_name);
5038                                     g.add_node(n1);
5039                                 }
5040                                 n2 = g.get_node(p2_name);
5041                                 if(n2==nullptr){
5042                                     n2 = new Node(p2_name);
5043                                     g.add_node(n2);
5044                                 }
5045                                 if(n1!=n2 && g.get_arc(n1, n2)==nullptr){
5046                                     auto a = new Arc(n1,n2);
5047                                     g.add_arc(a);
5048                                     a->connect();
5049                                 }
5050                             }
5051                         }
5052                     }
5053                 }
5054                 else { /* All variables appearing in the constraint are linked in the interaction graph */
5055                     auto nb_inst = c->get_nb_inst();
5056                     for(int inst = 0; inst<nb_inst; inst++){
5057                         for(auto it = c->_vars->begin(); it != c->_vars->end(); it++) {
5058                             auto p1 = it->second.first;
5059                             for(auto it2 = next(it); it2 != c->_vars->end(); it2++) {
5060                                 auto p2 = it2->second.first;
5061                                 if(p1->is_matrix_indexed()){
5062                                     auto dim = std::min(p1->get_dim(inst),p2->get_dim(inst));
5063                                     for (size_t j = 0; j<dim; j++) {
5064                                         auto p1_name = p1->get_name(inst,j);
5065                                         auto p2_name = p2->get_name(inst,j);
5066                                         n1 = g.get_node(p1_name);
5067                                         if(n1==nullptr){
5068                                             n1 = new Node(p1_name);
5069                                             g.add_node(n1);
5070                                         }
5071                                         n2 = g.get_node(p2_name);
5072                                         if(n2==nullptr){
5073                                             n2 = new Node(p2_name);
5074                                             g.add_node(n2);
5075                                         }
5076                                         if(n1!=n2 && g.get_arc(n1, n2)==nullptr){
5077                                             auto a = new Arc(n1,n2);
5078                                             g.add_arc(a);
5079                                             a->connect();
5080                                         }
5081                                     }
5082                                 }
5083                                 else {
5084                                     auto p1_name = p1->get_name(inst);
5085                                     auto p2_name = p2->get_name(inst);
5086                                     n1 = g.get_node(p1_name);
5087                                     if(n1==nullptr){
5088                                         n1 = new Node(p1_name);
5089                                         g.add_node(n1);
5090                                     }
5091                                     n2 = g.get_node(p2_name);
5092                                     if(n2==nullptr){
5093                                         n2 = new Node(p2_name);
5094                                         g.add_node(n2);
5095                                     }
5096                                     if(n1!=n2 && g.get_arc(n1, n2)==nullptr){
5097                                         auto a = new Arc(n1,n2);
5098                                         g.add_arc(a);
5099                                         a->connect();
5100                                     }
5101                                 }
5102                             }
5103                         }
5104                     }
5105                 }
5106             }
5107             if(!_obj->is_constant()){
5108                 for (auto &pair:*_obj->_qterms) {
5109                     auto coef = pair.second._coef;
5110                     auto p1 = pair.second._p->first;
5111                     auto p2 = pair.second._p->second;
5112                     if(p1->is_matrix_indexed()){
5113                         for (size_t inst = 0; inst<p1->get_dim(0); inst++) {
5114                             auto dim = p1->get_dim(inst);
5115                             for (size_t j = 0; j<dim; j++) {
5116                                 auto p1_name = p1->get_name(inst,j);
5117                                 auto p2_name = p2->get_name(inst,j);
5118                                 n1 = g.get_node(p1_name);
5119                                 if(n1==nullptr){
5120                                     n1 = new Node(p1_name);
5121                                     g.add_node(n1);
5122                                 }
5123                                 n2 = g.get_node(p2_name);
5124                                 if(n2==nullptr){
5125                                     n2 = new Node(p2_name);
5126                                     g.add_node(n2);
5127                                 }
5128                                 if(n1!=n2 && g.get_arc(n1, n2)==nullptr){
5129                                     auto a = new Arc(n1,n2);
5130                                     g.add_arc(a);
5131                                     a->connect();
5132                                 }
5133                             }
5134                         }
5135                     }
5136                     else if(p1->_is_vector){
5137                         for (size_t inst = 0; inst<p1->get_dim(0); inst++) {
5138                             auto p1_name = p1->get_name(inst);
5139                             auto p2_name = p2->get_name(inst);
5140                             n1 = g.get_node(p1_name);
5141                             if(n1==nullptr){
5142                                 n1 = new Node(p1_name);
5143                                 g.add_node(n1);
5144                             }
5145                             n2 = g.get_node(p2_name);
5146                             if(n2==nullptr){
5147                                 n2 = new Node(p2_name);
5148                                 g.add_node(n2);
5149                             }
5150                             if(n1!=n2 && g.get_arc(n1, n2)==nullptr){
5151                                 auto a = new Arc(n1,n2);
5152                                 g.add_arc(a);
5153                                 a->connect();
5154                             }
5155                         }
5156                     }
5157                     else {
5158                         auto p1_name = p1->get_name(false,false);
5159                         auto p2_name = p2->get_name(false,false);
5160                         n1 = g.get_node(p1_name);
5161                         if(n1==nullptr){
5162                             n1 = new Node(p1_name);
5163                             g.add_node(n1);
5164                         }
5165                         n2 = g.get_node(p2_name);
5166                         if(n2==nullptr){
5167                             n2 = new Node(p2_name);
5168                             g.add_node(n2);
5169                         }
5170                         if(n1!=n2 && g.get_arc(n1, n2)==nullptr){
5171                             auto a = new Arc(n1,n2);
5172                             g.add_arc(a);
5173                             a->connect();
5174                         }
5175                     }
5176                 }
5177                 for (auto &pair:*_obj->_pterms) {
5178                     for(auto it = pair.second._l->begin(); it != pair.second._l->end(); it++) {
5179                         auto n1_name = it->first->get_name(false,false);
5180                         n1 = g.get_node(n1_name);
5181                         if(n1==nullptr){
5182                             n1 = new Node(n1_name);
5183                             g.add_node(n1);
5184                         }
5185                         for(auto it2 = next(it); it2 != pair.second._l->end(); it2++) {
5186                             auto n2_name = it2->first->get_name(false,false);
5187                             n2 = g.get_node(n2_name);
5188                             if(n2==nullptr){
5189                                 n2 = new Node(n2_name);
5190                                 g.add_node(n2);
5191                             }
5192                             if(n1!=n2 && g.get_arc(n1, n2)==nullptr){
5193                                 auto a = new Arc(n1,n2);
5194                                 g.add_arc(a);
5195                                 a->connect();
5196                             }
5197                         }
5198                     }
5199                 }
5200             }
5201             return g;
5202         }
5203 
reset_lifted_vars_bounds()5204         void reset_lifted_vars_bounds() {
5205             for(auto &v_p: _vars)
5206             {
5207                 if(v_p.second->is_lifted())
5208                     v_p.second->reset_bounds();
5209             }
5210         }
5211 
reset_constrs()5212         void reset_constrs() {
5213             for(auto& c_p :_cons)
5214             {
5215                 c_p.second->uneval();
5216             }
5217             _obj->uneval();
5218 //            for(auto &v_p: _vars)
5219 //            {
5220 //                if(v_p.second->is_lifted())
5221 //                    v_p.second->reset_bounds();
5222 //            }
5223         }
5224 
fill_in_cstr_bounds(double * g_l,double * g_u)5225         void fill_in_cstr_bounds(double* g_l ,double* g_u) {
5226             size_t cid = 0;
5227             Constraint<type>* c = NULL;
5228             for(auto& c_p :_cons)
5229             {
5230                 c = c_p.second.get();
5231                 switch (c->get_ctype()) {
5232                     case eq:{
5233                         auto nb_ins = c->get_nb_inst();
5234                         size_t idx= 0;
5235                         for (size_t i = 0; i< nb_ins; i++){
5236                             if (!*c->_all_lazy || !c->_lazy[i]) {
5237                                 cid = c->_id+idx++;
5238                                 g_l[cid] = 0;
5239                                 g_u[cid] = 0;
5240                             }
5241                         }
5242                         break;
5243                     }
5244                     case leq:{
5245                         auto nb_ins = c->get_nb_inst();
5246                         size_t idx= 0;
5247                         for (size_t i = 0; i< nb_ins; i++){
5248                             if (!*c->_all_lazy || !c->_lazy[i]) {
5249                                 cid = c->_id+idx++;
5250                                 g_l[cid] = numeric_limits<double>::lowest();
5251                                 g_u[cid] = 0;
5252                             }
5253                         }
5254                         break;
5255                     }
5256                     case geq:{
5257                         auto nb_ins = c->get_nb_inst();
5258                         size_t idx= 0;
5259                         for (size_t i = 0; i< nb_ins; i++){
5260                             if (!*c->_all_lazy || !c->_lazy[i]) {
5261                                 cid = c->_id+idx++;
5262                                 g_l[cid] = 0;
5263                                 g_u[cid] = numeric_limits<double>::max();
5264                             }
5265                         }
5266                         break;
5267                     }
5268                     default:
5269                         throw invalid_argument("Undefined constraint type!\n");
5270                         exit(-1);
5271                         break;
5272                 }
5273             }
5274         }
5275 
5276 
5277 
embed(expr<type> & e)5278         void embed(expr<type>& e){/**<  Transfer all variables and parameters to the model. */
5279             switch (e.get_type()) {
5280                 case uexp_c:{
5281                     auto ue = (uexpr<type>*)&e;
5282                     if (ue->_son->is_function()) {
5283                         auto f = static_pointer_cast<func<type>>(ue->_son);
5284                         bool found_cpy = false;
5285                         auto name = f->to_str();
5286                         if (name.back()=='T') {
5287                             name = name.substr(0,name.size()-2);
5288                             if (_nl_funcs_map.count(name)>0) {
5289                                 auto cpy = _nl_funcs_map.at(name);
5290                                 f->_val = cpy->_val;
5291                                 f->_evaluated = true;
5292                                 found_cpy = true;
5293                             }
5294                         }
5295                         else {
5296                             auto name1 = "[["+name+"]]\u1D40";
5297                             if (_nl_funcs_map.count(name1)>0) {
5298                                 auto cpy = _nl_funcs_map.at(name1);
5299                                 f->_val = cpy->_val;
5300                                 f->_evaluated = true;
5301                                 found_cpy = true;
5302                             }
5303                             auto name2 = name+"\u1D40";
5304                             if (_nl_funcs_map.count(name2)>0) {
5305                                 auto cpy = _nl_funcs_map.at(name2);
5306                                 f->_val = cpy->_val;
5307                                 f->_evaluated = true;
5308                                 found_cpy = true;
5309                             }
5310                         }
5311                         if (!found_cpy) {
5312                             auto f_p = _nl_funcs_map.insert(make_pair<>(f->to_str(), f));
5313                             if (f_p.second) {
5314                                 embed(f);
5315                                 _nl_funcs.push_back(f);
5316                                 DebugOff(f->to_str() << endl);
5317                                 //                f->_val = make_shared<vector<double>>();
5318                                 //                f->_val->resize(f->get_nb_inst());
5319                             }
5320                             else {
5321                                 if(f->_dfdx->size()>0) {
5322                                     f_p.first->second->_dfdx = f->_dfdx;
5323                                 }
5324                                 ue->_son = f_p.first->second;
5325                             }
5326                         }
5327                     }
5328                     break;
5329                 }
5330                 case bexp_c:{
5331                     auto be = (bexpr<type>*)&e;
5332                     if (be->_lson->is_function()) {
5333                         auto f = static_pointer_cast<func<type>>(be->_lson);
5334                         bool found_cpy = false;
5335                         auto name = f->to_str();
5336                         if (name.back()=='T') {
5337                             name = name.substr(0,name.size()-2);
5338                             if (_nl_funcs_map.count(name)>0) {
5339                                 auto cpy = _nl_funcs_map.at(name);
5340                                 f->_val = cpy->_val;
5341                                 f->_evaluated = true;
5342                                 found_cpy = true;
5343                             }
5344                         }
5345                         else {
5346                             auto name1 = "[["+name+"]]\u1D40";
5347                             if (_nl_funcs_map.count(name1)>0) {
5348                                 auto cpy = _nl_funcs_map.at(name1);
5349                                 f->_val = cpy->_val;
5350                                 f->_evaluated = true;
5351                                 found_cpy = true;
5352                             }
5353                             auto name2 = name+"\u1D40";
5354                             if (_nl_funcs_map.count(name2)>0) {
5355                                 auto cpy = _nl_funcs_map.at(name2);
5356                                 f->_val = cpy->_val;
5357                                 f->_evaluated = true;
5358                                 found_cpy = true;
5359                             }
5360                         }
5361                         if (!found_cpy) {
5362 
5363                             auto f_p = _nl_funcs_map.insert(make_pair<>(f->to_str(), f));
5364                             if (f_p.second) {
5365                                 embed(f);
5366                                 DebugOff(f->to_str() << endl);
5367                                 _nl_funcs.push_back(f);
5368                                 //                f->_val = make_shared<vector<double>>();
5369                                 //                f->_val->resize(f->get_nb_inst());
5370                             }
5371                             else {
5372                                 if(f->_dfdx->size()>0) {
5373                                     f_p.first->second->_dfdx = f->_dfdx;
5374                                 }
5375                                 be->_lson = f_p.first->second;
5376                             }
5377                         }
5378                     }
5379                     if (be->_rson->is_function()) {
5380                         auto f = static_pointer_cast<func<type>>(be->_rson);
5381                         auto found_cpy = false;
5382                         auto name = f->to_str();
5383                         if (name.back()=='T') {
5384                             name = name.substr(0,name.size()-2);
5385                             if (_nl_funcs_map.count(name)>0) {
5386                                 auto cpy = _nl_funcs_map.at(name);
5387                                 f->_val = cpy->_val;
5388                                 f->_evaluated = true;
5389                                 found_cpy = true;
5390                             }
5391                         }
5392                         else {
5393                             auto name1 = "[["+name+"]]\u1D40";
5394                             if (_nl_funcs_map.count(name1)>0) {
5395                                 auto cpy = _nl_funcs_map.at(name1);
5396                                 f->_val = cpy->_val;
5397                                 f->_evaluated = true;
5398                                 found_cpy = true;
5399                             }
5400                             auto name2 = name+"\u1D40";
5401                             if (_nl_funcs_map.count(name2)>0) {
5402                                 auto cpy = _nl_funcs_map.at(name2);
5403                                 f->_val = cpy->_val;
5404                                 f->_evaluated = true;
5405                                 found_cpy = true;
5406                             }
5407                         }
5408                         if (!found_cpy) {
5409 
5410                             auto f_p = _nl_funcs_map.insert(make_pair<>(f->to_str(), f));
5411                             if (f_p.second) {
5412                                 embed(f);
5413                                 DebugOff(f->to_str() << endl);
5414                                 _nl_funcs.push_back(f);
5415                                 //                f->_val = make_shared<vector<double>>();
5416                                 //                f->_val->resize(f->get_nb_inst());
5417                             }
5418                             else {
5419                                 if(f->_dfdx->size()>0) {
5420                                     f_p.first->second->_dfdx = f->_dfdx;
5421                                 }
5422                                 be->_rson = f_p.first->second;
5423                             }
5424                         }
5425                     }
5426                     break;
5427                 }
5428                 default:
5429                     break;
5430             }
5431         }
5432 
5433 
5434         shared_ptr<func<type>> embed(const shared_ptr<func<type>>& f, bool insert_in_map = true){/**<  Transfer all variables and parameters to the model. */
5435             f->allocate_mem();
5436             merge_vars(f);
5437             //            return f;
5438             DebugOff(f->to_str() << endl);
5439             for (auto &p_t: f->get_lterms()) {
5440                 if (p_t.second._coef->is_function() && !p_t.second._coef->func_is_number()) {
5441                     auto cf = static_pointer_cast<func<type>>(p_t.second._coef);
5442                     auto exp = cf->get_expr();
5443                     if (exp) {
5444                         embed(*exp);
5445                     }
5446                     if(cf->is_matrix()){
5447                         auto newf = embed(make_shared<func<type>>(*cf));
5448                         cf->_val = newf->_val;
5449                     }
5450                 }
5451             }
5452             for (auto &p_t: f->get_qterms()) {
5453                 if (p_t.second._coef->is_function() && !p_t.second._coef->func_is_number()) {
5454                     auto cf = static_pointer_cast<func<type>>(p_t.second._coef);
5455                     auto exp = cf->get_expr();
5456                     if (exp) {
5457                         embed(*exp);
5458                     }
5459                     if(cf->is_matrix()){
5460                         auto newf = embed(make_shared<func<type>>(*cf));
5461                         cf->_val = newf->_val;
5462                     }
5463                 }
5464             }
5465             for (auto &p_t: f->get_pterms()) {
5466                 if (p_t.second._coef->is_function() && !p_t.second._coef->func_is_number()) {
5467                     auto cf = static_pointer_cast<func<type>>(p_t.second._coef);
5468                     auto exp = cf->get_expr();
5469                     if (exp) {
5470                         embed(*exp);
5471                     }
5472                 }
5473             }
5474             if (f->get_cst()->is_function() && !f->get_cst()->func_is_number()) {
5475                 auto c = static_pointer_cast<func<type>>(f->get_cst());
5476                 auto exp = c->get_expr();
5477                 if (exp) {
5478                     embed(*exp);
5479                 }
5480                 if(c->is_matrix()){
5481                     auto newf = embed(make_shared<func<type>>(*c));
5482                     c->_val = newf->_val;
5483                 }
5484             }
5485             if (f->get_expr()) {
5486                 embed(*f->get_expr());
5487             }
5488             bool found_cpy = false;
5489             auto name = f->to_str();
5490             if (name.back()=='T') {
5491                 name = name.substr(0,name.size()-2);
5492                 if (_nl_funcs_map.count(name)>0) {
5493                     auto cpy = _nl_funcs_map.at(name);
5494                     f->_val = cpy->_val;
5495                     f->_evaluated = true;
5496                     found_cpy = true;
5497                 }
5498             }
5499             else {
5500                 auto name1 = "[["+name+"]]\u1D40";
5501                 if (_nl_funcs_map.count(name1)>0) {
5502                     auto cpy = _nl_funcs_map.at(name1);
5503                     f->_val = cpy->_val;
5504                     f->_evaluated = true;
5505                     found_cpy = true;
5506                 }
5507                 auto name2 = name+"\u1D40";
5508                 if (_nl_funcs_map.count(name2)>0) {
5509                     auto cpy = _nl_funcs_map.at(name2);
5510                     f->_val = cpy->_val;
5511                     f->_evaluated = true;
5512                     found_cpy = true;
5513                 }
5514             }
5515             if (!found_cpy && insert_in_map) {
5516                 auto f_p = _nl_funcs_map.insert(make_pair<>(f->to_str(), f));
5517                 if (f_p.second) {
5518                     _nl_funcs.push_back(f_p.first->second);
5519                     DebugOff(f->to_str() << endl);
5520                     f->allocate_mem();
5521                     return f;
5522                     //        f_p.first->second->_val = make_shared<vector<double>>();
5523                     //        f_p.first->second->_val->resize(f_p.first->second->get_nb_inst());
5524                 }
5525                 //        if (f->_new) {
5526                 //            f_p.first->second = f;
5527                 //            return f;
5528                 //        }
5529                 //                if (f->get_nb_inst() > f_p.first->second->get_nb_inst()) {
5530                 //                    *f_p.first->second = *f;
5531                 //                }
5532 
5533                 else if (f->_dfdx->size()>0) {
5534                     f_p.first->second->_dfdx = f->_dfdx;
5535 
5536                 }
5537                 //        f_p.first->second->allocate_mem();
5538                 return f_p.first->second;
5539             }
5540             return f;
5541         }
5542 
5543 
5544 
print_nl_functions()5545         void print_nl_functions() const{
5546             cout << "Number of atomic functions = " << _nl_funcs.size();
5547             cout << endl;
5548             for (auto& f: _nl_funcs){
5549                 cout << f->_to_str;
5550                 //                f->print();
5551                 cout << endl;
5552             }
5553             cout << endl;
5554         }
5555 
5556         void print_int_solution(int prec=5) const{
5557             for (auto &v_pair:_vars) {
5558                 auto v = v_pair.second;
5559                 if(v->_is_relaxed)
5560                     v->print_vals(prec);
5561             }
5562         }
5563 
5564         void print_solution(int prec=5) const{
5565             print_obj_val(prec);
5566             for (auto &v_pair:_vars) {
5567                 auto v = v_pair.second;
5568                 v->print_vals(prec);
5569             }
5570         }
5571 
get_int_var(size_t idx)5572         shared_ptr<param_> get_int_var(size_t idx){
5573             return _int_vars.at(idx);
5574         }
5575 
round_solution()5576         void round_solution(){
5577             for (auto &v_pair:_vars) {
5578                 if(v_pair.second->_is_relaxed){
5579                     v_pair.second->round_vals();
5580                     auto int_var = get_int_var(v_pair.second->get_id());
5581                     int_var->copy_vals(v_pair.second);
5582                 }
5583             }
5584         }
5585 
5586 
print_symbolic()5587         void print_symbolic(){
5588             cout << "-------------------------" << endl;
5589             print_properties();
5590             _obj->print_symbolic();
5591             for(auto& p: _cons){
5592                 p.second->print_symbolic();
5593             }
5594             for(auto& v: _vars){
5595                 v.second->print_symbolic();
5596             }
5597             cout << "-------------------------" << endl;
5598         }
5599 
print_properties()5600         size_t print_properties() const{
5601             string str = "\n";
5602             if(is_linear()){
5603                 str += "Linear ";
5604             }
5605             else if(is_convex()){
5606                 str += "Convex ";
5607             }
5608             else if(is_concave()){
5609                 str += "Concave ";
5610             }
5611             else {
5612                 str += "Nonconvex ";
5613             }
5614             str += "Model: " + _name+"\n";;
5615             auto size_header = str.size()-1;
5616             str.insert(0,size_header,'-');
5617             str.append(size_header,'-');
5618             cout << str << endl;
5619             cout << "Number of variables = " << get_nb_vars() << endl;
5620             cout << "Number of constraints = " << get_nb_cons() << " (" << get_nb_ineq() << " inequalities, " << get_nb_eq() << " equations)" << endl;
5621             //    compute_funcs();
5622             cout << "Objective: ";
5623             if(_objt==minimize){
5624                 cout << "Min ";
5625             }
5626             else{
5627                 cout << "Max ";
5628             }
5629             return size_header;
5630         }
5631 
5632         /** Write integer solution point to file */
5633         void write_int_solution(int precision = 10){
5634             ofstream myfile;
5635             string fname = _name+"_int_solution.txt";
5636             myfile.open(fname);
5637             std::streambuf *coutbuf = std::cout.rdbuf(); //save old buf
5638             std::cout.rdbuf(myfile.rdbuf());
5639             print_int_solution(precision);
5640             std::cout.rdbuf(coutbuf);
5641             myfile.close();
5642         }
5643 
5644         /** Write solution point to file */
5645         void write_solution(int precision = 10){
5646             ofstream myfile;
5647             string fname = _name+"_solution.txt";
5648             myfile.open(fname);
5649             std::streambuf *coutbuf = std::cout.rdbuf(); //save old buf
5650             std::cout.rdbuf(myfile.rdbuf());
5651             print_solution(precision);
5652             std::cout.rdbuf(coutbuf);
5653             myfile.close();
5654         }
5655 
5656         /** Write Model to file */
5657         void write(int precision = 10){
5658             ofstream myfile;
5659             string fname = _name+".txt";
5660             myfile.open(fname);
5661             std::streambuf *coutbuf = std::cout.rdbuf(); //save old buf
5662             std::cout.rdbuf(myfile.rdbuf());
5663             print(precision);
5664             std::cout.rdbuf(coutbuf);
5665             myfile.close();
5666         }
5667 
5668         void print(int prec = 10){
5669             auto size_header = print_properties();
5670             _obj->print(prec);
5671             cout << "s.t." << endl;
5672             for(auto& p: _cons){
5673                 p.second->print(prec);
5674             }
5675             for(auto& v: _vars){
5676                 v.second->print(prec);
5677             }
5678             string tail;
5679             tail.insert(0,size_header,'-');
5680             cout << tail << endl;
5681         }
5682 
5683 
5684 
5685 
print_constraints()5686         void print_constraints() const{
5687             for(auto& p: _cons){
5688                 p.second->print_symbolic();
5689             }
5690         }
5691 
5692 
has_int()5693         bool has_int() const{
5694             return !_int_vars.empty();
5695         }
5696 
replace_integers()5697         void replace_integers(){/*< Replace internal type of integer variables so that continuous relaxations can be computed */
5698             bool has_int = false;
5699             for (auto &v_p:this->_vars_name) {
5700                 if (v_p.second->is_integer() || v_p.second->is_binary()) {
5701                     has_int = true;
5702                     auto v = v_p.second;
5703                     _int_vars[v->get_id()] = v;
5704                     auto new_v = make_shared<var<double>>(v_p.second->_name);
5705                     new_v->shallow_copy(*v);
5706                     new_v->_is_relaxed = true;
5707                     new_v->copy_vals(v);
5708                     new_v->copy_bounds(v);
5709                     v_p.second = new_v;
5710                 }
5711             }
5712             for (auto &v_p:this->_vars) {
5713                 if (v_p.second->is_integer() || v_p.second->is_binary()) {
5714                     auto name = v_p.second->_name;
5715                     v_p.second = this->get_var_ptr(name);
5716                 }
5717             }
5718             if(has_int){
5719                 this->_obj->relax(this->_vars);
5720                 for (auto &c_p: this->_cons) {
5721                     c_p.second->relax(this->_vars);
5722                 }
5723             }
5724         }
5725 
5726 
5727 
5728         /*** adding on/off option to a constraint within an interval ***/
5729 //        template<typename T=type,typename std::enable_if<is_arithmetic<type>::value>::type* = nullptr>
5730 //        void add_on_off_univariate(const Constraint<type>& c, var<bool>& on, int num_partns, int cur_partn){
5731 //            if (c.get_ftype() != lin_) {
5732 //                cerr << "Nonlinear constraint.\n";
5733 //                exit(-1);
5734 //            }
5735 //
5736 //            param<type> M1 ("M1");
5737 //            param<type> M2 ("M2");
5738 //
5739 //            size_t nb_ins = c.get_nb_inst();
5740 //
5741 //            for (size_t inst = 0; inst<nb_ins; inst++)
5742 //            {
5743 //                for (auto &pair:*c._lterms) {
5744 //                    auto term = pair.second;
5745 //                    type coef_val = 0;
5746 //                    if (term._coef->is_function()) {
5747 //                        auto coef = static_pointer_cast<func<type>>(term._coef);
5748 //                        coef_val = coef->eval(inst);//this will give you the value of this instance
5749 //                    }
5750 //                    else if(term._coef->is_param()) {
5751 //                        auto coef = static_pointer_cast<param<type>>(term._coef);
5752 //                        coef_val = coef->eval(inst);//this will give you the value of this instance
5753 //                    }
5754 //                    else { /*means (term._coef->is_number())*/
5755 //                        auto coef = static_pointer_cast<constant<type>>(term._coef);
5756 //                        coef_val = coef->eval();
5757 //                    }
5758 //
5759 //                    auto LB = (term._p->get_double_lb(inst));
5760 //                    auto UB = (term._p->get_double_ub(inst));
5761 //
5762 //                    if (c.get_ctype() == eq) {
5763 //                        double LB_partn = (LB*(num_partns - cur_partn + 1) + UB*(cur_partn - 1))/num_partns;
5764 //                        double UB_partn = (LB*(num_partns - cur_partn) + UB*(cur_partn))/num_partns;
5765 //
5766 //                        if (coef_val < 0){
5767 //                            M1.add_val(coef_val * LB_partn);
5768 //                            M2.add_val(coef_val * UB_partn);
5769 //                        }
5770 //                        else {
5771 //                            M1.add_val(coef_val * UB_partn);
5772 //                            M2.add_val(coef_val * LB_partn);
5773 //                        }
5774 //
5775 //                    }
5776 //
5777 //                    else if (c.get_ctype() == leq) {
5778 //                        if (coef_val < 0){
5779 //                            type LB_partn = (LB*(num_partns - cur_partn + 1) + UB*(cur_partn - 1))/num_partns;
5780 //                            M1.add_val(coef_val * LB_partn);
5781 //
5782 //                        }
5783 //                        else {
5784 //                            type UB_partn = (LB*(num_partns - cur_partn) + UB*(cur_partn))/num_partns;
5785 //                            M1.add_val(coef_val * UB_partn);
5786 //                        }
5787 //                    }
5788 //
5789 //                    else {
5790 //                        if (coef_val < 0){
5791 //                            auto UB_partn = (LB*(num_partns - cur_partn) + UB*(cur_partn))/num_partns;
5792 //                            M2.add_val(coef_val * UB_partn);
5793 //                        }
5794 //                        else {
5795 //                            auto LB_partn = (LB*(num_partns - cur_partn + 1) + UB*(cur_partn - 1))/num_partns;
5796 //                            M2.add_val(coef_val * LB_partn);
5797 //
5798 //                        }
5799 //                    }
5800 //                }
5801 //            }
5802 //
5803 //
5804 //            if (c.get_ctype() == eq){
5805 //                Constraint<type> res1(c.get_name() + "_on/off");
5806 //                res1 = c - M1*(1-on);
5807 //                add_constraint(res1<=0);
5808 //
5809 //                Constraint<type> res2(c.get_name() + "_on/off2");
5810 //                res2 = c - M2*(1-on);
5811 //                add_constraint(res2>=0);
5812 //            }
5813 //
5814 //            if (c.get_ctype() == leq){
5815 //                Constraint<type> res1(c.get_name() + "_on/off");
5816 //                res1 = c - M1*(1-on);
5817 //                add_constraint(res1<=0);
5818 //            }
5819 //
5820 //            if (c.get_ctype() == geq){
5821 //                Constraint<type> res2(c.get_name() + "_on/off2");
5822 //                res2 = c - M2*(1-on);
5823 //                add_constraint(res2>=0);
5824 //            }
5825 //        }
5826 
5827 
5828         //this function calculates on-off coefficients for representing the convex hull of the disjunctive union of constraints
5829         //INPUT: a constraint to be activated based on some binary variables, and a constraint type for making sure the coefficients are gathered correctly. Follows the formulation described by Hijazi et. al.
get_on_off_coefficients(Constraint<type> & c,const ConstraintType c_type)5830         void get_on_off_coefficients(Constraint<type>& c, const ConstraintType c_type){
5831             if (c.get_ftype() != lin_) {
5832                 cerr << "Nonlinear constraint.\n";
5833                 exit(-1);
5834             }
5835             //TODO : only consider the non-lazy instances to add on-off constraint
5836 
5837             /*allocate the coefficient vectors and the sum values to update them*/
5838             type M1sum_off, M2sum_off;
5839             type M1sum_on, M2sum_on;
5840 
5841             /*allocate the bound values*/
5842             type LB_off,UB_off;
5843             type LB_on,UB_on;
5844 
5845             size_t nb_ins = c.get_nb_inst();
5846 
5847             //go over all the instances and fill the coefficient values
5848             for (size_t inst = 0; inst<nb_ins; inst++)
5849             {
5850                 M1sum_off = 0;
5851                 M2sum_off = 0;
5852                 M1sum_on = 0;
5853                 M2sum_on = 0;
5854 
5855                 c.eval_all();
5856 
5857                 //cast a correct pointer for the constant part in the constraint
5858                 if (!c.get_cst()->is_zero()) {
5859                     if (c.get_cst()->is_number()) {
5860                         auto f_cst = static_pointer_cast<constant<type>>(c.get_cst());
5861                         M1sum_on -= f_cst->eval();
5862                         M2sum_on -= f_cst->eval();
5863                     }
5864                     else if (c.get_cst()->is_param()) {
5865                         auto f_cst = static_pointer_cast<param<type>>(c.get_cst());
5866                         M1sum_on -= f_cst->eval(inst);
5867                         M2sum_on -= f_cst->eval(inst);
5868                     }
5869                     else {
5870                         auto f_cst = static_pointer_cast<func<type>>(c.get_cst());
5871                         M1sum_on -= f_cst->eval(inst);
5872                         M2sum_on -= f_cst->eval(inst);
5873                     }
5874                 }
5875                 //collect the instance index as a string
5876                 auto partition_info = c._indices->_keys->at(inst);
5877 
5878                 //go over the linear terms and cast pointers
5879                 for (auto &pair:*c._lterms) {
5880                     auto term = pair.second;
5881 
5882                     auto in_S = *term._p->_in; //collect that the lterm is in S or not
5883                     type coef_val = 0;
5884                     if (term._coef->is_function()) {
5885                         auto coef = static_pointer_cast<func<type>>(term._coef);
5886                         coef_val = coef->eval(inst);//this will give you the value of this instance
5887                     }
5888                     else if(term._coef->is_param()) {
5889                         auto coef = static_pointer_cast<param<type>>(term._coef);
5890                         coef_val = coef->eval(inst);//this will give you the value of this instance
5891                     }
5892                     else { /*means (term._coef->is_number())*/
5893                         auto coef = static_pointer_cast<constant<type>>(term._coef);
5894                         coef_val = coef->eval();
5895                     }
5896 
5897                     auto inst_id = term._p->get_id_inst(inst);
5898                     auto num_partns = term._p->get_num_partns();
5899 
5900                     /* update the coef_val as coef_val * sign */
5901                     if (!term._sign) coef_val = -coef_val;
5902 
5903                     //set the global bounds
5904                     LB_off = (term._p->get_double_lb(inst_id));
5905                     UB_off = (term._p->get_double_ub(inst_id));
5906 
5907                     auto lifted = term._p->is_lifted();
5908                     if (lifted){ //if lifted to LB_on values should be the global bounds since the number of partitions is 1
5909                         LB_on = (term._p->get_double_lb(inst_id));
5910                         UB_on = (term._p->get_double_ub(inst_id));
5911                     }
5912                     else {
5913                         //collect the cur_partn number from the instance index (this is not the info _cur_partn stored in the variable, it is stored in the indices of the constraint)
5914                         auto name1 = term._p->get_name(true,true);
5915                         auto loc1 = partition_info.find(name1) + name1.length() +1 ;
5916                         auto loc2 = partition_info.find_first_of('}', loc1);
5917                         int cur_partn = stoi(partition_info.substr(loc1,loc2-loc1));
5918 
5919                         if (cur_partn > num_partns) throw invalid_argument("Current partition is out of range (larger than the number of partitions)");
5920 
5921                         LB_on = (LB_off*(num_partns - cur_partn + 1) + UB_off*(cur_partn - 1))/num_partns;
5922                         UB_on = (LB_off*(num_partns - cur_partn) + UB_off*(cur_partn))/num_partns;
5923                     }
5924 
5925                     // update the coefficients based on the constraint type leq or geq
5926                     if (c_type == leq) {
5927                         if (coef_val < 0){
5928                             if(in_S){
5929                                 M1sum_on -= coef_val * UB_on;
5930                             }
5931                             else {
5932                                 M1sum_off += coef_val * LB_off;
5933                             }
5934                         }
5935                         else {
5936                             if(in_S){
5937                                 M1sum_on -= coef_val * LB_on;
5938                             }
5939                             else {
5940                                 M1sum_off += coef_val * UB_off;
5941                             }
5942                         }
5943                     }
5944                     // if the constraint is of type geq update the coefficients accordingly
5945                     else if (c_type == geq){
5946                         if (coef_val < 0){
5947                             if(in_S){
5948                                 M2sum_on -= coef_val * LB_on;
5949                             }
5950                             else {
5951                                 M2sum_off += coef_val * UB_off;
5952                             }
5953                         }
5954                         else {
5955                             if(in_S){
5956                                 M2sum_on -= coef_val * UB_on;
5957                             }
5958                             else {
5959                                 M2sum_off += coef_val * LB_off;
5960                             }
5961                         }
5962                     }
5963                     else { //throw an error if the constraint is of some other type (i.e. eq)
5964                         throw invalid_argument("Only leq and geq types are allowed. If you want to get coefficients for eq, use leq and geq consecutively.");
5965                     }
5966                 }
5967                 //set the coefficient values in the attribute belonging to constraint object
5968                 if (c_type == leq){
5969                     c._offCoef.set_val(inst,M1sum_off);
5970                     c._onCoef.set_val(inst,M1sum_on);
5971                 }
5972                 else {
5973                     c._offCoef.set_val(inst,M2sum_off);
5974                     c._onCoef.set_val(inst,M2sum_on);
5975                 }
5976             }
5977 
5978         }
5979 
5980         //this function calculates on-off coefficients for representing the convex hull of the disjunctive union of constraints
5981         //INPUT: a constraint to be activated based on some binary variables, assuming the constraint is of standard type "leq (<=) ". Follows the formulation described by Hijazi et. al.
get_on_off_coefficients_standard(Constraint<type> & c)5982         void get_on_off_coefficients_standard(Constraint<type>& c){
5983             if (c.get_ftype() != lin_) {
5984                 cerr << "Nonlinear constraint.\n";
5985                 exit(-1);
5986             }
5987 
5988             //TODO : only consider the non-lazy instances to add on-off constraint
5989 
5990             /*allocate the coefficient vectors and the sum values to update them*/
5991             type M1sum_off, M1sum_on;
5992 
5993             /*allocate the bound values*/
5994             type LB_off,UB_off;
5995             type LB_on,UB_on;
5996 
5997             size_t nb_ins = c.get_nb_inst();
5998 
5999             for (size_t inst = 0; inst<nb_ins; inst++)
6000             {
6001                 string prev_name = "";
6002                 M1sum_off = 0;
6003                 M1sum_on = 0;
6004 
6005                 c.eval_all();
6006                 //collect the constant part in the constraint by casting the correct pointer
6007                 if (!c.get_cst()->is_zero()) {
6008                     if (c.get_cst()->is_number()) {
6009                         auto f_cst = static_pointer_cast<constant<type>>(c.get_cst());
6010                         M1sum_on -= f_cst->eval();
6011                     }
6012                     else if (c.get_cst()->is_param()) {
6013                         auto f_cst = static_pointer_cast<param<type>>(c.get_cst());
6014                         M1sum_on -= f_cst->eval(inst);
6015                     }
6016                     else {
6017                         auto f_cst = static_pointer_cast<func<type>>(c.get_cst());
6018                         M1sum_on -= f_cst->eval(inst);
6019                     }
6020                 }
6021                 //collect the instance index as a string
6022                 auto partition_info = c._indices->_keys->at(inst);
6023 
6024                 //go over the lterms and calculate the proper coefficients
6025                 for (auto &pair:*c._lterms) {
6026                     auto in_S = *pair.second._p->_in; //collect that the lterm is in S or not
6027                     type coef_val = 0;
6028                     if (pair.second._coef->is_function()) {
6029                         auto coef = static_pointer_cast<func<type>>(pair.second._coef);
6030                         coef_val = coef->eval(inst);//this will give you the value of this instance
6031                     }
6032                     else if(pair.second._coef->is_param()) {
6033                         auto coef = static_pointer_cast<param<type>>(pair.second._coef);
6034                         coef_val = coef->eval(inst);//this will give you the value of this instance
6035                     }
6036                     else { /*means (term._coef->is_number())*/
6037                         auto coef = static_pointer_cast<constant<type>>(pair.second._coef);
6038                         coef_val = coef->eval();
6039                     }
6040 
6041                     auto inst_id = pair.second._p->get_id_inst(inst);
6042                     auto num_partns = pair.second._p->get_num_partns();
6043                     auto in_SOC_partn = pair.second._p->get_in_SOC_partn();
6044 
6045                     /* update the coef_val as coef_val * sign */
6046                     if (!pair.second._sign) coef_val = -coef_val;
6047 
6048                     //set the global bounds
6049                     LB_off = (pair.second._p->get_double_lb(inst_id));
6050                     UB_off = (pair.second._p->get_double_ub(inst_id));
6051 
6052                     auto lifted = pair.second._p->is_lifted();
6053                     if (lifted || (num_partns == 1) || in_SOC_partn){ //if lifted to LB_on values should be the global bounds since the number of partitions is 1, similarly if number of partitions is 1
6054                         LB_on = (pair.second._p->get_double_lb(inst_id));
6055                         UB_on = (pair.second._p->get_double_ub(inst_id));
6056                     }
6057                     else {
6058                         //collect the cur_partn number from the instance index (this is not the info _cur_partn stored in the variable, it is stored in the indices of the constraint)
6059                         auto name1 = pair.second._p->get_name(true,true);
6060                         int cur_partn;
6061                         //this uses the variable names to access the correct number of partitions. The convention is if there are two variables with same base name, the names will be ordered alphabetically, and accessed similarly as well
6062                         if(prev_name == name1){
6063                             auto loc1 = partition_info.rfind(name1) + name1.length() +1 ;
6064                             auto loc2 = partition_info.find_first_of('}', loc1);
6065                             cur_partn = stoi(partition_info.substr(loc1,loc2-loc1));
6066 
6067                         }
6068                         else{
6069                             auto loc1 = partition_info.find(name1) + name1.length() +1 ;
6070                             auto loc2 = partition_info.find_first_of('}', loc1);
6071                             cur_partn = stoi(partition_info.substr(loc1,loc2-loc1));
6072                         }
6073 
6074                         //set the prev_name for variables having the same name
6075                         prev_name = name1;
6076 
6077                         if (cur_partn > num_partns) throw invalid_argument("Current partition is out of range (larger than the number of partitions)");
6078 
6079                         LB_on = (LB_off*(num_partns - cur_partn + 1) + UB_off*(cur_partn - 1))/num_partns;
6080                         UB_on = (LB_off*(num_partns - cur_partn) + UB_off*(cur_partn))/num_partns;
6081                     }
6082                     if (coef_val < 0){
6083                         if(in_S){
6084                             M1sum_on -= coef_val * UB_on;
6085                         }
6086                         else {
6087                             M1sum_off += coef_val * LB_off;
6088                         }
6089                     }
6090                     else {
6091                         if(in_S){
6092                             M1sum_on -= coef_val * LB_on;
6093                         }
6094                         else {
6095                             M1sum_off += coef_val * UB_off;
6096                         }
6097                     }
6098                 }
6099                 //set the coefficient values in the attribute belonging to constraint object
6100                 c._offCoef.set_val(inst,M1sum_off);
6101                 c._onCoef.set_val(inst,M1sum_on);
6102             }
6103 
6104         }
6105 
6106         //This function adds on-off version of a given linear constraint and the binary variables to activate. The boolean option handles all the facet definining inequalities of the convex hull (if true), else it only adds the Big_M version of the constraint
6107         //INPUT: linear constraint to be activated, corresponding binary variables to form a disjunctive union, big_M version of the constraint or the whole convex hull
6108         //OUTPUT: disjunctive union of the constraints provided by "c" and linked by "on"
6109         void add_on_off_multivariate_refined(Constraint<type>& c, const var<int>& on, bool big_M = false){
6110             if (c.get_ftype() != lin_) {
6111                 cerr << "Nonlinear constraint.\n";
6112                 exit(-1);
6113             }
6114 
6115             // TODO: consider only the not lazy ones in on-off
6116             // TODO: maybe do this somehow in the constructor
6117             c._onCoef.in(range(1,c.get_nb_inst()));
6118             c._offCoef.in(range(1,c.get_nb_inst()));
6119 
6120             //use bitset vector to represent S efficiently
6121             //currently this function can handle up to 64 linear variables in the constraint, and throws and error otherwise
6122             auto n_terms = c._lterms->size();
6123             if (n_terms > 64){
6124                 throw invalid_argument("Currently we can not handle more than 64 linear terms in an on/off constraint. Please do not use partitioning or decrease the number of linear terms.");
6125             }
6126             std::bitset<64> S;
6127             //decide on the subset selection limit
6128             int num_subset;
6129 
6130             //if there are less than 3 variables appearing in the linear constraint, the function will only add the big-M version, since that with the bounds on the variables defines the convex hull
6131             if ((n_terms <= 2) || (big_M)) num_subset = 1;
6132             else num_subset = std::pow(2,n_terms) -1;
6133 
6134             for (int i = 0 ; i< num_subset ; ++i) { //should be num_subset
6135                 S = i;
6136                 int j = 0;
6137 
6138                 shared_ptr<pair<type,type>> term_range;
6139                 func<type> LHS; //to handle the left hand side of the constaint
6140                 for (auto &lt:*c._lterms) { //set the _in values and create LHS
6141                     *(lt.second._p->_in) = S[j];
6142                     if (!S[j]){ //only if not in S
6143                         //cast the correct pointer to the coef part of the linear term and add that to the LHS depending on their _in values
6144                         auto coef = lt.second._coef->copy();
6145                         if (coef->is_function()) {
6146                             auto f_cst = *((func<type>*)(coef.get()));
6147                             auto var_range = make_shared<pair<type,type>>(c.get_range(lt.second._p));
6148                             term_range = get_product_range(f_cst._range,var_range);
6149                             LHS.insert(lt.second._sign, f_cst, *lt.second._p);
6150                         }
6151                         else if(coef->is_param()) {
6152                             auto p_cst = *((param<type>*)(coef.get()));
6153                             auto var_range = make_shared<pair<type,type>>(c.get_range(lt.second._p));
6154                             term_range = get_product_range(p_cst._range,var_range);
6155                             LHS.insert(lt.second._sign, p_cst, *lt.second._p);
6156                         }
6157                         else if(coef->is_number()) {
6158                             auto p_cst = *((constant<type>*)(coef.get()));
6159                             auto var_range = make_shared<pair<type,type>>(c.get_range(lt.second._p));
6160                             term_range = get_product_range(make_shared<pair<type,type>>(p_cst.eval(),p_cst.eval()),var_range);
6161                             LHS.insert(lt.second._sign, p_cst, *lt.second._p);
6162                         }
6163                         //update the ranges of the function appropriately
6164                         if(lt.second._sign){
6165                             LHS._range = get_plus_range(LHS._range, term_range);
6166                         }
6167                         else {
6168                             LHS._range = get_minus_range(LHS._range, term_range);
6169                         }
6170                     }
6171                     j++;
6172                 }
6173 
6174                 // all the cases are standardized into the leq form, then for each case, we obtain the _onCoef and _offCoef for the constraint and add it to the model with the proper LHS value for each subset S
6175                 if (c.get_ctype() == eq) {
6176                     get_on_off_coefficients_standard(c);
6177                     auto offCoef1 = c._offCoef.deep_copy();
6178                     auto onCoef1 = c._onCoef.deep_copy();
6179                     Constraint<type> res1(c.get_name() + "_" + to_string(i) + "_on/off");
6180                     res1 = LHS - offCoef1*(1-on) - onCoef1*on;
6181                     add_constraint(res1.in(*c._indices)<=0);
6182                     Constraint<type> n_c(c);
6183                     n_c *= -1;
6184                     get_on_off_coefficients_standard(n_c);
6185                     auto offCoef2 = n_c._offCoef.deep_copy();
6186                     auto onCoef2 = n_c._onCoef.deep_copy();
6187                     Constraint<type> res2(c.get_name() +  "_" + to_string(i) + "_on/off2");
6188                     res2 = -1 * LHS - offCoef2*(1-on) - onCoef2*on;
6189                     add_constraint(res2.in(*c._indices)<=0);
6190 
6191                 }
6192 
6193                 else if (c.get_ctype() == leq) {
6194                     get_on_off_coefficients_standard(c);
6195                     auto offCoef = c._offCoef.deep_copy();
6196                     auto onCoef = c._onCoef.deep_copy();
6197                     Constraint<type> res1(c.get_name() +  "_" + to_string(i) + "_on/off");
6198                     res1 = LHS - offCoef*(1-on) - onCoef*on;
6199                     add_constraint(res1.in(*c._indices)<=0);
6200                 }
6201 
6202                 else { //if c.get_ctype() == geq
6203                     Constraint<type> n_c(c);
6204                     n_c *= -1;
6205                     n_c._ctype = leq;
6206                     get_on_off_coefficients_standard(n_c);
6207                     auto offCoef = n_c._offCoef.deep_copy();
6208                     auto onCoef = n_c._onCoef.deep_copy();
6209                     Constraint<type> res2(c.get_name() +  "_" + to_string(i) + "_on/off2");
6210                     res2 = -1 * LHS - offCoef*(1-on) - onCoef*on;
6211                     add_constraint(res2.in(*c._indices)<=0);
6212                 }
6213             }
6214         }
6215 
6216         //this function adds the on/off version of the McCormick relaxations for bilinear terms, and on/off version of the secants for the quadratic terms
6217         //INPUT: base name for the constraints to be added, lifted variable, correspoinding variables v1 and v2, and the partitioning indicator variable on (binary)
6218         //OUTPUT: the on/off version of the McCormick relaxation (for every partition and the disjunctive union of that) for bilinears and the secant relaxation (for every partition and the disjunctive union of that) for quadratic variables
6219         template<typename T1>
add_on_off_McCormick_refined(std::string name,var<T1> && vlift,var<T1> && v1,var<T1> && v2,const var<int> & on)6220         void add_on_off_McCormick_refined(std::string name, var<T1>&& vlift, var<T1>&& v1, var<T1>&& v2, const var<int>& on) {
6221 
6222             if (!v1.is_bounded_below() || !v2.is_bounded_below() || !vlift.is_bounded_below() || !v1.is_bounded_above() || !v2.is_bounded_above() || !vlift.is_bounded_above()){
6223                 throw invalid_argument("Variables have to be bounded. Please set bounds for all!");}
6224 
6225             if (!(vlift._lift)){
6226                 throw invalid_argument("You forgot to set _lift to true for the lifted variable.");
6227             }
6228 
6229             //collect the number of partitions for each variable
6230             int num_partns1 = *v1._num_partns;
6231             int num_partns2 = *v2._num_partns;
6232 
6233             //collect the base name of each variable
6234             auto name1 = v1.get_name(true,true);
6235             auto name2 = v2.get_name(true,true);
6236 
6237             //if the variables are different apply McCormick relaxations for each partition pair
6238             if(v1._name!=v2._name)
6239             {
6240                 //a dimension check to prevent any errors
6241                 if (on.get_dim() != v1.get_dim() * num_partns1 * num_partns2){
6242                     throw invalid_argument("Number of on variables are not conforming with the given number of partitions");}
6243 
6244                 //create the proper index sets for partitions
6245                 indices partns1("partns1");
6246                 for (int i = 0; i < num_partns1 ; ++i)
6247                 {
6248                     partns1.add(name1+ "{" +to_string(i+1) + "}");
6249                 }
6250 
6251                 indices partns2("partns2");
6252                 for (int i = 0; i < num_partns2 ; ++i)
6253                 {
6254                     partns2.add(name2+ "{"+ to_string(i+1) + "}");
6255                 }
6256 
6257                 indices partns("partns");
6258                 //                partns = indices(range(1,num_partns1),range(1,num_partns2));
6259                 partns = indices(partns1,partns2);
6260 
6261                 //combine the index sets for instances
6262                 auto var_indices = combine(*v1._indices,*v2._indices);
6263                 auto inst_partition = indices(var_indices,partns);
6264 
6265                 // Create the parameters for the McCormick constraints (there are 4 differenc MC constraints)
6266                 param<type> V1par_MC1("V1par_MC1");
6267                 V1par_MC1.in(inst_partition);
6268                 param<type> V2par_MC1("V2par_MC1");
6269                 V2par_MC1.in(inst_partition);
6270                 param<type> Cpar_MC1("Cpar_MC1");
6271                 Cpar_MC1.in(inst_partition);
6272 
6273                 param<type> V1par_MC2("V1par_MC2");
6274                 V1par_MC2.in(inst_partition);
6275                 param<type> V2par_MC2("V2par_MC2");
6276                 V2par_MC2.in(inst_partition);
6277                 param<type> Cpar_MC2("Cpar_MC2");
6278                 Cpar_MC2.in(inst_partition);
6279 
6280                 param<type> V1par_MC3("V1par_MC3");
6281                 V1par_MC3.in(inst_partition);
6282                 param<type> V2par_MC3("V2par_MC3");
6283                 V2par_MC3.in(inst_partition);
6284                 param<type> Cpar_MC3("Cpar_MC3");
6285                 Cpar_MC3.in(inst_partition);
6286 
6287                 param<type> V1par_MC4("V1par_MC4");
6288                 V1par_MC4.in(inst_partition);
6289                 param<type> V2par_MC4("V2par_MC4");
6290                 V2par_MC4.in(inst_partition);
6291                 param<type> Cpar_MC4("Cpar_MC4");
6292                 Cpar_MC4.in(inst_partition);
6293 
6294                 //create the parameters for the bounds on the variables (for on and off cases)
6295                 param<type> v1_on_LB("v1_on_LB");
6296                 v1_on_LB.in(inst_partition);
6297                 param<type> v1_off_LB("v1_off_LB");
6298                 v1_off_LB.in(inst_partition);
6299 
6300                 param<type> v1_on_UB("v1_on_UB");
6301                 v1_on_UB.in(inst_partition);
6302                 param<type> v1_off_UB("v1_off_UB");
6303                 v1_off_UB.in(inst_partition);
6304 
6305                 param<type> v2_on_LB("v2_on_LB");
6306                 v2_on_LB.in(inst_partition);
6307                 param<type> v2_off_LB("v2_off_LB");
6308                 v2_off_LB.in(inst_partition);
6309 
6310                 param<type> v2_on_UB("v2_on_UB");
6311                 v2_on_UB.in(inst_partition);
6312                 param<type> v2_off_UB("v2_off_UB");
6313                 v2_off_UB.in(inst_partition);
6314 
6315                 //fill the parameters for the variable on-off bounds and the corresponding function values
6316                 size_t nb_ins = v1.get_nb_inst();
6317 
6318                 auto v1_global_lb = v1.get_lb();
6319                 auto v1_global_ub = v1.get_ub();
6320                 auto v2_global_lb = v2.get_lb();
6321                 auto v2_global_ub = v2.get_ub();
6322                 auto increment1 = (v1_global_ub - v1_global_lb)/num_partns1;
6323                 auto increment2 = (v2_global_ub - v2_global_lb)/num_partns2;
6324 
6325                 //for each partition pair and instance of the original constraint, set the values of all of the parameters
6326                 for (int i=0 ; i<num_partns1; ++i) {
6327                     auto LB_partn1 = v1_global_lb + increment1*i;
6328                     auto UB_partn1 = LB_partn1 + increment1;
6329                     LB_partn1.eval_all();
6330                     UB_partn1.eval_all();
6331                     for (int j=0 ; j<num_partns2; ++j) {
6332                         auto LB_partn2 = v2_global_lb + increment2*j;
6333                         auto UB_partn2 = LB_partn2 + increment2;
6334                         LB_partn2.eval_all();
6335                         UB_partn2.eval_all();
6336                         for (size_t inst = 0; inst< nb_ins; inst++){
6337                             //collect the proper indexing combining all the information available
6338                             auto cur_var_idx = var_indices._keys->at(inst);
6339                             string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"},"+name2+"{"+to_string(j+1)+"}";
6340 
6341                             v1_off_LB.set_val(cur_idx,v1_global_lb.eval(inst));
6342                             v1_off_UB.set_val(cur_idx,v1_global_ub.eval(inst));
6343                             v1_on_LB.set_val(cur_idx,LB_partn1.eval(inst));
6344                             v1_on_UB.set_val(cur_idx,UB_partn1.eval(inst));
6345 
6346                             v2_off_LB.set_val(cur_idx,v2_global_lb.eval(inst));
6347                             v2_off_UB.set_val(cur_idx,v2_global_ub.eval(inst));
6348                             v2_on_LB.set_val(cur_idx,LB_partn2.eval(inst));
6349                             v2_on_UB.set_val(cur_idx,UB_partn2.eval(inst));
6350 
6351                             V2par_MC1.set_val(cur_idx,LB_partn1.eval(inst));
6352                             V1par_MC1.set_val(cur_idx,LB_partn2.eval(inst));
6353                             Cpar_MC1.set_val(cur_idx,LB_partn1.eval(inst)*LB_partn2.eval(inst));
6354 
6355                             V2par_MC2.set_val(cur_idx,UB_partn1.eval(inst));
6356                             V1par_MC2.set_val(cur_idx,UB_partn2.eval(inst));
6357                             Cpar_MC2.set_val(cur_idx,UB_partn1.eval(inst)*UB_partn2.eval(inst));
6358 
6359                             Cpar_MC3.set_val(cur_idx,LB_partn1.eval(inst)*UB_partn2.eval(inst));
6360 
6361                             Cpar_MC4.set_val(cur_idx,UB_partn1.eval(inst)*LB_partn2.eval(inst));
6362 
6363                         }
6364                     }
6365 
6366                 }
6367                 //add the McCormick constraints (all four of them) by using the previously calculated parameters, original variables and the indicator variables for the partitions
6368                 auto nb_entries_v1 = v1._indices->get_nb_entries();
6369                 Constraint<type> MC1(name+"_McCormick1");
6370                 MC1 = vlift.from_ith(0,inst_partition) - V1par_MC1*v1.from_ith(0,inst_partition) - V2par_MC1*v2.from_ith(nb_entries_v1,inst_partition) + Cpar_MC1;
6371                 MC1.in(inst_partition) >= 0;
6372                 add_on_off_multivariate_refined(MC1, on);
6373 
6374                 Constraint<type> MC2(name+"_McCormick2");
6375                 MC2 = vlift.from_ith(0,inst_partition) - V1par_MC2*v1.from_ith(0,inst_partition) - V2par_MC2*v2.from_ith(nb_entries_v1,inst_partition) + Cpar_MC2;
6376                 MC2.in(inst_partition) >= 0;
6377                 add_on_off_multivariate_refined(MC2, on);
6378 
6379                 Constraint<type> MC3(name+"_McCormick3");
6380                 MC3 = vlift.from_ith(0,inst_partition) - V1par_MC2*v1.from_ith(0,inst_partition) - V2par_MC1*v2.from_ith(nb_entries_v1,inst_partition) + Cpar_MC3;
6381                 MC3.in(inst_partition) <= 0;
6382                 add_on_off_multivariate_refined(MC3, on);
6383 
6384                 Constraint<type> MC4(name+"_McCormick4");
6385                 MC4 = vlift.from_ith(0,inst_partition) - V1par_MC1*v1.from_ith(0,inst_partition) - V2par_MC2*v2.from_ith(nb_entries_v1,inst_partition) + Cpar_MC4;
6386                 MC4.in(inst_partition) <= 0;
6387                 add_on_off_multivariate_refined(MC4, on);
6388 
6389                 //add the on-off bound constraints on original the variables
6390                 Constraint<type> v1_on_off_LB(name+"_v1_on_off_LB");
6391                 v1_on_off_LB = v1.from_ith(0,inst_partition) - on*v1_on_LB - (1-on)*v1_off_LB;
6392                 v1_on_off_LB.in(inst_partition) >= 0;
6393                 add(v1_on_off_LB);
6394 
6395                 Constraint<type> v1_on_off_UB(name+"_v1_on_off_UB");
6396                 v1_on_off_UB = v1.from_ith(0,inst_partition) - on*v1_on_UB - (1-on)*v1_off_UB;
6397                 v1_on_off_UB.in(inst_partition) <= 0;
6398                 add(v1_on_off_UB);
6399 
6400                 Constraint<type> v2_on_off_LB(name+"_v2_on_off_LB");
6401                 v2_on_off_LB = v2.from_ith(nb_entries_v1,inst_partition) - on*v2_on_LB - (1-on)*v2_off_LB;
6402                 v2_on_off_LB.in(inst_partition) >= 0;
6403                 add(v2_on_off_LB);
6404 
6405                 Constraint<type> v2_on_off_UB(name+"_v2_on_off_UB");
6406                 v2_on_off_UB = v2.from_ith(nb_entries_v1,inst_partition) - on*v2_on_UB - (1-on)*v2_off_UB;
6407                 v2_on_off_UB.in(inst_partition) <= 0;
6408                 add(v2_on_off_UB);
6409             }
6410 
6411 
6412             else{ //if the variables are same lift the quadratic via on/off secant
6413                 if (num_partns1 != num_partns2) throw invalid_argument("Partition numbers must be same since the two varibles are same.\n");
6414                 if (on.get_dim() != v1.get_dim() * num_partns1){
6415                     throw invalid_argument("Number of on variables are not conforming with the given number of partitions");}
6416 
6417                 //create the index set for the partitions and combine them with the instance indices
6418                 indices partns("partns");
6419                 for (int i = 0; i < num_partns1 ; ++i)
6420                 {
6421                     partns.add(name1+"{"+to_string(i+1) + "}");
6422                 }
6423                 auto var_indices = *v1._indices;
6424                 auto inst_partition = indices(var_indices,partns);
6425 
6426                 //Create the parameters for on/off constraints (including secant parameters and the on/off bounds on the variables)
6427                 param<type> Vpar("Vpar");
6428                 Vpar.in(inst_partition);
6429                 param<type> Cpar("Cpar");
6430                 Cpar.in(inst_partition);
6431 
6432                 param<type> v1_on_LB("v1_on_LB");
6433                 v1_on_LB.in(inst_partition);
6434                 param<type> v1_off_LB("v1_off_LB");
6435                 v1_off_LB.in(inst_partition);
6436 
6437                 param<type> v1_on_UB("v1_on_UB");
6438                 v1_on_UB.in(inst_partition);
6439                 param<type> v1_off_UB("v1_off_UB");
6440                 v1_off_UB.in(inst_partition);
6441 
6442                 //fill the parameters for the variable on-off bounds and the corresponding function values
6443                 size_t nb_ins = v1.get_nb_inst();
6444 
6445                 //collect the bounds on the variables for efficient on/off bound calculation (considering uniform partitioning in the domain)
6446                 auto v1_global_lb = v1.get_lb();
6447                 auto v1_global_ub = v1.get_ub();
6448                 auto increment = (v1_global_ub - v1_global_lb)/num_partns1;
6449 
6450                 //for every partition and instance, fill the parameter values accordingly
6451                 for (int i=0 ; i<num_partns1; ++i) {
6452                     auto LB_partn = v1_global_lb + increment*i;
6453                     auto UB_partn = LB_partn + increment;
6454                     LB_partn.eval_all();
6455                     UB_partn.eval_all();
6456                     for (size_t inst = 0; inst< nb_ins; inst++){
6457                         auto cur_var_id = v1.get_id_inst(inst);
6458                         auto cur_var_idx = var_indices._keys->at(cur_var_id);
6459                         string cur_idx = cur_var_idx+","+name1+"{"+to_string(i+1)+"}";
6460                         v1_off_LB.set_val(cur_idx,v1_global_lb.eval(inst));
6461                         v1_off_UB.set_val(cur_idx,v1_global_ub.eval(inst));
6462                         v1_on_LB.set_val(cur_idx,LB_partn.eval(inst));
6463                         v1_on_UB.set_val(cur_idx,UB_partn.eval(inst));
6464                         Vpar.set_val(cur_idx,LB_partn.eval(inst)+UB_partn.eval(inst));
6465                         Cpar.set_val(cur_idx,LB_partn.eval(inst)*UB_partn.eval(inst));
6466 
6467                     }
6468                 }
6469                 //add the secant constraint if the quadratic term appears on the LHS
6470                 if (vlift._lift_ub){
6471                 Constraint<type> MC_secant(name+"_secant");
6472                 MC_secant = vlift.from_ith(0,inst_partition) - Vpar*v1.from_ith(0,inst_partition) + Cpar;
6473                 MC_secant.in(inst_partition) <= 0;
6474                 add_on_off_multivariate_refined(MC_secant, on);
6475                 }
6476 
6477                 //add the global lb constraint if the quadratic term appears on the RHS
6478                 if (vlift._lift_lb){
6479                 Constraint<type> MC_squared(name+"_McCormick_squared");
6480                 MC_squared += vlift;
6481                 MC_squared -= v1*v1;
6482                 MC_squared >= 0;
6483                 MC_squared._relaxed = true; /* MC_squared is a relaxation of a non-convex constraint */
6484                 add(MC_squared.in(*vlift._indices));
6485                 }
6486 
6487                 //add the on-off bound constraints on the variables
6488                 Constraint<type> v1_on_off_LB(name+"_v1_on_off_LB");
6489                 v1_on_off_LB = v1.from_ith(0,inst_partition) - on*v1_on_LB - (1-on)*v1_off_LB;
6490                 v1_on_off_LB.in(inst_partition) >= 0;
6491                 add(v1_on_off_LB);
6492 
6493                 Constraint<type> v1_on_off_UB(name+"_v1_on_off_UB");
6494                 v1_on_off_UB = v1.from_ith(0,inst_partition) - on*v1_on_UB - (1-on)*v1_off_UB;
6495                 v1_on_off_UB.in(inst_partition) <= 0;
6496                 add(v1_on_off_UB);
6497 
6498             }
6499         }
6500 
6501         template<typename T=type>
6502         shared_ptr<Model<type>> buildOA();
6503 
6504         /** Returns a model such that when optimized will return an iterior point to the current model**/
6505         template<typename T=type>
6506         Model<type> build_model_interior() const;
6507 
6508         template<typename T=type>
6509         shared_ptr<Model<type>> build_model_IIS();
6510         void add_outer_app_uniform(int nb_discr, Constraint<> con);
6511         //template<typename T=type>
6512         /** Adds OA cuts for active constraits in nonlin model and using nb_perturb perturbations to generate close by cuts.*/
6513         void add_outer_app_active(const Model<type>& nonlin, int nb_perturb);
6514         Model<type> add_outer_app_solution(const Model<type>& nonlin);
6515         void add_iterative(const Model<type>& interior, vector<double>& obbt_solution, shared_ptr<Model<>> lin);
6516 
6517 
6518         //this function partitions a given SOC constraint to given number of uniform regions and construct hyperplanes in order to satisfy the SOC constraint at equality with an inner approximation as a convex relaxation (which is originally a non-convex constraint)
6519         //INPUT: an SOC constraint to satisfy at equality, number of desired partitions, another number of partitions if the original SOC constraint involves more than 3 variables (we need to seperate that into different SOC constraints), use_lambda option for using the lambda formulation to activate the hyperplanes where the default is the on/off formulation
6520         template<typename T=type>
6521         void SOC_partition(Constraint<type>& c, int num_SOC_partitions1 = 10, int num_SOC_partitions2 = 10, bool use_lambda = false) { //currently the function asssumes there are only qterms in the original SOC constraint
6522             // TODO: incorporate linear terms in the constraint, or convert those into the standard format as well!
6523 
6524             auto is_rotated_SOC = c.check_rotated_soc(); //check the constraint is either an SOC or a rotated SOC
6525             auto is_SOC = c.check_soc();
6526 
6527             if (!is_rotated_SOC && !is_SOC) // if the constraint is not an SOC, throw an error
6528             {
6529                 throw invalid_argument("SOC partition can only be applied to second-order cones! \n");
6530             }
6531             if (!c._lterms->empty()) //currently, there is no support for linear terms in the SOC constraint
6532             {
6533                 throw invalid_argument("Current SOC partition version can only handle quadratic & bilinear terms! \n");
6534             }
6535             if (c.get_ctype() == eq){ //the original constraint should be either leq or geq since we need to also have the convex relaxation of the original constraint as the upper bound on the cone itself
6536                 throw invalid_argument("Please provide the constraint in the convex format (either <= or >=), and make sure you add the convex constraint to the model as well!");
6537             }
6538             //convert the constraint into standard format if the constraint is given as a >= constraint
6539             Constraint<type> n_c(c);
6540             if (c.get_ctype() == geq){
6541                 n_c *= -1;
6542                 n_c._ctype = leq;
6543             }
6544 
6545             //create the variables for bound calculations on the auxiliary variable for the case when there are more than 3 terms in the given SOC constraint
6546             var<> v1;
6547             var<> v2;
6548 
6549             if (is_SOC) //if the original constraint is an SOC
6550             {
6551                 unsigned num_qterms = n_c._qterms->size();
6552                 if (num_qterms >= 4){ //we need to split the constraint into two
6553                     if (num_qterms > 4){ //current version do not support more than 4 quadratic terms in the constraint
6554                         //TODO: create a generic scheme to further divide the constraint into smaller constraints and add them recursively
6555                         throw invalid_argument("Current SOC partition version can only up to four quadratic terms! \n");
6556                     }
6557                     //create the two constraints for the divided version of the original constraint
6558                     Constraint<type> SOC_1(n_c._name + "_SOC_1"); //to split the constraint (first half)
6559                     Constraint<type> SOC_2(n_c._name + "_SOC_2"); //to split the constraint (second half)
6560 
6561                     //in order to decide which constraint +t^2 should be added (the auxiliary variable to connect the separated SOC constraints)
6562                     bool first = false;
6563                     auto aux_idx = *n_c._indices;
6564 
6565                     //flag for assignment when assigning the variable pointers
6566                     bool first_occupied = false;
6567 
6568                     //go over the qterms
6569                     unsigned counter = 0; // counter is for dividing the constraint into two with 2 quadratic terms each
6570                     for (auto &qt_pair: *n_c._qterms) {
6571                         if (!qt_pair.second._p->first->is_double()) {
6572                             throw invalid_argument("Current hyperplanes only support double type variables!");
6573                         }
6574                         if (!qt_pair.second._coef->is_number()) { /*means coef is not a number*/
6575                             throw invalid_argument("Current hyperplanes only support constant coefficients for the variables");
6576                         }
6577                         auto sign = qt_pair.second._sign;
6578                         auto coef = static_pointer_cast<constant<type>>(qt_pair.second._coef);
6579                         if (counter < 2) {
6580                             SOC_1.insert(qt_pair.second);
6581                             if (sign ^ (coef->is_positive())) { //update the first to true if the variable appearing has a negative sign
6582                                 first = true;
6583                             }
6584                         }
6585                         else SOC_2.insert(qt_pair.second);
6586                         ++counter;
6587                     }
6588 
6589                     //TODO: remove the ranges of extra terms in SOC_1 and SOC_2
6590                     //set the ranges of the functions properly
6591                     SOC_1._range = n_c._range;
6592                     SOC_2._range = n_c._range;
6593 
6594                     if (first){ //if the t^2 is added to the first constraint, colect the other variables' pointers to obtain proper bounds for t
6595                         for (auto &qt_pair: *SOC_2._qterms) {
6596                             if (!first_occupied){
6597                                 v1 = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
6598                                 first_occupied = true;
6599                             }
6600                             else v2 = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
6601                         }
6602                     }
6603                     else{
6604                         for (auto &qt_pair: *SOC_1._qterms) {
6605                             if (!first_occupied){
6606                                 v1 = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
6607                                 first_occupied = true;
6608                             }
6609                             else v2 = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
6610                         }
6611                     }
6612 
6613                     //update the upper bound of t properly based on the bounds on the other variables appering the reduced SOC constraint
6614                     param<type> ub("ub");
6615                     ub.in(aux_idx);
6616 
6617                     //go over all the instances and calculate the upper bound for t
6618                     for (int i=0; i<aux_idx.size(); i++) {
6619                         //calculate all the possibilities and assign the worst case
6620                         size_t id1;
6621                         size_t id2;
6622 
6623                         if(v1._indices->_ids == nullptr){
6624                             id1 = i;
6625                         }
6626                         else id1 = v1._indices->_ids->at(0).at(i);
6627                         auto key1 = v1._indices->_keys->at(id1);
6628 
6629                         if(v2._indices->_ids == nullptr){
6630                             id2 = i;
6631                         }
6632                         else id2 = v2._indices->_ids->at(0).at(i);
6633                         auto key2 = v2._indices->_keys->at(id2);
6634 
6635                         //the largest possible values for the squared variables
6636                         auto prod_b1 = std::max(v1.get_lb(key1)*v1.get_lb(key1), v1.get_ub(key1)*v1.get_ub(key1));
6637                         auto prod_b2 = std::max(v2.get_lb(key2)*v2.get_lb(key2), v2.get_ub(key2)*v2.get_ub(key2));
6638 
6639                         size_t id3;
6640                         if(aux_idx._ids == nullptr){
6641                             id3 = i;
6642                         }
6643                         else id3 = aux_idx._ids->at(0).at(i);
6644                         auto key3 = aux_idx._keys->at(id3);
6645 
6646                         ub.set_val(key3, std::sqrt(prod_b1+prod_b2)); //since it is a cone constraint t can be at most this
6647                     }
6648 
6649                     var<type> t("t_" + n_c._name, 0, ub); //create the auxilary variable
6650                     add(t.in(aux_idx));
6651                     //TODO: consider the case where there can be multiple negative terms!
6652                     if (first) { //add constraints accordingly
6653                         SOC_1 += pow(t.in(aux_idx),2);
6654                         add(SOC_1.in(aux_idx) <= 0);
6655 
6656                         SOC_2 -= pow(t.in(aux_idx),2);
6657                         add(SOC_2.in(aux_idx) <= 0);
6658                     }
6659                     else{
6660                         SOC_1 -= pow(t.in(aux_idx),2);
6661                         add(SOC_1.in(aux_idx) <= 0);
6662 
6663                         SOC_2 += pow(t.in(aux_idx),2);
6664                         add(SOC_2.in(aux_idx) <= 0);
6665                     }
6666 
6667                     //call the hyperplane function to generate the disjunctive union of hyperplanes, if use_lambda is specified use the lambda formulation, otherwise use the on_off formulation
6668                     if(use_lambda){
6669                         add_lambda_SOC_hyperplanes_symmetric(SOC_1, num_SOC_partitions1);
6670                         add_lambda_SOC_hyperplanes_symmetric(SOC_2, num_SOC_partitions2);
6671                     }
6672                     else{
6673                     add_on_off_SOC_hyperplanes(SOC_1, num_SOC_partitions1);
6674                     add_on_off_SOC_hyperplanes(SOC_2, num_SOC_partitions2);
6675                     }
6676                 }
6677                 else { //if the number of qterms is less than 4, we can directly generate the hyperplanes
6678                     if(use_lambda){
6679                         add_lambda_SOC_hyperplanes_symmetric(n_c, num_SOC_partitions1); //this uses the first int as the number of partitions not the total one
6680                     }
6681                     else{
6682                         add_on_off_SOC_hyperplanes(n_c, num_SOC_partitions1); //this uses the first int as the number of partitions not the total one
6683                     }
6684                 }
6685             }
6686             else { //means the original constraint is a rotated SOC (then we should take care of the bilinear terms)
6687 
6688                 unsigned num_qterms = 0;
6689                 unsigned num_blnterms = 0;
6690                 for (auto &qt_pair: *n_c._qterms) { //go over the terms and count the standardized number of quadratic terms (which means a bilinear term will have 2 terms since it can be standardized with two quadratic terms)
6691                     if (qt_pair.second._p->first!=qt_pair.second._p->second) ++num_blnterms;
6692                     else ++num_qterms;
6693                 }
6694                 if (num_blnterms > 1){ //current procedure does not allow two bilinear terms
6695                     throw invalid_argument("Current SOC partition version can only allow one bilinear term! \n");
6696                 }
6697                 if (num_qterms + 2*num_blnterms >= 4){ //we need to split the constraint into two
6698 
6699                     if (num_qterms + 2*num_blnterms > 4){ //current procedure does not allow more than 4 standard quadratic terms
6700                         throw invalid_argument("Current SOC partition version can only up to four quadratic terms! \n");
6701                     }
6702                     //create the individual SOC constraint for the separated halves
6703                     Constraint<type> SOC_1(n_c._name + "_SOC_1"); //to split the constraint (first half)
6704                     Constraint<type> SOC_2(n_c._name + "_SOC_2"); //to split the constraint (second half)
6705 
6706                     auto aux_idx = *n_c._indices; /*** use this or n_c._indices->_keys->at(inst)? ***/
6707 
6708                     //flag for assignment when assigning the variable pointers
6709                     bool first_occupied = false;
6710 
6711                     //go over the qterms
6712                     for (auto &qt_pair: *n_c._qterms) {
6713                         if (!qt_pair.second._p->first->is_double()) {
6714                             throw invalid_argument("Current hyperplanes only support double type variables!");
6715                         }
6716                         if (qt_pair.second._p->first!=qt_pair.second._p->second){ //if the term is bilinear insert that into the first constraint by default
6717                             SOC_1.insert(qt_pair.second);
6718                         }
6719                         else { //if the term is quadratic, automatically insert that into the second part, and get the variables for calculating the bound of the auxiliary variable t
6720                             if (!first_occupied) {
6721                                 v1 = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
6722                                 first_occupied = true;
6723                             }
6724                             else {
6725                                 v2 = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
6726                             }
6727                             SOC_2.insert(qt_pair.second);
6728                         }
6729                     }
6730                     //update the range of the functions appropriately
6731                     //TODO: remove the ranges of extra terms in SOC_1 and SOC_2
6732                     SOC_1._range = n_c._range;
6733                     SOC_2._range = n_c._range;
6734 
6735 
6736                     //create the parameter for the upper bound of t
6737                     param<type> ub("ub");
6738                     ub.in(aux_idx);
6739 
6740                     //go over all the instances and calculate the best possible bound for the aux variable
6741                     for (int i=0; i<aux_idx.size(); i++) {
6742                         //calculate all the possibilities and assign the worst case
6743                         size_t id1;
6744                         size_t id2;
6745 
6746                         if(v1._indices->_ids == nullptr){
6747                             id1 = i;
6748                         }
6749                         else id1 = v1._indices->_ids->at(0).at(i);
6750                         auto key1 = v1._indices->_keys->at(id1);
6751 
6752                         if(v2._indices->_ids == nullptr){
6753                             id2 = i;
6754                         }
6755                         else id2 = v2._indices->_ids->at(0).at(i);
6756                         auto key2 = v2._indices->_keys->at(id2);
6757 
6758                         //these terms are the largest possible values that the squared variables can take
6759                         auto prod_b1 = std::max(v1.get_lb(key1)*v1.get_lb(key1), v1.get_ub(key1)*v1.get_ub(key1));
6760                         auto prod_b2 = std::max(v2.get_lb(key2)*v2.get_lb(key2), v2.get_ub(key2)*v2.get_ub(key2));
6761 
6762                         size_t id3;
6763                         if(aux_idx._ids == nullptr){
6764                             id3 = i;
6765                         }
6766                         else id3 = aux_idx._ids->at(0).at(i);
6767                         auto key3 = aux_idx._keys->at(id3);
6768 
6769                         ub.set_val(key3, std::sqrt(prod_b1+prod_b2)); //since the constraint is SOC, this is the maximum value t can take
6770                     }
6771 
6772                     var<type> t("t_" + n_c._name, 0, ub); //create the auxilary variable
6773                     add(t.in(aux_idx));
6774                     //TODO: consider the case where there can be multiple negative terms!
6775                     //add constraints accordingly with including the auxiliary term
6776                     SOC_1 += pow(t.in(aux_idx),2);
6777                     add(SOC_1.in(aux_idx) <= 0);
6778 
6779                     SOC_2 -= pow(t.in(aux_idx),2);
6780                     add(SOC_2.in(aux_idx) <= 0);
6781 
6782                     //call the hyperplane function to generate the disjunctive union of hyperplanes (either with using lambda formulation or on/off formulation)
6783                     if(use_lambda){
6784                         add_lambda_SOC_hyperplanes_symmetric(SOC_1,num_SOC_partitions1);
6785                         add_lambda_SOC_hyperplanes_symmetric(SOC_2,num_SOC_partitions2);
6786                     }
6787                     else{
6788                     add_on_off_SOC_hyperplanes(SOC_1,num_SOC_partitions1);
6789                     add_on_off_SOC_hyperplanes(SOC_2,num_SOC_partitions2);
6790                     }
6791                 }
6792 
6793 
6794                 else{ //if the total number of standard terms are less than 4, we can directly generate the hyperplanes by standardizing the bilinear term
6795                     if(use_lambda){
6796                         add_lambda_SOC_hyperplanes_symmetric(n_c,num_SOC_partitions1);
6797                     }
6798                     else{
6799                         add_on_off_SOC_hyperplanes(n_c,num_SOC_partitions1);
6800                     }
6801                 }
6802             }
6803         }
6804 
6805 
6806 
6807         // INPUT: an SOC type constraint, and total number of binary variables
6808         // OUTPUT: disjunctive union of hyperplanes as an inner approximation to the SOC, where the disjunctive union is made by lambda formulation
6809         // IMPORTANT NOTE: we also utilize the symmetric nature of the formulation to make the formulation more efficient
add_lambda_SOC_hyperplanes_symmetric(Constraint<type> & c,int num_SOC_partitions)6810         void add_lambda_SOC_hyperplanes_symmetric(Constraint<type>& c, int num_SOC_partitions){ //currently this is not fully correct
6811             //TODO: scale the coefficients properly
6812 
6813             DebugOn("SOC_hyperplane function!" << endl);
6814             c.print();
6815 
6816             auto is_rotated_SOC = c.check_rotated_soc(); //collect the information about the cone
6817             auto is_SOC = c.check_soc();
6818 
6819             //create hyperplane indices
6820             indices hyper_idx("hyper_idx");
6821             for (int i=0; i<num_SOC_partitions; ++i) {
6822                 hyper_idx.add(to_string(i+1));
6823             }
6824             //get the combined index set
6825             auto inst_hyper = indices(*c._indices,hyper_idx);
6826 
6827             //create lambda indices
6828             indices lambda_idx("lambda_idx");
6829             for (int i=0; i<num_SOC_partitions+2; ++i) {
6830                 lambda_idx.add(to_string(i+1));
6831             }
6832             //get the combined index set
6833             auto inst_lambda = indices(*c._indices,lambda_idx);
6834 
6835             //create on_link_lambda indices
6836             indices on_link_lambda_idx("on_link_lambda_idx");
6837             for (int i=0; i<num_SOC_partitions+1; ++i) {
6838                 on_link_lambda_idx.add(to_string(i+1));
6839             }
6840 
6841             Constraint<type> v1_rep_pos(c._name + "_v1_rep_pos"); //create the v1_rep constraint (positive side)
6842             Constraint<type> v1_rep_neg(c._name + "_v1_rep_neg"); //create the v1_rep constraint (negative side)
6843             Constraint<type> v2_rep_pos(c._name + "_v2_rep_pos"); //create the v2_rep constraint (positive side)
6844             Constraint<type> v2_rep_neg(c._name + "_v2_rep_neg"); //create the v2_rep constraint (negative side)
6845             Constraint<type> v3_rep(c._name + "_v3_rep"); //create the v3_rep constraint
6846             var<int> on(c._name + "_binary",0,1); //create the partition variable
6847             Constraint<type> onSum(c._name + "_binarySum"); //create the partition assignment constraint
6848             var<> lambda(c._name + "_lambda",0,1); //create the lambda variable
6849             Constraint<type> lambdaSum(c._name + "_lambdaSum"); //create the lambda constraint
6850             Constraint<type> on_link_lambda(c._name + "_on_link_lambda"); // lambda linking with the partition variables
6851             var<int> v1_sign(c._name + "_v1_sign",0,1); //create the sign indicator for v1 (1 if negative 0 positive)
6852             var<int> v2_sign(c._name + "_v2_sign",0,1); //create the sign indicator for v2 (1 if negative 0 positive)
6853 
6854             if (is_SOC){ //this will follow the standard creation of the hyperplane
6855 
6856                 //create the variables
6857                 var<> lhs_first_var;
6858                 var<> lhs_second_var;
6859                 var<> rhs_var;
6860 
6861                 //create the scaling factors for the variables
6862                 double lhs_first_scale;
6863                 double lhs_second_scale;
6864                 double rhs_scale;
6865 
6866                 //flag for assignment
6867                 bool first_occupied = false;
6868 
6869                 //go over the quadratic terms and collect the variables and their multipliers (scales)
6870                 for (auto &qt_pair: *c._qterms) {
6871                     if (!qt_pair.second._p->first->is_double()) {
6872                         throw invalid_argument("Current hyperplanes only support double type variables!");
6873                     }
6874                     if (!qt_pair.second._coef->is_number()) { /*means coef is not a number*/
6875                         throw invalid_argument("Current hyperplanes only support constant coefficients for the variables");
6876                     }
6877                     auto sign = qt_pair.second._sign;
6878                     auto coef = static_pointer_cast<constant<type>>(qt_pair.second._coef);
6879                     if (sign ^ (coef->is_negative())) {
6880                         if (!first_occupied){
6881                             lhs_first_var = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
6882                             if(coef->is_positive()) lhs_first_scale = std::sqrt(coef->eval());
6883                             else lhs_first_scale = std::sqrt((-1)*coef->eval());
6884                             first_occupied = true;
6885                         }
6886                         else{
6887                             lhs_second_var = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
6888                             if(coef->is_positive()) lhs_second_scale = std::sqrt(coef->eval());
6889                             else lhs_second_scale = std::sqrt((-1)*coef->eval());
6890 
6891                         }
6892                     }
6893                     else{
6894                         rhs_var = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
6895                         if(coef->is_positive()) rhs_scale = std::sqrt(coef->eval());
6896                         else rhs_scale = std::sqrt((-1)*coef->eval());
6897                     }
6898                 }
6899 
6900                 //combine all the indices
6901                 //How to combine safely when the index sets are same? ***********************************************************************************************************************
6902                 auto inst_combined  = combine(*lhs_first_var._indices,*lhs_second_var._indices,*rhs_var._indices);
6903                 auto inst_combined_partn = indices(inst_combined, hyper_idx);
6904                 auto inst_combined_lambda = indices(inst_combined, lambda_idx);
6905 
6906                 //collect the number of entries
6907                 auto nb_entries_v1 = lhs_first_var._indices->get_nb_entries();
6908                 auto nb_entries_v2 = lhs_second_var._indices->get_nb_entries();
6909                 auto nb_entries_v3 = rhs_var._indices->get_nb_entries();
6910 
6911                 //create the multipliers for the hyperplane
6912                 param<double> v1_coef("v1_coef");
6913                 param<double> v2_coef("v2_coef");
6914                 param<double> v3_coef("v3_coef");
6915 
6916                 v1_coef.in(inst_combined_lambda);
6917                 v2_coef.in(inst_combined_lambda);
6918                 v3_coef.in(inst_combined_lambda);
6919 
6920                 // Lambda coefficient matrix when linking with partition variables
6921                 param<> lambda_coef(c._name+"_lambda_linking_coefficients");
6922                 // Partition coefficient matrix when linking with lambda variables
6923                 param<> on_coef(c._name+"_partition_linking_coefficients");
6924 
6925                 // we can use constraint_idx as the hyper_idx since there will be exactly |num_SOC_partitions| many linking constraints
6926                 lambda_coef.in(indices(inst_combined_lambda, on_link_lambda_idx));
6927                 on_coef.in(indices(inst_combined_partn, on_link_lambda_idx));
6928 
6929                 //on variable constraint and definition
6930                 add(on.in(inst_combined_partn));
6931 
6932                 //create the summation constraint for partition
6933                 onSum = sum(on.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1));
6934                 add(onSum.in(*c._indices) == 1);
6935 
6936                 //lambda variable constraint and definition
6937                 add(lambda.in(inst_combined_lambda));
6938 
6939                 //create the summation constraint for partition
6940                 lambdaSum = sum(lambda.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1));
6941                 add(lambdaSum.in(*c._indices) == 1);
6942 
6943                 add(v1_sign.in(inst_combined));
6944                 add(v2_sign.in(inst_combined));
6945 
6946                 size_t nb_ins = lhs_first_var.get_nb_inst(); //get the number of instances
6947 
6948                 // fill lambda_coef
6949                 for (size_t inst = 0; inst< nb_ins; inst++){
6950                     // lhs_first
6951                     auto cur_var_id_lhs_first = lhs_first_var.get_id_inst(inst);
6952                     auto cur_var_idx_lhs_first = lhs_first_var._indices->_keys->at(cur_var_id_lhs_first);
6953 
6954                     // lhs_second
6955                     auto cur_var_id_lhs_second = lhs_second_var.get_id_inst(inst);
6956                     auto cur_var_idx_lhs_second = lhs_second_var._indices->_keys->at(cur_var_id_lhs_second);
6957 
6958                     // rhs
6959                     auto cur_var_id_rhs = rhs_var.get_id_inst(inst);
6960                     auto cur_var_idx_rhs = rhs_var._indices->_keys->at(cur_var_id_rhs);
6961 
6962                     //for constraints
6963                     for (int i=0 ; i<num_SOC_partitions+1; ++i) {
6964                         string cur_idx = cur_var_idx_lhs_first+","+cur_var_idx_lhs_second+","+cur_var_idx_rhs+","+to_string(i+2)+","+to_string(i+1);
6965                         lambda_coef.set_val(cur_idx,1);
6966                     }
6967                 }
6968 
6969                 // fill on_coef
6970                 for (size_t inst = 0; inst< nb_ins; inst++){
6971                     // lhs_first
6972                     auto cur_var_id_lhs_first = lhs_first_var.get_id_inst(inst);
6973                     auto cur_var_idx_lhs_first = lhs_first_var._indices->_keys->at(cur_var_id_lhs_first);
6974 
6975                     // lhs_second
6976                     auto cur_var_id_lhs_second = lhs_second_var.get_id_inst(inst);
6977                     auto cur_var_idx_lhs_second = lhs_second_var._indices->_keys->at(cur_var_id_lhs_second);
6978 
6979                     // rhs
6980                     auto cur_var_id_rhs = rhs_var.get_id_inst(inst);
6981                     auto cur_var_idx_rhs = rhs_var._indices->_keys->at(cur_var_id_rhs);
6982 
6983                     //first and last constraint
6984                     string cur_idx = cur_var_idx_lhs_first+","+cur_var_idx_lhs_second+","+cur_var_idx_rhs+","+to_string(1)+","+to_string(1);
6985                     on_coef.set_val(cur_idx,1);
6986                     cur_idx = cur_var_idx_lhs_first+","+cur_var_idx_lhs_second+","+cur_var_idx_rhs+","+to_string(num_SOC_partitions)+","+to_string(num_SOC_partitions+1);
6987                     on_coef.set_val(cur_idx,1);
6988 
6989                     //for constraints
6990                     for (int i=0 ; i<num_SOC_partitions-1; ++i) {
6991                         cur_idx = cur_var_idx_lhs_first+","+cur_var_idx_lhs_second+","+cur_var_idx_rhs+","+to_string(i+1)+","+to_string(i+2);
6992                         on_coef.set_val(cur_idx,1);
6993                         cur_idx = cur_var_idx_lhs_first+","+cur_var_idx_lhs_second+","+cur_var_idx_rhs+","+to_string(i+2)+","+to_string(i+2);
6994                         on_coef.set_val(cur_idx,1);
6995                     }
6996                 }
6997 
6998                 // add the link constraint between partition variables and lambda variables
6999                 on_link_lambda = lambda.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1).from_ith(0,lambda_coef.get_matrix_ids(nb_entries_v1+nb_entries_v2+nb_entries_v3,1))*lambda_coef.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1) - on.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1).from_ith(0,on_coef.get_matrix_ids(nb_entries_v1+nb_entries_v2+nb_entries_v3,1)) * on_coef.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1);
7000                 add(on_link_lambda.in(indices(*c._indices,on_link_lambda_idx)) <= 0);
7001 
7002 
7003                 // fill lambda representation coefficients
7004                 for (size_t inst = 0; inst< nb_ins; inst++){
7005 
7006                     size_t id1;
7007                     if(lhs_first_var._indices->_ids == nullptr){
7008                         id1 = inst;
7009                     }
7010                     else id1 = lhs_first_var._indices->_ids->at(0).at(inst);
7011                     auto key1 = lhs_first_var._indices->_keys->at(id1);
7012 
7013                     size_t id2;
7014                     if(lhs_second_var._indices->_ids == nullptr){
7015                         id2 = inst;
7016                     }
7017                     else id2 = lhs_second_var._indices->_ids->at(0).at(inst);
7018                     auto key2 = lhs_second_var._indices->_keys->at(id2);
7019 
7020                     size_t id3;
7021                     if(rhs_var._indices->_ids == nullptr){
7022                         id3 = inst;
7023                     }
7024                     else id3 = rhs_var._indices->_ids->at(0).at(inst);
7025                     auto key3 = rhs_var._indices->_keys->at(id3);
7026 
7027                     double radius1 = std::max(std::abs(lhs_first_var.get_lb(key1)), std::abs(lhs_first_var.get_ub(key1)))*lhs_first_scale;
7028                     double radius2 = std::max(std::abs(lhs_second_var.get_lb(key2)), std::abs(lhs_first_var.get_ub(key2)))*lhs_second_scale;
7029                     double radius3 = rhs_var.get_ub(key3)*rhs_scale;
7030 
7031                     double radius = std::min( std::sqrt(std::pow(radius1,2)+std::pow(radius2,2)), radius3);
7032 
7033                     // lhs_first
7034                     auto cur_var_id_lhs_first = lhs_first_var.get_id_inst(inst);
7035                     auto cur_var_idx_lhs_first = lhs_first_var._indices->_keys->at(cur_var_id_lhs_first);
7036 
7037                     // lhs_second
7038                     auto cur_var_id_lhs_second = lhs_second_var.get_id_inst(inst);
7039                     auto cur_var_idx_lhs_second = lhs_second_var._indices->_keys->at(cur_var_id_lhs_second);
7040 
7041                     // rhs
7042                     auto cur_var_id_rhs = rhs_var.get_id_inst(inst);
7043                     auto cur_var_idx_rhs = rhs_var._indices->_keys->at(cur_var_id_rhs);
7044 
7045                     string cur_idx = cur_var_idx_lhs_first+","+cur_var_idx_lhs_second+","+cur_var_idx_rhs+","+to_string(1);
7046                     v1_coef.set_val(cur_idx,0);
7047                     v2_coef.set_val(cur_idx,0);
7048                     v3_coef.set_val(cur_idx,0);
7049 
7050                     for (int i=0 ; i<num_SOC_partitions+1; ++i) {
7051                         //calculate the hyperplane coefficients
7052                         auto v1_val = radius*std::cos(M_PI*i/(2*num_SOC_partitions));
7053                         auto v2_val = radius*std::sin(M_PI*i/(2*num_SOC_partitions));
7054                         auto v3_val = radius;
7055                         cur_idx = cur_var_idx_lhs_first+","+cur_var_idx_lhs_second+","+cur_var_idx_rhs+","+to_string(i+2);
7056                         v1_coef.set_val(cur_idx,v1_val/lhs_first_scale);
7057                         v2_coef.set_val(cur_idx,v2_val/lhs_second_scale);
7058                         v3_coef.set_val(cur_idx,v3_val/rhs_scale);
7059                     }
7060                 }
7061 
7062                 //add the constraints involving the individual variables (tying lambda to the variables as lower and upper bounds)
7063                 v1_rep_pos = lhs_first_var + 100 * v1_sign - v1_coef.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1) * lambda.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1);
7064                 v1_rep_pos.in(*c._indices) >= 0;
7065                 add(v1_rep_pos);
7066 
7067                 v1_rep_neg = lhs_first_var - 100 * (1 - v1_sign) + v1_coef.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1) * lambda.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1);
7068                 v1_rep_neg.in(*c._indices) <= 0;
7069                 add(v1_rep_neg);
7070 
7071                 v2_rep_pos = lhs_second_var + 100 * v2_sign - v2_coef.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1) * lambda.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1);
7072                 v2_rep_pos.in(*c._indices) >= 0;
7073                 add(v2_rep_pos);
7074 
7075                 v2_rep_neg = lhs_second_var - 100 * (1 - v2_sign) + v2_coef.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1) * lambda.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1);
7076                 v2_rep_neg.in(*c._indices) <= 0;
7077                 add(v2_rep_neg);
7078 
7079                 //this is the variable on the rhs
7080                 v3_rep = rhs_var - v3_coef.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1) * lambda.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1);
7081                 v3_rep.in(*c._indices) <= 0;
7082                 add(v3_rep);
7083             }
7084 
7085             else if (is_rotated_SOC){ //this will follow bilinear scheme
7086 
7087                 //create the variables
7088                 var<> quad_var;
7089                 var<> bln_first_var;
7090                 var<> bln_second_var;
7091 
7092                 //create the scaling factors for the variables
7093                 double quad_scale;
7094                 double bln_scale;
7095 
7096                 for (auto &qt_pair: *c._qterms) {
7097                     if (!qt_pair.second._p->first->is_double()) {
7098                         throw invalid_argument("Current hyperplanes only support double type variables!");
7099                     }
7100                     if (!qt_pair.second._coef->is_number()) { /*means coef is not a number*/
7101                         throw invalid_argument("Current hyperplanes only support constant coefficients for the variables");
7102                     }
7103                     if (qt_pair.second._p->first!=qt_pair.second._p->second) {
7104                         if (!qt_pair.second._p->second->is_double()) {
7105                             throw invalid_argument("Current hyperplanes only support double type variables!");
7106                         }
7107                         //we do not check the sign of the bilinear term, since current assumption is that the sign is negative
7108                         bln_first_var = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
7109                         bln_second_var = *static_pointer_cast<var<double>>(qt_pair.second._p->second);
7110                         auto coef = static_pointer_cast<constant<type>>(qt_pair.second._coef);
7111                         if(coef->is_positive()) bln_scale = std::sqrt(coef->eval())/2; //dividing to two since the standard form has 1/4 as the multiplier
7112                         else bln_scale = std::sqrt((-1)*coef->eval())/(2);
7113                     }
7114                     else{
7115                         quad_var = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
7116                         auto coef = static_pointer_cast<constant<type>>(qt_pair.second._coef);
7117                         if(coef->is_positive()) quad_scale = std::sqrt(coef->eval());
7118                         else quad_scale = std::sqrt((-1)*coef->eval());
7119                     }
7120                 }
7121 
7122                 //combine all the indices
7123                 auto inst_combined  = combine(*quad_var._indices,*bln_first_var._indices,*bln_second_var._indices);
7124                 auto inst_combined_partn = indices(inst_combined,hyper_idx);
7125                 auto inst_combined_lambda = indices(inst_combined, lambda_idx);
7126 
7127                 //collect the number of entries
7128                 auto nb_entries_v1 = quad_var._indices->get_nb_entries();
7129                 auto nb_entries_v2 = bln_first_var._indices->get_nb_entries();
7130                 auto nb_entries_v3 = bln_second_var._indices->get_nb_entries();
7131 
7132                 //create the multipliers for the hyperplane
7133                 param<double> quad_coef("quad_coef");
7134                 param<double> first_minus_second_coef("first_minus_second_coef");
7135                 param<double> first_plus_second_coef("first_plus_second_coef");
7136 
7137                 quad_coef.in(inst_combined_lambda);
7138                 first_minus_second_coef.in(inst_combined_lambda);
7139                 first_plus_second_coef.in(inst_combined_lambda);
7140 
7141                 // Lambda coefficient matrix when linking with partition variables
7142                 param<> lambda_coef(c._name+"_lambda_linking_coefficients");
7143                 // Partition coefficient matrix when linking with lambda variables
7144                 param<> on_coef(c._name+"_partition_linking_coefficients");
7145 
7146                 // we can use constraint_idx as the hyper_idx since there will be exactly |num_SOC_partitions| many linking constraints
7147                 lambda_coef.in(indices(inst_combined_lambda, on_link_lambda_idx));
7148                 on_coef.in(indices(inst_combined_partn, on_link_lambda_idx));
7149 
7150                 //on variable constraint and definition
7151                 add(on.in(inst_combined_partn));
7152 
7153                 //create the summation constraint for partition
7154                 onSum = sum(on.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1));
7155                 add(onSum.in(*c._indices) == 1);
7156 
7157                 //lambda variable constraint and definition
7158                 add(lambda.in(inst_combined_lambda));
7159 
7160                 //create the summation constraint for partition
7161                 lambdaSum = sum(lambda.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1));
7162                 add(lambdaSum.in(*c._indices) == 1);
7163 
7164                 add(v1_sign.in(inst_combined));
7165                 add(v2_sign.in(inst_combined));
7166 
7167                 size_t nb_ins = quad_var.get_nb_inst(); //get the number of instances
7168 
7169                 // fill lambda_coef
7170                 for (size_t inst = 0; inst< nb_ins; inst++){
7171                     // quad
7172                     auto cur_var_id_quad = quad_var.get_id_inst(inst);
7173                     auto cur_var_idx_quad = quad_var._indices->_keys->at(cur_var_id_quad);
7174 
7175                     // bln_first
7176                     auto cur_var_id_bln_first = bln_first_var.get_id_inst(inst);
7177                     auto cur_var_idx_bln_first = bln_first_var._indices->_keys->at(cur_var_id_bln_first);
7178 
7179                     // bln_second
7180                     auto cur_var_id_bln_second = bln_second_var.get_id_inst(inst);
7181                     auto cur_var_idx_bln_second = bln_second_var._indices->_keys->at(cur_var_id_bln_second);
7182 
7183                     //for constraints
7184                     for (int i=0 ; i<num_SOC_partitions+1; ++i) {
7185                         string cur_idx = cur_var_idx_quad+","+cur_var_idx_bln_first+","+cur_var_idx_bln_second+","+to_string(i+2)+","+to_string(i+1);
7186                         lambda_coef.set_val(cur_idx,1);
7187                     }
7188                 }
7189 
7190                 // fill on_coef
7191                 for (size_t inst = 0; inst< nb_ins; inst++){
7192                     // quad
7193                     auto cur_var_id_quad = quad_var.get_id_inst(inst);
7194                     auto cur_var_idx_quad = quad_var._indices->_keys->at(cur_var_id_quad);
7195 
7196                     // bln_first
7197                     auto cur_var_id_bln_first = bln_first_var.get_id_inst(inst);
7198                     auto cur_var_idx_bln_first = bln_first_var._indices->_keys->at(cur_var_id_bln_first);
7199 
7200                     // bln_second
7201                     auto cur_var_id_bln_second = bln_second_var.get_id_inst(inst);
7202                     auto cur_var_idx_bln_second = bln_second_var._indices->_keys->at(cur_var_id_bln_second);
7203 
7204                     //first and last constraints
7205                     string cur_idx = cur_var_idx_quad+","+cur_var_idx_bln_first+","+cur_var_idx_bln_second+","+to_string(1)+","+to_string(1);
7206                     on_coef.set_val(cur_idx,1);
7207                     cur_idx = cur_var_idx_quad+","+cur_var_idx_bln_first+","+cur_var_idx_bln_second+","+to_string(num_SOC_partitions)+","+to_string(num_SOC_partitions+1);
7208                     on_coef.set_val(cur_idx,1);
7209 
7210                     //for constraints
7211                     for (int i=0 ; i<num_SOC_partitions-1; ++i) {
7212                         cur_idx = cur_var_idx_quad+","+cur_var_idx_bln_first+","+cur_var_idx_bln_second+","+to_string(i+1)+","+to_string(i+2);
7213                         on_coef.set_val(cur_idx,1);
7214                         cur_idx = cur_var_idx_quad+","+cur_var_idx_bln_first+","+cur_var_idx_bln_second+","+to_string(i+2)+","+to_string(i+2);
7215                         on_coef.set_val(cur_idx,1);
7216                     }
7217                 }
7218 
7219                 // add the link constraint between partition variables and lambda variables
7220                 on_link_lambda = lambda.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1).from_ith(0,lambda_coef.get_matrix_ids(nb_entries_v1+nb_entries_v2+nb_entries_v3,1))*lambda_coef.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1) - on.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1).from_ith(0,on_coef.get_matrix_ids(nb_entries_v1+nb_entries_v2+nb_entries_v3,1)) * on_coef.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1);
7221                 add(on_link_lambda.in(indices(*c._indices,on_link_lambda_idx)) <= 0);
7222 
7223                 // fill lambda representation coefficients
7224                 for (size_t inst = 0; inst< nb_ins; inst++){
7225 
7226                     size_t id1;
7227                     if(quad_var._indices->_ids == nullptr){
7228                         id1 = inst;
7229                     }
7230                     else id1 = quad_var._indices->_ids->at(0).at(inst);
7231                     auto key1 = quad_var._indices->_keys->at(id1);
7232 
7233                     size_t id2;
7234                     if(bln_first_var._indices->_ids == nullptr){
7235                         id2 = inst;
7236                     }
7237                     else id2 = bln_first_var._indices->_ids->at(0).at(inst);
7238                     auto key2 = bln_first_var._indices->_keys->at(id2);
7239 
7240                     size_t id3;
7241                     if(bln_second_var._indices->_ids == nullptr){
7242                         id3 = inst;
7243                     }
7244                     else id3 = bln_second_var._indices->_ids->at(0).at(inst);
7245                     auto key3 = bln_second_var._indices->_keys->at(id3);
7246 
7247                     double radius1 = std::max(std::abs(quad_var.get_lb(key1)), std::abs(quad_var.get_ub(key1)))*quad_scale;
7248                     double radius2 = std::max(std::abs(bln_first_var.get_lb(key2) - bln_second_var.get_ub(key3)), std::abs(bln_first_var.get_ub(key2) - bln_second_var.get_lb(key3)))*bln_scale;
7249                     double radius3 = (bln_first_var.get_ub(key2) + bln_second_var.get_ub(key3))*bln_scale;
7250 
7251                     double radius = std::min( std::sqrt(std::pow(radius1,2)+std::pow(radius2,2)), radius3);
7252 
7253                     // quad
7254                     auto cur_var_id_quad = quad_var.get_id_inst(inst);
7255                     auto cur_var_idx_quad = quad_var._indices->_keys->at(cur_var_id_quad);
7256 
7257                     // bln_first
7258                     auto cur_var_id_bln_first = bln_first_var.get_id_inst(inst);
7259                     auto cur_var_idx_bln_first = bln_first_var._indices->_keys->at(cur_var_id_bln_first);
7260 
7261                     // bln_second
7262                     auto cur_var_id_bln_second = bln_second_var.get_id_inst(inst);
7263                     auto cur_var_idx_bln_second = bln_second_var._indices->_keys->at(cur_var_id_bln_second);
7264 
7265                     string cur_idx = cur_var_idx_quad+","+cur_var_idx_bln_first+","+cur_var_idx_bln_second+","+to_string(1);
7266                     quad_coef.set_val(cur_idx,0);
7267                     first_minus_second_coef.set_val(cur_idx,0);
7268                     first_plus_second_coef.set_val(cur_idx,0);
7269 
7270                     for (int i=0 ; i<num_SOC_partitions+1; ++i) {
7271                         //calculate the hyperplane coefficients
7272                         auto quad_val = radius*std::cos(M_PI*i/(2*num_SOC_partitions));
7273                         auto first_minus_second_val = radius*std::sin(M_PI*i/(2*num_SOC_partitions));
7274                         auto first_plus_second_val = radius;
7275                         cur_idx = cur_var_idx_quad+","+cur_var_idx_bln_first+","+cur_var_idx_bln_second+","+to_string(i+2);
7276                         quad_coef.set_val(cur_idx,quad_val/quad_scale);
7277                         first_minus_second_coef.set_val(cur_idx,first_minus_second_val/bln_scale);
7278                         first_plus_second_coef.set_val(cur_idx,first_plus_second_val/bln_scale);
7279                     }
7280                 }
7281 
7282                 //add the constraints involving the individual variables (tying lambda to the variables as lower and upper bounds)
7283                 v1_rep_pos = quad_var + 100 * v1_sign - quad_coef.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1) * lambda.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1);
7284                 v1_rep_pos.in(*c._indices) >= 0;
7285                 add(v1_rep_pos);
7286 
7287                 v1_rep_neg = quad_var - 100 * (1 - v1_sign) + quad_coef.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1) * lambda.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1);
7288                 v1_rep_neg.in(*c._indices) <= 0;
7289                 add(v1_rep_neg);
7290 
7291                 v2_rep_pos = (bln_first_var - bln_second_var) + 100 * v2_sign - first_minus_second_coef.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1) * lambda.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1);
7292                 v2_rep_pos.in(*c._indices) >= 0;
7293                 add(v2_rep_pos);
7294 
7295                 v2_rep_neg = (bln_first_var - bln_second_var)  - 100 * (1 - v2_sign) + first_minus_second_coef.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1) * lambda.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1);
7296                 v2_rep_neg.in(*c._indices) <= 0;
7297                 add(v2_rep_neg);
7298 
7299                 //this is the second part of the bilinear, which ends up in the rhs of the SOC constraint as the bounding term in the standard format
7300                 v3_rep = (bln_first_var + bln_second_var) - first_plus_second_coef.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1) * lambda.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1);
7301                 v3_rep.in(*c._indices) <= 0;
7302                 add(v3_rep);
7303 
7304             }
7305 
7306         }
7307 
7308         // INPUT: an SOC type constraint, and total number of partitions(binary variables)
7309         // OUTPUT: disjunctive union of hyperplanes as an inner approximation to the SOC, where the disjunctive union is made by on/off-formulation by Hijazi et. al.
7310         template<typename T=type> //function for creating hyperplanes to have an inner approximation of an SOC
add_on_off_SOC_hyperplanes(Constraint<type> & c,int num_SOC_partitions)7311         void add_on_off_SOC_hyperplanes(Constraint<type>& c, int num_SOC_partitions) { //currently the function asssumes there are only qterms!
7312 
7313             DebugOn("SOC_hyperplane function!" << endl);
7314             c.print();
7315 
7316             auto is_rotated_SOC = c.check_rotated_soc(); //collect the information about the cone
7317             auto is_SOC = c.check_soc();
7318 
7319             //create hyperplane indices
7320             indices hyper_idx("hyper_idx");
7321             for (int i=0; i<num_SOC_partitions; ++i) {
7322                 hyper_idx.add(to_string(i+1));
7323             }
7324             //get the combined index set
7325             auto inst_hyper = indices(*c._indices,hyper_idx);
7326 
7327             Constraint<type> SOC_hyperplanes(c._name + "_hyperplane"); //create the hyperplane constraint
7328             var<int> on(c._name + "_binary",0,1); //create the partition variable
7329             Constraint<type> onSum(c._name + "_binarySum"); //create the partition assignment constraint
7330 
7331             if (is_SOC){ //this will follow the standard creation of the hyperplane
7332 
7333                 //create the variables
7334                 var<> lhs_first_var;
7335                 var<> lhs_second_var;
7336                 var<> rhs_var;
7337 
7338                 //create the scaling factors for the variables
7339                 double lhs_first_scale;
7340                 double lhs_second_scale;
7341                 double rhs_scale;
7342 
7343                 //flag for assignment
7344                 bool first_occupied = false;
7345 
7346                 for (auto &qt_pair: *c._qterms) {
7347                     if (!qt_pair.second._p->first->is_double()) {
7348                         throw invalid_argument("Current hyperplanes only support double type variables!");
7349                     }
7350                     if (!qt_pair.second._coef->is_number()) { /*means coef is not a number*/
7351                         throw invalid_argument("Current hyperplanes only support constant coefficients for the variables");
7352                     }
7353                     auto sign = qt_pair.second._sign;
7354                     auto coef = static_pointer_cast<constant<type>>(qt_pair.second._coef);
7355                     if (sign ^ (coef->is_negative())) {
7356                         if (!first_occupied){
7357                             lhs_first_var = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
7358                             if(coef->is_positive()) lhs_first_scale = std::sqrt(coef->eval());
7359                             else lhs_first_scale = std::sqrt((-1)*coef->eval());
7360                             first_occupied = true;
7361                         }
7362                         else{
7363                             lhs_second_var = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
7364                             if(coef->is_positive()) lhs_second_scale = std::sqrt(coef->eval());
7365                             else lhs_second_scale = std::sqrt((-1)*coef->eval());
7366 
7367                         }
7368                     }
7369                     else{
7370                         rhs_var = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
7371                         if(coef->is_positive()) rhs_scale = std::sqrt(coef->eval());
7372                         else rhs_scale = std::sqrt((-1)*coef->eval());
7373                     }
7374                 }
7375 
7376 
7377                 //combine all the indices
7378                 auto inst_combined  = combine(*lhs_first_var._indices,*lhs_second_var._indices,*rhs_var._indices);
7379                 auto inst_combined_partn = indices(inst_combined,hyper_idx);
7380 
7381                 //collect the number of entries
7382                 auto nb_entries_v1 = lhs_first_var._indices->get_nb_entries();
7383                 auto nb_entries_v2 = lhs_second_var._indices->get_nb_entries();
7384                 auto nb_entries_v3 = rhs_var._indices->get_nb_entries();
7385 
7386                 //create the multipliers for the hyperplane
7387                 param<double> lhs_first_coef("lhs_first_coef");
7388                 param<double> lhs_second_coef("lhs_second_coef");
7389                 param<double> rhs_coef("rhs_coef");
7390 
7391                 lhs_first_coef.in(inst_combined_partn);
7392                 lhs_second_coef.in(inst_combined_partn);
7393                 rhs_coef.in(inst_combined_partn);
7394 
7395                 add(on.in(inst_combined_partn));
7396 
7397                 //create the summation constraint for partition
7398                 onSum = sum(on.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1));
7399                 add(onSum.in(*c._indices) == 1);
7400 
7401                 size_t nb_ins = lhs_first_var.get_nb_inst(); //get the number of instances
7402 
7403                 // fill hyperplane coefficients
7404                 for (int i=0 ; i<num_SOC_partitions; ++i) {
7405                     //calculate the hyperplane coefficients
7406                     auto rhs_val = std::cos(2*M_PI*i/num_SOC_partitions)*std::sin(2*M_PI*(i+1)/num_SOC_partitions) - std::cos(2*M_PI*(i+1)/num_SOC_partitions)*std::sin(2*M_PI*i/num_SOC_partitions);
7407                     auto lhs_first_val = std::sin(2*M_PI*i/num_SOC_partitions)-std::sin(2*M_PI*(i+1)/num_SOC_partitions);
7408                     auto lhs_second_val = std::cos(2*M_PI*(i+1)/num_SOC_partitions)-std::cos(2*M_PI*i/num_SOC_partitions);
7409                     if (rhs_val < 0)
7410                     {
7411                         rhs_val = -rhs_val;
7412                         lhs_first_val = -lhs_first_val;
7413                         lhs_second_val = -lhs_second_val;
7414                     }
7415 
7416                     for (size_t inst = 0; inst< nb_ins; inst++){
7417                         // lhs_first
7418                         auto cur_var_id_lhs_first = lhs_first_var.get_id_inst(inst);
7419                         auto cur_var_idx_lhs_first = lhs_first_var._indices->_keys->at(cur_var_id_lhs_first);
7420 
7421                         // lhs_second
7422                         auto cur_var_id_lhs_second = lhs_second_var.get_id_inst(inst);
7423                         auto cur_var_idx_lhs_second = lhs_second_var._indices->_keys->at(cur_var_id_lhs_second);
7424 
7425                         // rhs
7426                         auto cur_var_id_rhs = rhs_var.get_id_inst(inst);
7427                         auto cur_var_idx_rhs = rhs_var._indices->_keys->at(cur_var_id_rhs);
7428 
7429                         string cur_idx = cur_var_idx_lhs_first+","+cur_var_idx_lhs_second+","+cur_var_idx_rhs+","+to_string(i+1);
7430                         lhs_first_coef.set_val(cur_idx,lhs_first_val*lhs_first_scale);
7431                         lhs_second_coef.set_val(cur_idx,lhs_second_val*lhs_second_scale);
7432                         rhs_coef.set_val(cur_idx,rhs_val*rhs_scale);
7433                     }
7434                 }
7435 
7436                 // set the _in_SOC_partn to true
7437                 lhs_first_var._in_SOC_partn = true;
7438                 lhs_second_var._in_SOC_partn = true;
7439                 rhs_var._in_SOC_partn = true;
7440 
7441                 SOC_hyperplanes = lhs_first_var.from_ith(0, inst_combined_partn) * lhs_first_coef + lhs_second_var.from_ith(nb_entries_v1, inst_combined_partn) * lhs_second_coef + rhs_var.from_ith(nb_entries_v1 + nb_entries_v2, inst_combined_partn) * rhs_coef;
7442                 SOC_hyperplanes.in(inst_hyper) <= 0;
7443                 //the third argument for this function decides full facets or only Big-M type of version
7444                 add_on_off_multivariate_refined(SOC_hyperplanes, on, false);
7445 
7446                 // set the _in_SOC_partn to false back for removing the confusion in get_on_off_coefficients
7447                 lhs_first_var._in_SOC_partn = false;
7448                 lhs_second_var._in_SOC_partn = false;
7449                 rhs_var._in_SOC_partn = false;
7450             }
7451 
7452             else if (is_rotated_SOC){ //this will follow bilinear scheme
7453 
7454                 //create the variables
7455                 var<> quad_var;
7456                 var<> bln_first_var;
7457                 var<> bln_second_var;
7458 
7459                 //create the scaling factors for the variables
7460                 double quad_scale;
7461                 double bln_scale;
7462 
7463                 for (auto &qt_pair: *c._qterms) {
7464                     if (!qt_pair.second._p->first->is_double()) {
7465                         throw invalid_argument("Current hyperplanes only support double type variables!");
7466                     }
7467                     if (!qt_pair.second._coef->is_number()) { /*means coef is not a number*/
7468                         throw invalid_argument("Current hyperplanes only support constant coefficients for the variables");
7469                     }
7470                     if (qt_pair.second._p->first!=qt_pair.second._p->second) {
7471                         if (!qt_pair.second._p->second->is_double()) {
7472                             throw invalid_argument("Current hyperplanes only support double type variables!");
7473                         }
7474                             //we do not check the sign of the bilinear term, since current assumption is that the sign is negative
7475                             bln_first_var = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
7476                             bln_second_var = *static_pointer_cast<var<double>>(qt_pair.second._p->second);
7477                             auto coef = static_pointer_cast<constant<type>>(qt_pair.second._coef);
7478                             if(coef->is_positive()) bln_scale = std::sqrt(coef->eval())/2; //dividing to two since the standard form has 1/4 as the multiplier
7479                             else bln_scale = std::sqrt((-1)*coef->eval())/(2);
7480                     }
7481                     else{
7482                         quad_var = *static_pointer_cast<var<double>>(qt_pair.second._p->first);
7483                         auto coef = static_pointer_cast<constant<type>>(qt_pair.second._coef);
7484                         if(coef->is_positive()) quad_scale = std::sqrt(coef->eval());
7485                         else quad_scale = std::sqrt((-1)*coef->eval());
7486                     }
7487                 }
7488 
7489 
7490                 //combine all the indices
7491                 auto inst_combined  = combine(*quad_var._indices,*bln_first_var._indices,*bln_second_var._indices);
7492                 auto inst_combined_partn = indices(inst_combined,hyper_idx);
7493 
7494                 //collect the number of entries
7495                 auto nb_entries_v1 = quad_var._indices->get_nb_entries();
7496                 auto nb_entries_v2 = bln_first_var._indices->get_nb_entries();
7497                 auto nb_entries_v3 = bln_second_var._indices->get_nb_entries();
7498 
7499                 //create the multipliers for the hyperplane
7500                 param<double> quad_coef("quad_coef");
7501                 param<double> first_minus_second_coef("first_minus_second_coef");
7502                 param<double> first_plus_second_coef("first_plus_second_coef");
7503 
7504                 quad_coef.in(inst_combined_partn);
7505                 first_minus_second_coef.in(inst_combined_partn);
7506                 first_plus_second_coef.in(inst_combined_partn);
7507 
7508 
7509                 add(on.in(inst_combined_partn));
7510                 //create the summation constraint for partition
7511                 onSum = sum(on.in_matrix(nb_entries_v1+nb_entries_v2+nb_entries_v3,1));
7512                 add(onSum.in(*c._indices) == 1);
7513 
7514                 size_t nb_ins = quad_var.get_nb_inst(); //get the number of instances
7515 
7516                 // fill hyperplane coefficients
7517                 for (int i=0 ; i<num_SOC_partitions; ++i) {
7518                     //calculate the hyperplane coefficients
7519                     auto first_plus_second_val = std::cos(2*M_PI*i/num_SOC_partitions)*std::sin(2*M_PI*(i+1)/num_SOC_partitions) - std::cos(2*M_PI*(i+1)/num_SOC_partitions)*std::sin(2*M_PI*i/num_SOC_partitions);
7520                     auto quad_val = std::sin(2*M_PI*i/num_SOC_partitions)-std::sin(2*M_PI*(i+1)/num_SOC_partitions);
7521                     auto first_minus_second_val = std::cos(2*M_PI*(i+1)/num_SOC_partitions)-std::cos(2*M_PI*i/num_SOC_partitions);
7522                     if (first_plus_second_val < 0)
7523                     {
7524                         first_plus_second_val = -first_plus_second_val;
7525                         quad_val = -quad_val;
7526                         first_minus_second_val = -first_minus_second_val;
7527                     }
7528 
7529                     for (size_t inst = 0; inst< nb_ins; inst++){
7530                         // quad
7531                         auto cur_var_id_quad = quad_var.get_id_inst(inst);
7532                         auto cur_var_idx_quad = quad_var._indices->_keys->at(cur_var_id_quad);
7533 
7534                         // bln_first
7535                         auto cur_var_id_bln_first = bln_first_var.get_id_inst(inst);
7536                         auto cur_var_idx_bln_first = bln_first_var._indices->_keys->at(cur_var_id_bln_first);
7537 
7538                         // bln_second
7539                         auto cur_var_id_bln_second = bln_second_var.get_id_inst(inst);
7540                         auto cur_var_idx_bln_second = bln_second_var._indices->_keys->at(cur_var_id_bln_second);
7541 
7542                         string cur_idx = cur_var_idx_quad+","+cur_var_idx_bln_first+","+cur_var_idx_bln_second+","+to_string(i+1);
7543                         quad_coef.set_val(cur_idx,quad_val*quad_scale);
7544                         first_minus_second_coef.set_val(cur_idx,first_minus_second_val*bln_scale);
7545                         first_plus_second_coef.set_val(cur_idx,first_plus_second_val*bln_scale);
7546                     }
7547                 }
7548 
7549                 // set the _in_SOC_partn to true
7550                 quad_var._in_SOC_partn = true;
7551                 bln_first_var._in_SOC_partn = true;
7552                 bln_second_var._in_SOC_partn = true;
7553 
7554                 SOC_hyperplanes = quad_var.from_ith(0, inst_combined_partn) * quad_coef + (bln_first_var.from_ith(nb_entries_v1, inst_combined_partn) - bln_second_var.from_ith(nb_entries_v1+nb_entries_v2, inst_combined_partn) ) * first_minus_second_coef + (bln_first_var.from_ith(nb_entries_v1, inst_combined_partn) + bln_second_var.from_ith(nb_entries_v1+nb_entries_v2, inst_combined_partn) ) * first_plus_second_coef;
7555                 SOC_hyperplanes.in(inst_hyper) <= 0;
7556                 //the third argument for this function decides full facets or only Big-M type of version
7557                 add_on_off_multivariate_refined(SOC_hyperplanes, on, false);
7558 
7559                 // set the _in_SOC_partn to false back for removing the confusion in get_on_off_coefficients
7560                 quad_var._in_SOC_partn = false;
7561                 bln_first_var._in_SOC_partn = false;
7562                 bln_second_var._in_SOC_partn = false;
7563             }
7564         }
7565 
7566         /* Run a single iteration of Optimality Based Bound Tightening
7567          @param[in] relaxed_model a convex relaxtion of the current model
7568          @param[in] res vector storing the computation results
7569          */
7570         template<typename T=type,
7571         typename std::enable_if<is_same<T,double>::value>::type* = nullptr>
7572         std::tuple<bool,int,double,double,double,double,double,double> run_obbt_one_iteration(shared_ptr<Model<T>> relaxed_model= nullptr, double max_time = 1000, unsigned max_iter=1e3, double rel_tol=1e-2, double abs_tol=1e6, unsigned nb_threads = 1, SolverType ub_solver_type = ipopt, SolverType lb_solver_type = ipopt, double ub_solver_tol=1e-6, double lb_solver_tol=1e-6, double range_tol=1e-3, bool linearize=false, shared_ptr<Model<T>> obbt_model= nullptr, Model<T> & interior_model=nullptr);
7573 
7574 
7575         /* Run Optimality Based Bound Tightening
7576         @param[in] relaxed_model a convex relaxtion of the current model
7577         @param[in] res vector storing the computation results
7578         */
7579         template<typename T=type,
7580         typename std::enable_if<is_same<T,double>::value>::type* = nullptr>
7581         std::tuple<bool,int,double,double,double,double,double,double> run_obbt(shared_ptr<Model<T>> relaxed_model= nullptr, double max_time = 1000, unsigned max_iter=1e3, double rel_tol=1e-2, double abs_tol=1e6, unsigned nb_threads = 1, SolverType ub_solver_type = ipopt, SolverType lb_solver_type = ipopt, double ub_solver_tol=1e-6, double lb_solver_tol=1e-6, double range_tol=1e-3, bool linearize=false);
7582 
7583 
7584 //        void add_on_off(var<>& v, var<bool>& on){
7585             //    if(v.get_ub() != v.get_ub_off()) {
7586             //        Constraint UB(v._name + "_UB_on/off");
7587             //        UB += v - v.get_ub() * on - (1 - on) * v.get_ub_off();
7588             //        UB <= 0;
7589             //        addConstraint(UB);
7590             //    }
7591             //    if(v.get_lb() != v.get_lb_off()) {
7592             //        Constraint LB(v._name + "_LB_on/off");
7593             //        LB += v - v.get_lb() * on - (1 - on) * v.get_lb_off();
7594             //        LB >= 0;
7595             //        addConstraint(LB);
7596             //    }
7597 //        }
7598 
7599 
7600 
7601         //        void add_on_off_McCormick(std::string name, var<>& v, var<>& v1, var<>& v2, var<bool>& on) {
7602         //    Constraint MC1(name+"_McCormick1");
7603         //    MC1 += v;
7604         //    MC1 -= v1.get_lb()*v2 + v2.get_lb()*v1 - v1.get_lb()*v2.get_lb();
7605         //    MC1 >= 0;
7606         //    add_on_off(MC1, on);
7607         //    Constraint MC2(name+"_McCormick2");
7608         //    MC2 += v;
7609         //    MC2 -= v1.get_ub()*v2 + v2.get_ub()*v1 - v1.get_ub()*v2.get_ub();
7610         //    MC2 >= 0;
7611         //    add_on_off(MC2, on);
7612         //    Constraint MC3(name+"_McCormick3");
7613         //    MC3 += v;
7614         //    MC3 -= v1.get_lb()*v2 + v2.get_ub()*v1 - v1.get_lb()*v2.get_ub();
7615         //    MC3 <= 0;
7616         //    add_on_off(MC3, on);
7617         //    Constraint MC4(name+"_McCormick4");
7618         //    MC4 += v;
7619         //    MC4 -= v1.get_ub()*v2 + v2.get_lb()*v1 - v1.get_ub()*v2.get_lb();
7620         //    MC4 <= 0;
7621         //    add_on_off(MC4, on);
7622         //        }
7623 
7624 
7625         //functions for the evaluation of the objective function
7626         template<class T=type>
7627         inline type eval(const shared_ptr<constant_>& c, size_t i=0) {
7628             return _obj->eval(c,i);
7629         }
7630 
7631         template<class T=type>
eval(const shared_ptr<constant_> & c,size_t i,size_t j)7632         inline type eval(const shared_ptr<constant_>& c, size_t i, size_t j){
7633             return _obj->eval(c,i,j);
7634         }
7635 #ifdef USE_CoinUtils
readMPS(const string & fname)7636         int readMPS(const string& fname){
7637                 //string mps_file = "/Users/l297598/Downloads/fhnw-binpack4-58.mps", gms_file="/Users/l297598/Downloads/nvs24.gms";
7638             CoinMpsIO m;
7639             CoinMessageHandler* handler_ = new CoinMessageHandler();;
7640             m.passInMessageHandler(handler_);
7641             bool savePrefix = m.messageHandler()->prefix();
7642             m.messageHandler()->setPrefix(handler_->prefix());
7643             m.setSmallElementValue(m.getSmallElementValue());
7644             int status = 0;
7645             try {
7646                 status = m.readMps(fname.c_str(), "");
7647                     //        status = m.readGms(gms_file.c_str());
7648                 auto nb_vars = m.getNumCols();
7649                 DebugOn("The number of variables is " << nb_vars << endl);
7650                 auto nb_cstr = m.getNumRows();
7651                 DebugOn("The number of constraints is " << nb_cstr << endl);
7652                 if (m.reader()->whichSection() == COIN_QUAD_SECTION) {
7653                     CoinBigIndex *start = NULL;
7654                     int *column = NULL;
7655                     double *element = NULL;
7656                     status = m.readQuadraticMps(NULL, start, column, element, 2);
7657                     if (!status){
7658                             //                loadQuadraticObjective(nb_vars, start, column, element);
7659                     }
7660                     delete[] start;
7661                     delete[] column;
7662                     delete[] element;
7663                 }
7664                 if (m.reader()->whichSection() == COIN_CONIC_SECTION) {
7665                     CoinBigIndex *start = NULL;
7666                     int *column = NULL;
7667                     double *element = NULL;
7668                     int * coneType = NULL;
7669                     int nbCones = 0;
7670                     status = m.readConicMps(NULL, start, column, coneType, nbCones);
7671                     if (!status){
7672                             //                loadQuadraticObjective(nb_vars, start, column, element);
7673                     }
7674                     DebugOn("Problem has " << nbCones << " Cones" << endl);
7675                     delete[] start;
7676                     delete[] column;
7677                     delete[] coneType;
7678                 }
7679             }
7680             catch (CoinError e) {
7681                 e.print();
7682                 status = -1;
7683             }
7684             delete handler_;
7685             return status;
7686         }
7687 #endif
7688 
7689         template<typename T=type,typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr>
7690         int readNL(const string& fname);
7691     };
7692 
7693     //    void compute_constrs(vector<Constraint*>& v, double* res, unsigned i, unsigned j);
7694 
7695     //    template<typename T>
7696     //    pair<shared_ptr<func_>, ObjectiveType> max(const func<T>& f){
7697     //        auto fcpy = f.copy();
7698     //        fcpy->allocate_mem();
7699     //        return make_pair<>(fcpy,maximize);
7700     //    };
7701     //
7702     //    template<typename T>
7703     //    pair<shared_ptr<func_>, ObjectiveType> min(const func<T>& f){
7704     //        auto fcpy = f.copy();
7705     //        f->_val->resize(1);
7706     //        return make_pair<>(fcpy,minimize);
7707     //    };
7708     //
7709     //    template<typename T>
7710     //    pair<shared_ptr<func_>, ObjectiveType> min(func<T>&& f){
7711     //        f->_val->resize(1);
7712     //        auto fcpy = move(f.copy());
7713     //        return make_pair<>(fcpy,minimize);
7714     //    };
7715 
7716     template<typename type = double>
7717     class Program{
7718     public:
7719         //        virtual void update_model(){};
7720         string _status;
7721     };
7722 
7723     template<class T>
min(const param<T> & p1,const param<T> & p2)7724     func<T> min(const param<T>& p1, const param<T>& p2){
7725         func<T> res(bexpr<T>(min_, p1.copy(), p2.copy()));
7726         res._all_sign = std::min(p1.get_all_sign(),p2.get_all_sign());
7727         res._all_convexity = undet_;
7728         res._range->first = gravity::min(p1._range->first,p2._range->first);
7729         res._range->second = gravity::min(p1._range->second,p2._range->second);
7730         res._expr->_range->first = res._range->first;
7731         res._expr->_range->second = res._range->second;
7732         res._expr->_all_convexity = res._all_convexity;
7733         res._expr->_all_sign = res._all_sign;
7734         return res;
7735     }
7736 
7737     template<class T>
max(const param<T> & p1,const param<T> & p2)7738     func<T> max(const param<T>& p1, const param<T>& p2){
7739         func<T> res(bexpr<T>(max_, p1.copy(), p2.copy()));
7740         res._all_sign = std::max(p1.get_all_sign(),p2.get_all_sign());
7741         res._all_convexity = undet_;
7742         res._range->first = gravity::max(p1._range->first,p2._range->first);
7743         res._range->second = gravity::max(p1._range->second,p2._range->second);
7744         res._expr->_range->first = res._range->first;
7745         res._expr->_range->second = res._range->second;
7746         res._expr->_all_convexity = res._all_convexity;
7747         res._expr->_all_sign = res._all_sign;
7748         return res;
7749     }
7750 
7751     template<class T>
min(const param<T> & p1,const func<T> & p2)7752     func<T> min(const param<T>& p1, const func<T>& p2){
7753         func<T> res(bexpr<T>(min_, p1.copy(), p2.copy()));
7754         res._all_sign = std::min(p1.get_all_sign(),p2.get_all_sign());
7755         res._all_convexity = undet_;
7756         res._range->first = gravity::min(p1._range->first,p2._range->first);
7757         res._range->second = gravity::min(p1._range->second,p2._range->second);
7758         res._expr->_range->first = res._range->first;
7759         res._expr->_range->second = res._range->second;
7760         res._expr->_all_convexity = res._all_convexity;
7761         res._expr->_all_sign = res._all_sign;
7762         res.update_vars();
7763         return res;
7764     }
7765 
7766     template<class T>
min(const func<T> & p1,const param<T> & p2)7767     func<T> min(const func<T>& p1, const param<T>& p2){
7768         func<T> res(bexpr<T>(min_, p1.copy(), p2.copy()));
7769         res._all_sign = std::min(p1.get_all_sign(),p2.get_all_sign());
7770         res._all_convexity = undet_;
7771         res._range->first = gravity::min(p1._range->first,p2._range->first);
7772         res._range->second = gravity::min(p1._range->second,p2._range->second);
7773         res._expr->_range->first = res._range->first;
7774         res._expr->_range->second = res._range->second;
7775         res._expr->_all_convexity = res._all_convexity;
7776         res._expr->_all_sign = res._all_sign;
7777         res.update_vars();
7778         return res;
7779     }
7780 
7781     template<class T>
max(const param<T> & p1,const func<T> & p2)7782     func<T> max(const param<T>& p1, const func<T>& p2){
7783         func<T> res(bexpr<T>(max_, p1.copy(), p2.copy()));
7784         res._all_sign = std::max(p1.get_all_sign(),p2.get_all_sign());
7785         res._all_convexity = undet_;
7786         res._range->first = gravity::max(p1._range->first,p2._range->first);
7787         res._range->second = gravity::max(p1._range->second,p2._range->second);
7788         res._expr->_range->first = res._range->first;
7789         res._expr->_range->second = res._range->second;
7790         res._expr->_all_convexity = res._all_convexity;
7791         res._expr->_all_sign = res._all_sign;
7792         return res;
7793     }
7794 
7795     template<class T>
max(const func<T> & p1,const param<T> & p2)7796     func<T> max(const func<T>& p1, const param<T>& p2){
7797         func<T> res(bexpr<T>(max_, p1.copy(), p2.copy()));
7798         res._all_sign = std::max(p1.get_all_sign(),p2.get_all_sign());
7799         res._all_convexity = undet_;
7800         res._range->first = gravity::max(p1._range->first,p2._range->first);
7801         res._range->second = gravity::max(p1._range->second,p2._range->second);
7802         res._expr->_range->first = res._range->first;
7803         res._expr->_range->second = res._range->second;
7804         res._expr->_all_convexity = res._all_convexity;
7805         res._expr->_all_sign = res._all_sign;
7806         return res;
7807     }
7808 
7809     template<class T>
max(const func<T> & p1,const func<T> & p2)7810     func<T> max(const func<T>& p1, const func<T>& p2){
7811         func<T> res(bexpr<T>(max_, p1.copy(), p2.copy()));
7812         res._all_sign = std::max(p1.get_all_sign(),p2.get_all_sign());
7813         res._all_convexity = undet_;
7814         res._range->first = gravity::max(p1._range->first,p2._range->first);
7815         res._range->second = gravity::max(p1._range->second,p2._range->second);
7816         res._expr->_range->first = res._range->first;
7817         res._expr->_range->second = res._range->second;
7818         res._expr->_all_convexity = res._all_convexity;
7819         res._expr->_all_sign = res._all_sign;
7820         res.update_vars();
7821         return res;
7822     }
7823 
7824     template<class T>
min(const func<T> & p1,const func<T> & p2)7825     func<T> min(const func<T>& p1, const func<T>& p2){
7826         func<T> res(bexpr<T>(min_, p1.copy(), p2.copy()));
7827 //        auto lson = static_ptr_cast<func<T>>(res.get_lson());
7828 //        auto rson = static_ptr_cast<func<T>>(res.get_rson());
7829         res._all_sign = std::min(p1.get_all_sign(),p2.get_all_sign());
7830         res._all_convexity = undet_;
7831         res._range->first = gravity::min(p1._range->first,p2._range->first);
7832         res._range->second = gravity::min(p1._range->second,p2._range->second);
7833         res._expr->_range->first = res._range->first;
7834         res._expr->_range->second = res._range->second;
7835         res._expr->_all_convexity = res._all_convexity;
7836         res._expr->_all_sign = res._all_sign;
7837         res.update_vars();
7838         return res;
7839     }
7840 #ifdef USE_MP
7841     // Converter of optimization problems from NL to Gravity format.
7842     class MPConverter : public mp::ExprVisitor<MPConverter, func<>> {
7843     public:
7844 
MPConverter(Model<> & m)7845         MPConverter(Model<>& m): _model(&m){};
7846 
7847         Model<>* _model = nullptr;
7848 
VisitPow2(UnaryExpr e)7849         func<> VisitPow2(UnaryExpr e) {
7850           return pow(Visit(e.arg()),2);
7851         }
7852 
7853 
VisitNumericConstant(mp::NumericConstant c)7854         double VisitNumericConstant(mp::NumericConstant c) {
7855           return (c.value());
7856         }
7857 
VisitMinus(UnaryExpr e)7858         func<> VisitMinus(UnaryExpr e) {
7859             return -1*Visit(e.arg());
7860         }
7861 
VisitSum(SumExpr e)7862         func<> VisitSum(SumExpr e){
7863             func<> sum;
7864             for (SumExpr::iterator i = e.begin(), end = e.end(); i != end; ++i){
7865                 sum += Visit(*i);
7866             }
7867             return sum;
7868         }
7869 
get_cont_int_var(int index)7870         var<> get_cont_int_var(int index){
7871             if(_model->has_int()){
7872                 auto y = _model->get_var<double>("y");
7873                 if(index >= y.get_id())/* Integer variable */
7874                 {
7875                     return y(index);
7876                 }
7877             }
7878             /* Continuous variable */
7879             auto x = _model->get_var<double>("x");
7880             return x(index);
7881         }
7882 
VisitVariable(mp::Reference r)7883         var<> VisitVariable(mp::Reference r) {
7884             return get_cont_int_var(r.index());
7885         }
7886 
VisitConstant(NumericConstant n)7887         double VisitConstant(NumericConstant n) { return n.value(); }
7888 //        func<> Visit(NumericExpr e) {
7889 //          return ExprVisitor<MPConverter, func<>>::Visit(e);
7890 //        }
VisitAdd(BinaryExpr e)7891         func<> VisitAdd(BinaryExpr e) {
7892           return Visit(e.lhs()) + Visit(e.rhs());
7893         }
7894 
VisitSub(BinaryExpr e)7895         func<> VisitSub(BinaryExpr e) {
7896           return Visit(e.lhs()) - Visit(e.rhs());
7897         }
7898 
VisitMul(BinaryExpr e)7899         func<> VisitMul(BinaryExpr e) {
7900           return Visit(e.lhs()) * Visit(e.rhs());
7901         }
7902 
VisitDiv(BinaryExpr e)7903         func<> VisitDiv(BinaryExpr e) {
7904           return Visit(e.lhs()) / Visit(e.rhs());
7905         }
7906 
7907 
VisitPow(BinaryExpr e)7908         func<> VisitPow(BinaryExpr e) {
7909           return pow(Visit(e.lhs()), Visit(e.rhs()).eval());
7910         }
7911 
7912 //        func<> VisitLess(BinaryExpr e) {
7913 //          return IloMax(Visit(e.lhs()) - Visit(e.rhs()), 0.0);
7914 //        }
7915 //
7916 //        func<> VisitMin(VarArgExpr e) {
7917 //          return IloMin(ConvertArgs(e));
7918 //        }
7919 //
7920 //        func<> VisitMax(VarArgExpr e) {
7921 //          return IloMax(ConvertArgs(e));
7922 //        }
7923 //
7924 //        func<> VisitMinus(UnaryExpr e) {
7925 //          return -Visit(e.arg());
7926 //        }
7927 
VisitAbs(UnaryExpr e)7928         func<> VisitAbs(UnaryExpr e) {
7929           return abs(Visit(e.arg()));
7930         }
7931 
VisitSin(UnaryExpr e)7932         func<> VisitSin(UnaryExpr e) {
7933             return sin(Visit(e.arg()));
7934         }
7935 
VisitCos(UnaryExpr e)7936         func<> VisitCos(UnaryExpr e) {
7937             return cos(Visit(e.arg()));
7938         }
7939 
VisitSqrt(UnaryExpr e)7940         func<> VisitSqrt(UnaryExpr e) {
7941             return sqrt(Visit(e.arg()));
7942         }
7943 
VisitExp(UnaryExpr e)7944         func<> VisitExp(UnaryExpr e) {
7945             return exp(Visit(e.arg()));
7946         }
7947 
VisitLog(UnaryExpr e)7948         func<> VisitLog(UnaryExpr e) {
7949             return log(Visit(e.arg()));
7950         }
7951 //        func<> VisitFloor(UnaryExpr e) {
7952 //          return IloFloor(Visit(e.arg()));
7953 //        }
7954 //
7955 //        func<> VisitCeil(UnaryExpr e) {
7956 //          return IloCeil(Visit(e.arg()));
7957 //        }
7958     };
7959 
7960 #endif
7961 
7962 
7963 }
7964 
7965 
7966 
7967 
7968 #endif /* model_hpp */
7969