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 <: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 < : _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 < : 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 <:*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