1 // 2 // func.h 3 // Gravity 4 // 5 // Created by Hijazi, Hassan on 21/10/16. 6 // 7 // 8 9 #ifndef func_h 10 #define func_h 11 12 13 #include <gravity/expr.h> 14 #include <gravity/poly.h> 15 #include <gravity/var.h> 16 #include <gravity/Auxiliary.h> 17 #include <stdio.h> 18 #include <map> 19 #include <math.h> 20 #include <iterator> 21 #include <queue> 22 #include <list> 23 #include <limits> 24 #include <set> 25 // 26 using namespace std; 27 28 namespace gravity { 29 // 30 // 31 // 32 /** Backbone class for function */ 33 class func_ : public constant_{ 34 private: 35 36 37 public: 38 FType _ftype = const_; /**< Function type, e.g., constant, linear, quadratic... >>**/ 39 NType _return_type = double_; /**< Return type, e.g., bool, integer, complex... >>**/ 40 41 shared_ptr<map<string, pair<shared_ptr<param_>, unsigned>>> _params = nullptr;/**< Set of parameters in current function, stored as a map <parameter name, <paramter pointer, number of times it appears in function>>**/ 42 shared_ptr<map<string, pair<shared_ptr<param_>, unsigned>>> _vars = nullptr;/**< Set of variables in current function, stored as a map <variable name, <variable pointer, number of times it appears in function>>**/ 43 44 shared_ptr<constant_> _cst = nullptr;/**< Constant part of the function */ 45 shared_ptr<map<string, lterm>> _lterms = nullptr; /**< Set of linear terms, stored as a map <string describing term, term>. */ 46 shared_ptr<map<string, qterm>> _qterms = nullptr; /**< Set of quadratic terms, stored as a map <string describing term, term>. */ 47 shared_ptr<map<string, pterm>> _pterms = nullptr; /**< Set of polynomial terms, stored as a map <string describing term, term>. */ 48 // map<string, expr*>* _DAG = nullptr; /**< Map of experssions stored in the expression tree (a Directed Acyclic Graph) */ 49 // deque<shared_ptr<expr>>* _queue = nullptr; /**< A queue storing the expression tree from the leaves to the root (the root is stored at the end of the queue)*/ 50 Convexity _all_convexity = linear_; /**< If all instances of this function have the same convexity type, it stores it here, i.e. linear, convex, concave, otherwise it stores unknown. >>**/ 51 Sign _all_sign = zero_; /**< If all instances of this function have the same sign, it stores it here, otherwise it stores unknown. >>**/ 52 53 shared_ptr<vector<Convexity>> _convexity = nullptr; /**< Vector of convexity types, i.e., linear, convex, concave or unknown. This is a vector since a function can have multiple instances (different constants coefficients, and bounds, but same structure) >>**/ 54 shared_ptr<vector<Sign>> _sign = nullptr; /**< vector storing the sign of return value if known. >>**/ 55 shared_ptr<map<size_t, set<size_t>>> _hess_link = nullptr; /**< Set of variables linked to one another in the hessian, stored by variable ids */ 56 57 bool _new = true; /**< Will become false once this function is added to a program. Can be useful for iterative model solving. */ 58 bool _is_constraint = false; 59 bool _is_hessian = false; 60 bool _embedded = false; /**< If the function is embedded in a mathematical model or in another function, this is used for memory management. >>**/ 61 bool _evaluated = true;/**< If the function has already been evaluated, useful for constant funcs */ 62 string _to_str = "";/**< A string representation of the expression */ 63 64 size_t _nb_vars = 0; /**< Number of variables */ 65 66 size_t _nnz_j = 0; /**< Number of nonzeros in the Jacobian **/ 67 size_t _nnz_h = 0; /**< Number of nonzeros in the Hessian **/ 68 69 string _name = "noname"; 70 shared_ptr<indices> _indices = nullptr; /*< If indexed, point to the indexing set */ 71 /** Accessors */ 72 FType get_ftype() const; get_return_type()73 inline NType get_return_type() const{ return _return_type;}; 74 get_hess_link()75 map<size_t, set<size_t>>& get_hess_link() { return *_hess_link;}; get_vars()76 map<string, pair<shared_ptr<param_>, unsigned>>& get_vars() { return *_vars;}; get_params()77 map<string, pair<shared_ptr<param_>, unsigned>>& get_params() { return *_params;}; 78 79 /** true/false statements */ is_indexed()80 bool is_indexed() const{ 81 return (_indices && _indices->_ids); 82 } 83 /* Has var with identical indexing */ 84 bool has_var(const param_& v) const; 85 /* Has symbolic var ignoring indexing */ 86 bool has_sym_var(const param_& v) const; 87 /* Has var with identical indexing */ 88 bool has_var(const string& name) const; is_binary()89 bool is_binary() const { 90 return (_return_type==binary_); 91 }; 92 is_integer()93 bool is_integer() const { 94 return (_return_type==integer_); 95 }; 96 is_float()97 bool is_float() const { 98 return (_return_type==float_); 99 }; 100 is_double()101 bool is_double() const { 102 return (_return_type==double_); 103 }; 104 is_long()105 bool is_long() const { 106 return (_return_type==long_); 107 }; 108 is_complex()109 bool is_complex() const { 110 return (_return_type==complex_); 111 }; is_zero()112 virtual bool is_zero() const{ return false;}; is_convex()113 virtual bool is_convex() const{ 114 return (_all_convexity==convex_ || _all_convexity==linear_); 115 } 116 is_concave()117 virtual bool is_concave() const{ 118 return (_all_convexity==concave_ || _all_convexity==linear_); 119 } 120 121 bool check_soc(); 122 bool check_rotated_soc(); 123 124 bool is_convex(size_t idx) const; 125 bool is_concave(size_t idx) const; 126 bool is_linear() const; 127 bool is_quadratic() const; 128 bool is_polynomial() const; 129 bool is_nonlinear() const; 130 bool is_transposed() const; func_is_number()131 bool func_is_number() const{ 132 return (_vars->empty() && _params->empty()); 133 } 134 is_unitary()135 bool is_unitary() const{ 136 return (_vars->size()==1); 137 } 138 has_square()139 bool has_square() const { 140 for (auto &qt: *_qterms) { 141 if (qt.second._p->first==qt.second._p->second && !qt.second._p->first->_is_transposed && !qt.second._coef_p1_tr) { 142 return true; 143 } 144 } 145 return false; 146 } 147 148 149 /* Virtual functions */ 150 fcopy()151 virtual shared_ptr<func_> fcopy() const{return nullptr;}; 152 153 /* Modifiers */ 154 155 156 157 158 159 /** 160 Copy and embed derivatives of f. 161 @param[in] f function to copy derivatives from. 162 */ 163 void copy_derivatives(const func_& f); 164 165 166 unsigned nb_occ_var(string name) const;/**< Returns the number of occurences the variable has in this function. */ 167 168 unsigned nb_occ_param(string name) const;/**< Returns the number of occurences the parameter has in this function. */ 169 170 void incr_occ_var(string str);/**< Increases the number of occurences the variable has in this function. */ 171 172 void incr_occ_param(string str);/**< Increases the number of occurences the parameter has in this function. */ 173 174 void decr_occ_var(string str, int nb=1);/**< Decreases the number of occurences the variable has in this function by nb. */ 175 176 void decr_occ_param(string str, int nb=1);/**< Decreases the number of occurences the parameter has in this function by nb. */ 177 178 get_lterms()179 map<string, lterm>& get_lterms() const{ 180 return *_lterms; 181 } 182 get_qterms()183 map<string, qterm>& get_qterms() const{ 184 return *_qterms; 185 } 186 get_pterms()187 map<string, pterm>& get_pterms() const{ 188 return *_pterms; 189 } 190 191 192 193 194 195 196 /** 197 Returns a vector of monomials of degree d using the variables in the current function 198 @param[in] d degree of monomials 199 @return a vector of monomials of degree d using the variables in the current function 200 */ 201 vector<pterm> get_monomials(unsigned d); 202 203 204 qterm* get_square(shared_ptr<param_> p); /**< Returns the quadratic term containing a square of p or nullptr if none exists. **/ 205 206 207 208 209 210 /** 211 Returns the number of variables per-instance. 212 @param[in] instance number. 213 @return number of variables per-instance. 214 */ get_nb_vars(unsigned inst)215 size_t get_nb_vars(unsigned inst) const{ 216 size_t n = 0; 217 for (auto &vp:*_vars) { 218 if(vp.second.first->_is_vector || vp.second.first->is_matrix_indexed()){ 219 n += vp.second.first->get_dim(inst); 220 } 221 else { 222 n += 1; 223 } 224 } 225 return n; 226 }; 227 get_nb_vars()228 size_t get_nb_vars() const{ 229 size_t n = 0; 230 for (auto &vp:*_vars) { 231 if(vp.second.first->_is_vector){ 232 n += vp.second.first->get_dim(); 233 } 234 else { 235 n += 1; 236 } 237 } 238 return n; 239 }; 240 get_nb_int_vars()241 size_t get_nb_int_vars() const{ 242 size_t n = 0; 243 for (auto &vp:*_vars) { 244 if(vp.second.first->_is_relaxed){ 245 if(vp.second.first->_is_vector){ 246 n += vp.second.first->get_dim(); 247 } 248 else { 249 n += 1; 250 } 251 } 252 } 253 return n; 254 }; 255 256 size_t get_id_inst(size_t inst = 0) const { 257 if (is_indexed()) { 258 if(_indices->_ids->at(0).size() <= inst){ 259 throw invalid_argument("func_::get_id_inst(size_t inst) inst is out of range"); 260 } 261 return _indices->_ids->at(0).at(inst); 262 } 263 auto dim = get_dim(); 264 if(inst > dim-1){ 265 throw invalid_argument("func_::get_id_inst(size_t inst) inst is out of range"); 266 } 267 return inst; 268 }; 269 get_id_inst(size_t inst1,size_t inst2)270 size_t get_id_inst(size_t inst1, size_t inst2) const { 271 if (is_matrix_indexed()) { 272 if (_indices->_ids->size()<=inst1) { 273 throw invalid_argument("get_id_inst(size_t inst1, size_t inst2) inst1 out of range\n"); 274 } 275 if (_indices->_ids->at(inst1).size()<=inst2) { 276 throw invalid_argument("get_id_inst(size_t inst1, size_t inst2) inst2 out of range\n"); 277 } 278 return _indices->_ids->at(inst1).at(inst2); 279 } 280 throw invalid_argument("Calling get_id_inst(size_t inst1, size_t inst2) on a non-indexed param\n"); 281 }; 282 283 284 /** Return the number of linear terms in this function */ 285 unsigned nb_linear_terms() const; 286 287 /** The function iterates over key references in _ids and keeps only the unique entries */ 288 void keep_unique_keys(); 289 290 291 292 293 294 295 296 /** 297 Returns a pointer to the constant part of the function. 298 @return a pointer to the constant part of the function. 299 */ 300 shared_ptr<constant_> get_cst() const; 301 302 /** 303 Returns a pointer to the variable matching the name provided. 304 @param[in] name variable name. 305 @return a pointer to the variable matching the name provided. 306 */ 307 shared_ptr<param_> get_var(const string& name) const; 308 309 /** 310 Returns a pointer to the variable matching the index provided. 311 @param[in] idx variable index. 312 @return a pointer to the variable matching the index provided. 313 */ 314 shared_ptr<param_> get_var(size_t idx) const; 315 316 /** 317 Returns a pointer to the parameter matching the name provided. 318 @param[in] name variable name. 319 @return a pointer to the parameter matching the name provided. 320 */ 321 shared_ptr<param_> get_param(string name) const; 322 323 void add_var(shared_ptr<param_> v, int nb = 1);/**< Inserts the variable in this function input list. nb represents the number of occurences v has. WARNING: Assumes that v has not been added previousely!*/ 324 325 void add_param(shared_ptr<param_> v, int nb = 1);/**< Inserts the parameter in this function input list. nb represents the number of occurences v has. WARNING: Assumes that v has not been added previousely!*/ 326 327 328 /** 329 Reverse the convexity property of the current function 330 */ 331 void reverse_convexity(); 332 333 334 335 336 void update_sign_add(const constant_& c); 337 void update_sign_multiply(const constant_& c); 338 339 void update_quad_convexity(); 340 update_convexity_add(Convexity c)341 void update_convexity_add(Convexity c){ 342 if(_all_convexity==linear_){ 343 _all_convexity = c; 344 } 345 else if(c!=linear_){ 346 if(_all_convexity!=c){ 347 _all_convexity = undet_; 348 } 349 } 350 } print()351 virtual void print(){}; print(int prec)352 virtual void print(int prec){}; 353 virtual void print(size_t index, int prec = 10) {}; 354 virtual void print(size_t i, size_t j, int prec = 10) {}; 355 356 357 358 void print_symbolic(bool endline = true, bool display_input = true); 359 }; 360 361 362 363 /* 364 From Table 1.1 in http://www2.math.uni-wuppertal.de/~xsc/preprints/prep_01_4.pdf 365 Replacing R* with +INF or -INF 366 */ 367 template<class T,typename enable_if<is_arithmetic<T>::value>::type* = nullptr> extended_plus(T x,T y)368 T extended_plus(T x, T y){ 369 370 if(x==numeric_limits<T>::max() && y==numeric_limits<T>::lowest()){ 371 throw invalid_argument("In function extended_plus cannot add +inf to -inf"); 372 } 373 if(x==numeric_limits<T>::lowest() && y==numeric_limits<T>::max()){ 374 throw invalid_argument("In function extended_plus cannot add -inf to +inf"); 375 } 376 //+INF 377 if(x==numeric_limits<T>::max() || y==numeric_limits<T>::max()){ 378 return numeric_limits<T>::max(); 379 } 380 if(x==numeric_limits<T>::lowest() || y==numeric_limits<T>::lowest()){ 381 return numeric_limits<T>::lowest(); 382 } 383 auto res = (x+y); 384 if (res>numeric_limits<T>::max()){ 385 return numeric_limits<T>::max(); 386 } 387 if (res<numeric_limits<T>::lowest()){ 388 return numeric_limits<T>::lowest(); 389 } 390 return res; 391 } 392 393 /* 394 From Table 1.2 in http://www2.math.uni-wuppertal.de/~xsc/preprints/prep_01_4.pdf 395 Replacing R* with +INF or -INF 396 */ 397 template<class T,typename enable_if<is_arithmetic<T>::value>::type* = nullptr> extended_minus(T x,T y)398 T extended_minus(T x, T y){ 399 400 if(x==numeric_limits<T>::max() && y==numeric_limits<T>::max()){ 401 return numeric_limits<T>::max(); 402 throw invalid_argument("In function extended_minus cannot substract +inf to +inf"); 403 } 404 if(x==numeric_limits<T>::lowest() && y==numeric_limits<T>::lowest()){ 405 return numeric_limits<T>::lowest(); 406 throw invalid_argument("In function extended_minus cannot substract -inf to -inf"); 407 } 408 //+INF 409 if(x==numeric_limits<T>::max() || y==numeric_limits<T>::lowest()){ 410 return numeric_limits<T>::max(); 411 } 412 if(x==numeric_limits<T>::lowest() || y==numeric_limits<T>::max()){ 413 return numeric_limits<T>::lowest(); 414 } 415 auto res = (x-y); 416 if (res>numeric_limits<T>::max()){ 417 return numeric_limits<T>::max(); 418 } 419 if (res<numeric_limits<T>::lowest()){ 420 return numeric_limits<T>::lowest(); 421 } 422 return res; 423 } 424 425 /* 426 From Table 1.3 in http://www2.math.uni-wuppertal.de/~xsc/preprints/prep_01_4.pdf 427 Replacing R* with +INF or -INF 428 */ 429 template<class T,typename enable_if<is_arithmetic<T>::value>::type* = nullptr> extended_mult(T x,T y)430 T extended_mult(T x, T y){ 431 //+INF 432 if(x==numeric_limits<T>::lowest() && y==numeric_limits<T>::lowest()){ 433 return numeric_limits<T>::max(); 434 } 435 if(x==numeric_limits<T>::lowest() && y<0){ 436 return numeric_limits<T>::max(); 437 } 438 if(y==numeric_limits<T>::lowest() && x<0){ 439 return numeric_limits<T>::max(); 440 } 441 if(x==numeric_limits<T>::lowest() && y==0){ 442 return numeric_limits<T>::lowest(); 443 } 444 if(y==numeric_limits<T>::lowest() && x==0){ 445 return numeric_limits<T>::lowest(); 446 } 447 if(x==numeric_limits<T>::max() && y==numeric_limits<T>::max()){ 448 return numeric_limits<T>::max(); 449 } 450 if(x==numeric_limits<T>::max() && y>=0){ 451 return numeric_limits<T>::max(); 452 } 453 if(y==numeric_limits<T>::max() && x>=0){ 454 return numeric_limits<T>::max(); 455 } 456 //-INF 457 if(x==numeric_limits<T>::lowest() && y==numeric_limits<T>::max()){ 458 return numeric_limits<T>::lowest(); 459 } 460 if(x==numeric_limits<T>::lowest() && y>=0){ 461 return numeric_limits<T>::lowest(); 462 } 463 if(y==numeric_limits<T>::lowest() && x>=0){ 464 return numeric_limits<T>::lowest(); 465 } 466 if(x==numeric_limits<T>::max() && y==numeric_limits<T>::lowest()){ 467 return numeric_limits<T>::lowest(); 468 } 469 if(x==numeric_limits<T>::max() && y<0){ 470 return numeric_limits<T>::lowest(); 471 } 472 if(y==numeric_limits<T>::max() && x<0){ 473 return numeric_limits<T>::lowest(); 474 } 475 if(x==numeric_limits<T>::max() && y==0){ 476 return numeric_limits<T>::max(); 477 } 478 if(y==numeric_limits<T>::max() && x==0){ 479 return numeric_limits<T>::max(); 480 } 481 if(y==0 && x==0){ 482 return 0; 483 } 484 auto res = (x*y); 485 if (res>numeric_limits<T>::max()){ 486 return numeric_limits<T>::max(); 487 } 488 if (res<numeric_limits<T>::lowest()){ 489 return numeric_limits<T>::lowest(); 490 } 491 return res; 492 } 493 494 template<class T,typename enable_if<is_same<T,Cpx>::value>::type* = nullptr> extended_minus(T x,T y)495 T extended_minus(T x, T y){ 496 T res; 497 res.real(extended_minus(x.real(),y.real())); 498 res.imag(extended_minus(x.imag(),y.imag())); 499 return res; 500 } 501 502 template<class T,typename enable_if<is_same<T,Cpx>::value>::type* = nullptr> extended_plus(T x,T y)503 T extended_plus(T x, T y){ 504 T res; 505 res.real(extended_plus(x.real(),y.real())); 506 res.imag(extended_plus(x.imag(),y.imag())); 507 return res; 508 } 509 510 template<class T,typename enable_if<is_same<T,Cpx>::value>::type* = nullptr> extended_mult(T x,T y)511 T extended_mult(T x, T y){ 512 T res; 513 res.real(extended_minus(extended_mult(x.real(),y.real()),extended_mult(x.imag(),y.imag()))); 514 res.imag(extended_plus(extended_mult(x.real(),y.imag()),extended_mult(x.imag(),y.real()))); 515 return res; 516 } 517 518 template<class T1, class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> get_product_range(shared_ptr<pair<T1,T1>> x,shared_ptr<pair<T2,T2>> y)519 shared_ptr<pair<T1,T1>> get_product_range(shared_ptr<pair<T1,T1>> x, shared_ptr<pair<T2,T2>> y){ 520 shared_ptr<pair<T1,T1>> res = make_shared<pair<T1,T1>>(); 521 T1 x1 = x->first; 522 T1 x2 = x->second; 523 T1 y1 = y->first; 524 T1 y2 = y->second; 525 T1 min1 = gravity::min(extended_mult(x1,y1), extended_mult(x1,y2)); 526 T1 min2 = gravity::min(extended_mult(x2,y1), extended_mult(x2,y2)); 527 T1 max1 = gravity::max(extended_mult(x1,y1), extended_mult(x1,y2)); 528 T1 max2 = gravity::max(extended_mult(x2,y1), extended_mult(x2,y2)); 529 res->first = gravity::min(min1,min2); 530 res->second = gravity::max(max1,max2); 531 return res; 532 } 533 534 template<class T1, class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 535 shared_ptr<pair<T2,T2>> get_product_range(shared_ptr<pair<T1,T1>> x, shared_ptr<pair<T2,T2>> y){ 536 shared_ptr<pair<T2,T2>> res = make_shared<pair<T2,T2>>(); 537 T2 x1 = x->first; 538 T2 x2 = x->second; 539 T2 y1 = y->first; 540 T2 y2 = y->second; 541 T2 min1 = gravity::min(extended_mult(x1,y1), extended_mult(x1,y2)); 542 T2 min2 = gravity::min(extended_mult(x2,y1), extended_mult(x2,y2)); 543 T2 max1 = gravity::max(extended_mult(x1,y1), extended_mult(x1,y2)); 544 T2 max2 = gravity::max(extended_mult(x2,y1), extended_mult(x2,y2)); 545 res->first = gravity::min(min1,min2); 546 res->second = gravity::max(max1,max2); 547 return res; 548 } 549 550 template<class T1, class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> get_div_range(shared_ptr<pair<T1,T1>> range1,shared_ptr<pair<T2,T2>> range2)551 shared_ptr<pair<T1,T1>> get_div_range(shared_ptr<pair<T1,T1>> range1, shared_ptr<pair<T2,T2>> range2){ 552 if(range2->first==numeric_limits<T2>::lowest() || range2->second==numeric_limits<T2>::max() 553 || range1->first==numeric_limits<T1>::lowest()|| range1->second==numeric_limits<T1>::max()){ 554 shared_ptr<pair<T1,T1>> res = make_shared<pair<T1,T1>>(); 555 res->first =numeric_limits<T1>::lowest(); 556 res->second =numeric_limits<T1>::max(); 557 return res; 558 } 559 auto inv_range2 = make_shared<pair<T2,T2>>(*range2); 560 inv_range2->first = 1./inv_range2->first; 561 inv_range2->second = 1./inv_range2->second; 562 return get_product_range<T1, T1, nullptr>(range1, inv_range2); 563 } 564 565 template<class T1, class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 566 shared_ptr<pair<T2,T2>> get_div_range(shared_ptr<pair<T1,T1>> range1, shared_ptr<pair<T2,T2>> range2){ 567 shared_ptr<pair<T2,T2>> res = make_shared<pair<T2,T2>>(); 568 if(range2->first==numeric_limits<T2>::lowest() || range2->second==numeric_limits<T2>::max() 569 || range1->first==numeric_limits<T1>::lowest()|| range1->second==numeric_limits<T1>::max()){ 570 res->first =numeric_limits<T2>::lowest(); 571 res->second =numeric_limits<T2>::max(); 572 return res; 573 } 574 auto inv_range2 = make_shared<pair<T2,T2>>(*range2); 575 inv_range2->first = 1./inv_range2->first; 576 inv_range2->second = 1./inv_range2->second; 577 return get_product_range<T2, T2, nullptr>(range1, inv_range2); 578 } 579 580 template<class T1, class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> get_plus_range(shared_ptr<pair<T1,T1>> x,shared_ptr<pair<T2,T2>> y)581 shared_ptr<pair<T1,T1>> get_plus_range(shared_ptr<pair<T1,T1>> x, shared_ptr<pair<T2,T2>> y){ 582 shared_ptr<pair<T1,T1>> res = make_shared<pair<T1,T1>>(); 583 T1 x1 = x->first; 584 T1 x2 = x->second; 585 T1 y1 = y->first; 586 T1 y2 = y->second; 587 res->first = extended_plus(x1,y1); 588 res->second = extended_plus(x2,y2); 589 return res; 590 } 591 592 template<class T1, class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 593 shared_ptr<pair<T2,T2>> get_plus_range(shared_ptr<pair<T1,T1>> x, shared_ptr<pair<T2,T2>> y){ 594 shared_ptr<pair<T2,T2>> res = make_shared<pair<T2,T2>>(); 595 T2 x1 = x->first; 596 T2 x2 = x->second; 597 T2 y1 = y->first; 598 T2 y2 = y->second; 599 res->first = extended_plus(x1,y1); 600 res->second = extended_plus(x2,y2); 601 return res; 602 } 603 604 605 template<class T1, class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> get_minus_range(shared_ptr<pair<T1,T1>> x,shared_ptr<pair<T2,T2>> y)606 shared_ptr<pair<T1,T1>> get_minus_range(shared_ptr<pair<T1,T1>> x, shared_ptr<pair<T2,T2>> y){ 607 shared_ptr<pair<T1,T1>> res = make_shared<pair<T1,T1>>(); 608 T1 x1 = x->first; 609 T1 x2 = x->second; 610 T1 y1 = y->first; 611 T1 y2 = y->second; 612 res->first = extended_minus(x1,y2); 613 res->second = extended_minus(x2,y1); 614 return res; 615 } 616 617 template<class T1, class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 618 shared_ptr<pair<T2,T2>> get_minus_range(shared_ptr<pair<T1,T1>> x, shared_ptr<pair<T2,T2>> y){ 619 shared_ptr<pair<T2,T2>> res = make_shared<pair<T2,T2>>(); 620 T2 x1 = x->first; 621 T2 x2 = x->second; 622 T2 y1 = y->first; 623 T2 y2 = y->second; 624 res->first = extended_minus(x1,y2); 625 res->second = extended_minus(x2,y1); 626 return res; 627 } 628 629 630 template<typename type> 631 class func: public func_{ 632 private: 633 /** Computes and stores the derivative of f with respect to variable v. Returns a pointer to the stored function. */ compute_derivative(const param_ & v)634 shared_ptr<func> compute_derivative(const param_& v){ 635 auto vid = v._name; 636 if(_dfdx->count(vid)!=0){ 637 return _dfdx->at(vid); 638 } 639 auto df = make_shared<func>(get_derivative(v)); 640 if(_is_vector){ 641 df->_is_vector=true;//TODO check this. 642 } 643 df->_evaluated = false; 644 df->allocate_mem(); 645 (*_dfdx)[vid] = df; 646 DebugOff( "First derivative with respect to " << v.get_name(false,false) << " = " << df->to_str() << endl); 647 return df; 648 } 649 650 public: 651 shared_ptr<expr<type>> _expr = nullptr; /**< Nonlinear part of the function, this points to the root node in _DAG */ 652 653 shared_ptr<map<string,shared_ptr<func>>> _dfdx = nullptr;/**< A map storing the derivatives indexed by variables' names */ 654 shared_ptr<vector<type>> _val = nullptr; /**< vector of values **/ 655 shared_ptr<pair<type,type>> _range = nullptr; /**< (Min,Max) values in vals **/ 656 shared_ptr<vector<pair<type,type>>> _all_range = nullptr; /**< Vector of (Min,Max) values for each instance of this func **/ 657 update_range()658 void update_range(){ 659 _range = make_shared<pair<type,type>>(make_pair<>(zero<type>().eval(), zero<type>().eval())); 660 } 661 662 func()663 func(){ 664 update_type(); 665 update_range(); 666 _cst = make_shared<constant<type>>(); 667 _lterms = make_shared<map<string, lterm>>(); 668 _qterms = make_shared<map<string, qterm>>(); 669 _pterms = make_shared<map<string, pterm>>(); 670 _vars = make_shared<map<string, pair<shared_ptr<param_>, unsigned>>>(); 671 _params = make_shared<map<string, pair<shared_ptr<param_>, unsigned>>>(); 672 _dfdx = make_shared<map<string,shared_ptr<func<type>>>>(); 673 _val = make_shared<vector<type>>(); 674 }; 675 676 update_type()677 void update_type() { 678 _type = func_c; 679 if(typeid(type)==typeid(bool)) { 680 _return_type = binary_; 681 return; 682 } 683 if(typeid(type)==typeid(short)) { 684 _return_type = short_; 685 return; 686 } 687 if(typeid(type)==typeid(int)) { 688 _return_type = integer_; 689 return; 690 } 691 if(typeid(type)==typeid(float)) { 692 _return_type = float_; 693 return; 694 } 695 if(typeid(type)==typeid(double)) { 696 _return_type = double_; 697 return; 698 } 699 if(typeid(type)==typeid(long double)) { 700 _return_type = long_; 701 return; 702 } 703 if(typeid(type)==typeid(Cpx)) { 704 _return_type = complex_; 705 return; 706 } 707 throw invalid_argument("Unsupported numerical function type"); 708 } 709 has_lifted_vars()710 bool has_lifted_vars() const{ 711 for (auto const& vp: *_vars){ 712 if(vp.second.first->is_lifted()) 713 return true; 714 } 715 return false; 716 } 717 is_constant()718 bool is_constant() const{ 719 return (_vars->empty()); 720 } 721 722 /** 723 Update the function indexing and its variables/parameters using the keep_ids vector of bool, only keep an index if it corresponding entry in keep_id is true. 724 @param[in] keep_ids vector of booleans, specifying which ids to keep 725 */ 726 void update_indices(const vector<bool>& keep_ids); 727 728 /** 729 Update the function indexing and its variables/parameters using the keep_ids vector of bool, only keep a row if it corresponding entry in keep_id is true. 730 @param[in] keep_ids vector of booleans, specifying which rows to keep 731 */ 732 void update_rows(const vector<bool>& keep_ids); 733 734 /** 735 Update the variables/parameters indexing using the set ids. 736 @param[in] ids indext set 737 */ 738 void update_var_indices(const indices& ids); 739 740 /** 741 Update the variables/parameters indexing using the set ids and using references from old_ids. 742 @param[in] ids indext set 743 */ 744 void update_var_indices(const indices& ids, const indices& old_ids); 745 746 /** 747 Index the function and its variables/parameters using the indices in ids 748 @param[in] ids indices 749 @return current function 750 */ in(const indices & ids)751 func& in(const indices& ids){//TODO assert each var and param that is not transposed has same dim as ids 752 // _nb_vars = 0; 753 // string key; 754 // auto iter = _vars->begin(); 755 // while (iter!=_vars->end()) { 756 // auto pair = (*iter++); 757 // auto v = pair.second.first; 758 // if (!v->is_indexed() && !v->_is_vector) {// i.e., it is not transposed 759 // v->index_in(ids); 760 // } 761 // } 762 // iter = _params->begin(); 763 // while (iter!=_params->end()) { 764 // auto pair = (*iter++); 765 // auto p = pair.second.first; 766 // if (!p->is_indexed() && !p->_is_transposed) {// i.e., it is not transposed 767 // p->index_in(ids); 768 // } 769 // } 770 _indices = make_shared<indices>(ids.deep_copy()); 771 _dim[0] = std::max(_dim[0], ids.size()); 772 if(_expr){// TODO take care of nonlinear part 773 _expr->in(ids); 774 } 775 if(_cst->is_function()){ 776 auto rhs_f = static_pointer_cast<func<type>>(_cst); 777 rhs_f->index_in(ids); 778 } 779 return *this; 780 } 781 782 783 /** Operators */ 784 bool operator==(const func& p) const { 785 if (_type!=p._type || _return_type!=p._return_type || _dim[0]!=p._dim[0] || _dim[1]!=p._dim[1] || _to_str!=p._to_str) return false; 786 if(_indices==p._indices) return true; /* accounts for both being nullptr */ 787 if((_indices && !p._indices) || (p._indices && !_indices) || (*_indices != *p._indices)) return false; 788 return true; 789 } 790 vec()791 func vec() const{ 792 auto f(*this); 793 if(func_is_param()){ 794 auto vi = f._vars->begin()->second.first; 795 if(!vi->_is_vector){ 796 vi->_is_vector = true; 797 vi->_name = "["+vi->_name+"]"; 798 } 799 } 800 f._is_vector = true; 801 return f; 802 } 803 get_expr()804 shared_ptr<expr<type>> get_expr() const{ 805 return _expr; 806 } 807 transpose()808 void transpose(){ 809 _is_transposed = !_is_transposed; 810 _is_vector = true; 811 auto temp = _dim[0]; 812 _dim[0] = _dim[1]; 813 _dim[1] = temp; 814 if(get_dim()==1){ 815 _is_vector = false; 816 } 817 for (auto &vp: *_vars) { 818 if(vp.second.first->_is_transposed){ 819 vp.second.first->transpose(); 820 } 821 } 822 for (auto &pp: *_params) { 823 if(pp.second.first->_is_transposed){ 824 pp.second.first->transpose(); 825 } 826 } 827 } 828 get_derivative(shared_ptr<constant_> ex,const param_ & v)829 func get_derivative(shared_ptr<constant_> ex, const param_& v) const{ 830 auto name = v.get_name(false,false); 831 if(ex->is_var()){ 832 auto vv = static_pointer_cast<param_>(ex); 833 if(vv->get_name(false,false)==name){ 834 return unit<type>(); 835 } 836 } 837 else if(ex->is_function()){ 838 auto f = static_pointer_cast<func>(ex); 839 return f->get_derivative(v); 840 } 841 else if(ex->is_uexpr()){ 842 func son; 843 auto uexp = static_pointer_cast<uexpr<type>>(ex); 844 if (uexp->_son->is_function()) { 845 auto f = static_pointer_cast<func>(uexp->_son); 846 son = (*f); 847 if(!son.has_var(v)){ 848 return func(); 849 } 850 } 851 else if(uexp->_son->is_var()) { 852 auto vv = static_pointer_cast<param_>(uexp->_son); 853 if(vv->get_name(false,false)==name){ 854 son.insert(v); 855 son.unbounded_range(); 856 son._indices = v._indices; 857 son._dim[0] = v._dim[0]; 858 son._dim[1] = v._dim[1]; 859 } 860 else{ 861 return func(); 862 } 863 864 } 865 else { 866 return func(); 867 } 868 // f(g(x))' = f'(g(x))*g'(x). 869 switch (uexp->_otype) { 870 case cos_: 871 return uexp->_coef*-1.*get_derivative(uexp->_son,v)*sin(son); 872 break; 873 case sin_: 874 return uexp->_coef*get_derivative(uexp->_son,v)*cos(son); 875 break; 876 case sqrt_: 877 return uexp->_coef*get_derivative(uexp->_son,v)/(2.*sqrt(son)); 878 break; 879 case exp_:{ 880 return uexp->_coef*get_derivative(uexp->_son,v)*exp(son); 881 break; 882 } 883 case log_: 884 return uexp->_coef*get_derivative(uexp->_son,v)/son; 885 break; 886 case abs_: 887 return uexp->_coef*df_abs(son)*get_derivative(uexp->_son,v); 888 break; 889 case relu_: 890 return uexp->_coef*unit_step(son)*get_derivative(uexp->_son,v); 891 break; 892 case df_abs_: 893 return zero<type>(); 894 break; 895 case unit_step_: 896 return zero<type>(); 897 break; 898 default: 899 throw invalid_argument("Unsupported unary operation"); 900 break; 901 } 902 } 903 else if(ex->is_bexpr()){ 904 func lson, rson; 905 auto bexp = static_pointer_cast<bexpr<type>>(ex); 906 if (bexp->_lson->is_function()) { 907 auto f = static_pointer_cast<func>(bexp->_lson); 908 lson = *f; 909 } 910 else if(bexp->_lson->is_var()) { 911 auto vv = static_pointer_cast<param_>(bexp->_lson); 912 if(vv->get_name(false,false)==name){ 913 lson.insert(v); 914 lson.unbounded_range(); 915 lson._indices = v._indices; 916 lson._dim[0] = v._dim[0]; 917 lson._dim[1] = v._dim[1]; 918 } 919 } 920 else { 921 return func(); 922 } 923 if (bexp->_rson->is_function()) { 924 auto f = static_pointer_cast<func>(bexp->_rson); 925 rson = *f; 926 } 927 else if(bexp->_rson->is_var()) { 928 auto vv = static_pointer_cast<param_>(bexp->_rson); 929 if(vv->get_name(false,false)==name){ 930 rson.insert(v); 931 rson.unbounded_range(); 932 rson._indices = v._indices; 933 rson._dim[0] = v._dim[0]; 934 rson._dim[1] = v._dim[1]; 935 } 936 } 937 else { 938 return func(); 939 } 940 if(!lson.has_var(v) && !rson.has_var(v)){ 941 return func(); 942 } 943 switch (bexp->_otype) { 944 case plus_: 945 return bexp->_coef*(lson.get_derivative(v) + rson.get_derivative(v)); 946 break; 947 case minus_: 948 return bexp->_coef*(lson.get_derivative(v) - rson.get_derivative(v)); 949 break; 950 case product_:{ 951 return bexp->_coef*(lson.get_derivative(v)*(rson) + (lson)*rson.get_derivative(v)); 952 // f'g + fg' 953 break; 954 } 955 case div_: 956 return bexp->_coef*((lson.get_derivative(v)*(rson) - (lson)*rson.get_derivative(v))/(rson*rson)); 957 // (f'g - fg')/g^2 958 break; 959 case min_: 960 return 0.5*bexp->_coef*(lson.get_derivative(v) + rson.get_derivative(v) - df_abs(lson-rson)*(lson.get_derivative(v)-rson.get_derivative(v))); 961 break; 962 case max_: 963 return 0.5*bexp->_coef*(lson.get_derivative(v) + rson.get_derivative(v) + df_abs(lson-rson)*(lson.get_derivative(v)-rson.get_derivative(v))); 964 break; 965 case power_: 966 if (!rson.is_number()) { 967 throw invalid_argument("Function in exponent not supported yet.\n"); 968 } 969 // auto exponent = poly_eval(_rson); 970 // return (exponent*get_poly_derivative(_lson,v)*power(*_lson, exponent-1));// nf'f^n-1 971 break; 972 default: 973 throw invalid_argument("unsupported operation"); 974 break; 975 } 976 } 977 return func(); 978 } 979 get_stored_derivative(const string & vid)980 shared_ptr<func> get_stored_derivative(const string& vid) const{ /**< Returns the stored derivative with respect to variable v. */ 981 auto it = _dfdx->find(vid); 982 if (it!=_dfdx->end()) { 983 return it->second; 984 } 985 else { 986 throw invalid_argument("No derivatives stored!\n"); 987 } 988 } 989 990 // func_ get_derivative(const param_& v) const; /**< Computes and returns the derivative with respect to variable v. */ 991 992 // func_ get_dfdx(const param_& v); /**< Computes all derivatives and returns a copy of the derivative with respect to variable v. */ 993 994 995 template<typename T=type, 996 typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr> init_range()997 void init_range() { 998 _range = make_shared<pair<type,type>>(make_pair<>(numeric_limits<type>::max(), numeric_limits<type>::lowest())); 999 } 1000 1001 1002 template<class T=type, class = typename enable_if<is_same<T, Cpx>::value>::type> init_range()1003 void init_range() { 1004 _range = make_shared<pair<type,type>>(make_pair<>(Cpx(numeric_limits<double>::max(), numeric_limits<double>::max()), Cpx(numeric_limits<double>::lowest(), numeric_limits<double>::lowest()))); 1005 } 1006 1007 template<typename T=type, 1008 typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr> unbounded_range()1009 void unbounded_range() { 1010 _range = make_shared<pair<type,type>>(make_pair<>(numeric_limits<type>::lowest(), numeric_limits<type>::max())); 1011 } 1012 1013 1014 template<class T=type, class = typename enable_if<is_same<T, Cpx>::value>::type> unbounded_range()1015 void unbounded_range() { 1016 _range = make_shared<pair<type,type>>(make_pair<>(Cpx(numeric_limits<double>::lowest(), numeric_limits<double>::lowest()), Cpx(numeric_limits<double>::max(), numeric_limits<double>::max()))); 1017 } 1018 1019 /** 1020 Recompute range based on stored values. 1021 */ reset_range()1022 void reset_range(){ 1023 init_range(); 1024 if(is_matrix_indexed()){ 1025 for(auto i = 0; i<_indices->_ids->size();i++){ 1026 for(auto j = 0; j<_indices->_ids->at(i).size();j++){ 1027 auto idx = _indices->_ids->at(i).at(j); 1028 auto v = _val->at(idx); 1029 if(_range->first > v){ 1030 _range->first = v; 1031 } 1032 if(_range->second < v){ 1033 _range->second = v; 1034 } 1035 } 1036 } 1037 } 1038 else if(is_indexed()){ 1039 for(auto i = 0; i<_indices->_ids->at(0).size();i++){ 1040 auto idx = _indices->_ids->at(0).at(i); 1041 auto v = _val->at(idx); 1042 if(_range->first > v){ 1043 _range->first = v; 1044 } 1045 if(_range->second < v){ 1046 _range->second = v; 1047 } 1048 } 1049 } 1050 else { 1051 for (auto v:*_val) { 1052 if(_range->first > v){ 1053 _range->first = v; 1054 } 1055 if(_range->second < v){ 1056 _range->second = v; 1057 } 1058 } 1059 } 1060 } 1061 1062 1063 /** Return a vector of the current model variables in instance inst_i of func 1064 @param[in] inst_i: function instance 1065 @param[in] var_name: ignore var with var_name 1066 **/ get_x_ignore(int inst_i,string var_name)1067 vector<double> get_x_ignore(int inst_i, string var_name){ 1068 vector<double> res; 1069 size_t posv; 1070 double xv; 1071 for(auto &it: *_vars) 1072 { 1073 auto v = it.second.first; 1074 if(v->_name==var_name){ 1075 continue; 1076 } 1077 if(v->_is_vector) 1078 { 1079 for (auto i=0;i<v->_dim[0];i++) 1080 { 1081 posv=i; 1082 v->get_double_val(posv, xv); 1083 res.push_back(xv); 1084 } 1085 1086 } 1087 else 1088 { 1089 posv=v->get_id_inst(inst_i); 1090 v->get_double_val(posv, xv); 1091 res.push_back(xv); 1092 1093 } 1094 } 1095 return res; 1096 } 1097 1098 /** Return a vector of the current model variables in instance inst_i of func 1099 @param[in] inst_i: 1100 **/ get_x(int inst_i)1101 vector<double> get_x(int inst_i){ 1102 vector<double> res; 1103 size_t posv; 1104 double xv; 1105 for(auto &it: *_vars) 1106 { 1107 auto v = it.second.first; 1108 if(v->_is_vector) 1109 { 1110 for (auto i=0;i<v->_dim[0];i++) 1111 { 1112 posv=i; 1113 v->get_double_val(posv, xv); 1114 res.push_back(xv); 1115 } 1116 1117 } 1118 else 1119 { 1120 posv=v->get_id_inst(inst_i); 1121 v->get_double_val(posv, xv); 1122 res.push_back(xv); 1123 1124 } 1125 } 1126 return res; 1127 } 1128 /** sets model variables in instance inst_i of func to x 1129 @param[in] inst_i: 1130 @param[in] x: 1131 **/ set_x(int inst_i,const vector<double> & x)1132 void set_x(int inst_i, const vector<double>& x){ 1133 size_t posv; 1134 int counter=0; 1135 for(auto &it: *_vars) 1136 { 1137 auto v = it.second.first; 1138 if(v->_is_vector) 1139 { 1140 for (auto i=0;i<v->_dim[0];i++) 1141 { 1142 posv=i; 1143 v->set_double_val(posv, x[counter++]); 1144 } 1145 1146 } 1147 else 1148 { 1149 posv=v->get_id_inst(inst_i); 1150 v->set_double_val(posv, x[counter++]); 1151 1152 } 1153 } 1154 } 1155 /** Get a set of active points by uniformly discretizing the variable domain 1156 @param[in] nb_discr: 1157 @param[in] nb_inst: 1158 @param[in] d: 1159 @return True if found at least one active point after discretizing 1160 Assumes that no variable is of vector format 1161 Assumes soc, rotated soc variables are ordered in a standard form (x_1^2+x_2^2<=x_3x_4) 1162 To generalize to any convex function, use newton raphson and loop over all varaibles twice (fix different variabels to discretized values each time) 1163 If looping over all variables twice, have to check for feasibility of an assignment 1164 **/ get_grid_discretize(int nb_discr,int nb_inst,vector<double> d)1165 bool get_grid_discretize(int nb_discr, int nb_inst, vector<double> d){ 1166 // res = gradf(x*)*(x-x*) + f(x*) 1167 1168 size_t posv; 1169 double lb, ub, xv; 1170 vector<double> xgrid; 1171 bool solution; 1172 const double zero_tol=1e-8; 1173 solution=true; 1174 xgrid.clear(); 1175 int counter=0; 1176 for(auto it=_vars->begin(); it!=_vars->end(); it++) 1177 { 1178 auto v = (*it).second.first; 1179 posv=v->get_id_inst(nb_inst); 1180 if(next(it) != _vars->end()) 1181 { 1182 1183 ub=v->get_double_ub(posv); 1184 lb=v->get_double_lb(posv); 1185 xv=lb+d[counter++]*(ub-lb)/nb_discr; 1186 v->set_double_val(posv, xv); 1187 xgrid.push_back(xv); 1188 } 1189 else 1190 { 1191 if(check_soc()) 1192 { 1193 xv=sqrt(pow(xgrid[0],2)+pow(xgrid[1],2)); 1194 v->set_double_val(posv, xv); 1195 xgrid.push_back(xv); 1196 1197 } 1198 if(check_rotated_soc()) 1199 { 1200 if(std::abs(xgrid[2])>=zero_tol) 1201 { 1202 xv=(pow(xgrid[0],2)+pow(xgrid[1],2))/(xgrid[2]); 1203 v->set_double_val(posv, xv); 1204 xgrid.push_back(xv); 1205 } 1206 else 1207 { 1208 solution=false; 1209 } 1210 1211 } 1212 if(_name.find("_squared")!=std::string::npos) 1213 { 1214 xv=pow(xgrid[0],2); 1215 v->set_double_val(posv, xv); 1216 xgrid.push_back(xv); 1217 } 1218 } 1219 } 1220 // for(auto i=0;i<xgrid.size();i++) 1221 // { 1222 // DebugOn(xgrid[i]<<"\t"); 1223 // 1224 // } 1225 // DebugOn(endl); 1226 return solution; 1227 } 1228 1229 /** Returns a set of OA cuts at the current model variables for a _squared constraint. This if is specific to constraints of the form ay- bx^2 or bx^2-ay 1230 @param[in] nb_discr: 1231 @return func of OA cuts ( for all func instance, symbolic) 1232 Note:: assumes coef_x is same for all function instances 1233 **/ get_outer_app_squared()1234 func<> get_outer_app_squared() 1235 { 1236 func<> res; 1237 vector<double> lb,ub; 1238 double coef_x, coef_x_times2; 1239 if(is_quadratic() && _lterms->size()==1 && _qterms->size()==1 && _qterms->begin()->second._p->first==_qterms->begin()->second._p->second) 1240 { 1241 1242 auto y=_lterms->begin()->second; 1243 res.insert(y); 1244 auto x=_qterms->begin()->second._p->first; 1245 1246 double coef_x=eval_coef(_qterms->begin()->second._coef, 0); 1247 double coef_x_times2=coef_x*2; 1248 param<type> xstar("xstar_"+x->_name); 1249 xstar.in(*x->_indices); 1250 xstar.copy_vals(x); 1251 if(_qterms->begin()->second._sign){ 1252 res-=coef_x*xstar*xstar; 1253 } 1254 else{ 1255 res+=coef_x*xstar*xstar; 1256 } 1257 1258 1259 1260 res.insert(_qterms->begin()->second._sign, coef_x_times2*xstar, *x); 1261 // res.print(); 1262 1263 // DebugOn("Sign x2 "<<_qterms->begin()->second._sign<<endl); 1264 // DebugOn("Coefficient x2 "<<coef_x<<endl); 1265 // DebugOn("Coefficient x2 times 2 "<<coef_x_times2<<endl); 1266 1267 } 1268 1269 return res; 1270 1271 } 1272 1273 /** Returns an outer-approximation of the function using the current value of the variables and index it using the specified index set 1274 @param[in] idx: index set for indexing the symbolic OA cut 1275 **/ 1276 func<type> get_outer_app(const indices& idx, bool scale=false){ 1277 const double zero_tol=1e-8; 1278 double t, scale_fact=1; 1279 if(scale) 1280 scale_fact=1E3; 1281 auto cpy = *this; 1282 auto keep = this->_indices->get_common_refs(idx); 1283 1284 for(auto &it: *cpy._vars){ 1285 auto v = it.second.first; 1286 if(!v->_is_vector){ 1287 v->_indices->filter_refs(keep); 1288 } 1289 } 1290 1291 func<type> res; // res = gradf(x*)*(x-x*) + f(x*) 1292 param<type> f_xstar("f_xstar_"+_name); 1293 f_xstar = cpy; 1294 f_xstar._indices->filter_refs(keep); 1295 for(auto &it: *cpy._vars){ 1296 auto v = it.second.first; 1297 param<type> xstar("xstar_"+v->_name); 1298 xstar.in(*v->_indices); 1299 xstar.copy_vals(v); 1300 param<type> df_xstar("df_xstar_"+v->_name); 1301 auto df = *compute_derivative(*v); 1302 df.uneval(); 1303 df.eval_all(); 1304 indices df_xstar_ind("df_xstar"+v->_name+"_ind"); 1305 for(auto key:*(cpy._indices->_keys)){ 1306 df_xstar_ind.add(key); 1307 } 1308 1309 df_xstar.in(df_xstar_ind); 1310 df_xstar.copy_vals(df); 1311 df_xstar._indices->filter_refs(keep); 1312 for(auto key:*(df_xstar._indices->_keys)){ 1313 t=(df_xstar.eval(key))*scale_fact; 1314 df_xstar.set_val(key, t); 1315 1316 } 1317 switch (v->get_intype()) { 1318 case binary_: 1319 1320 res += df_xstar*(*static_pointer_cast<var<bool>>(v)); 1321 break; 1322 case short_: 1323 res += df_xstar*(*static_pointer_cast<var<short>>(v)); 1324 break; 1325 case integer_: 1326 res += df_xstar*(*static_pointer_cast<var<int>>(v)); 1327 break; 1328 case float_: 1329 res += df_xstar*(*static_pointer_cast<var<float>>(v)); 1330 break; 1331 break; 1332 case double_: 1333 res += df_xstar*(*static_pointer_cast<var<double>>(v)); 1334 break; 1335 default: 1336 break; 1337 } 1338 1339 res -= df_xstar*xstar; 1340 } 1341 res += f_xstar*scale_fact; 1342 res.index_in(idx); 1343 if(!res._cst->is_zero() && res._cst->is_function()){ 1344 param<type> rhs("xstar_rhs_"+_name); 1345 auto rhs_f = static_pointer_cast<func<type>>(res._cst); 1346 rhs_f->eval_all(); 1347 rhs = *rhs_f; 1348 res._cst = rhs.copy(); 1349 } 1350 // merge_vars(r 1351 // es); 1352 1353 // for(auto &it: *_vars){ 1354 // auto v = it.second.first; 1355 // auto vname=v->_name; 1356 // DebugOn(vname<<endl); 1357 // } 1358 return res; 1359 } 1360 1361 1362 1363 1364 1365 1366 1367 1368 func<type> get_outer_app_insti(size_t nb_inst, bool scale=false){ /**< Returns an outer-approximation of the function using the current value of the variables **/ 1369 func<type> res; // res = gradf(x*)*(x-x*) + f(x*) 1370 const double active_tol=1e-8; 1371 double f_xstar, xv, dfv; 1372 vector<double> xcurrent, dfvector; 1373 int counter; 1374 counter=0; 1375 1376 uneval(); 1377 f_xstar=eval(nb_inst); 1378 //DebugOn("F_xstar in func.h\t"<<f_xstar<<endl); 1379 size_t posv; 1380 1381 1382 for(auto &it: *_vars){ 1383 auto v = it.second.first; 1384 indices ids("ids"); 1385 auto key=v->_indices->_keys; 1386 1387 dfvector.clear(); 1388 auto df = *compute_derivative(*v); 1389 df.eval_all(); 1390 if(v->_is_vector) 1391 { 1392 1393 1394 for (auto i=0;i<v->_dim[0];i++) 1395 { 1396 posv=i; 1397 v->get_double_val(posv, xv); 1398 xcurrent.push_back(xv); 1399 1400 df.uneval(); 1401 dfv=df.eval(i); 1402 ids.add((*key)[posv]); 1403 dfvector.push_back(dfv); 1404 if(scale) //assuming con is the SDP cut as it is the only nonconvex one 1405 { 1406 res -= dfv*xv*1E3; 1407 } 1408 else{ 1409 res -= dfv*xv; 1410 } 1411 } 1412 } 1413 else if(!(v->_is_vector)) 1414 { 1415 posv=v->get_id_inst(nb_inst); 1416 v->get_double_val(posv, xv); 1417 xcurrent.push_back(xv); 1418 ids.add((*key)[posv]); 1419 df.uneval(); 1420 dfv=df.eval(nb_inst); 1421 dfvector.push_back(dfv); 1422 if(scale) //assuming con is the SDP cut as it is the only nonconvex one 1423 { 1424 res -= dfv*xv*1E3; 1425 } 1426 else{ 1427 res -= dfv*xv; 1428 } 1429 } 1430 1431 param<type> df_xstar("df_xstar"+v->_name); 1432 df_xstar.in(ids); 1433 1434 for(auto i=0;i<dfvector.size();i++) 1435 { 1436 if(scale) //assuming con is the SDP cut as it is the only nonconvex one 1437 { 1438 df_xstar.set_double_val(i, dfvector[i]*1E3); 1439 } 1440 else{ 1441 df_xstar.set_double_val(i, dfvector[i]); 1442 } 1443 } 1444 1445 1446 switch (v->get_intype()) { 1447 case binary_: 1448 res += df_xstar.tr()*(*static_pointer_cast<var<bool>>(v)).in(ids); 1449 break; 1450 case short_: 1451 res += df_xstar.tr()*(*static_pointer_cast<var<short>>(v)).in(ids); 1452 break; 1453 case integer_: 1454 res += df_xstar.tr()*(*static_pointer_cast<var<int>>(v)).in(ids); 1455 break; 1456 case float_: 1457 res += df_xstar.tr()*(*static_pointer_cast<var<float>>(v)).in(ids); 1458 break; 1459 break; 1460 case double_: 1461 // res += df_xstar*(*static_pointer_cast<param<double>>(v)).in(ids); 1462 res += df_xstar.tr()*(*static_pointer_cast<var<double>>(v)).in(ids); 1463 break; 1464 // case long_: 1465 // res += df_xstar*(*static_pointer_cast<param<long double>>(v)).in(ids); 1466 // break; 1467 // case complex_: 1468 // res += df_xstar*(*static_pointer_cast<param<Cpx>>(v)).in(ids); 1469 // break; 1470 default: 1471 break; 1472 } 1473 1474 1475 1476 } 1477 1478 //if(f_xstar>=active_tol) 1479 if(scale) //assuming con is the SDP cut as it is the only nonconvex one 1480 { 1481 res += f_xstar*1E3; 1482 } 1483 else{ 1484 res += f_xstar; 1485 } 1486 1487 1488 1489 1490 indices res_ind("res_ind"); 1491 res_ind.add(to_string(nb_inst)); 1492 res._indices=make_shared<gravity::indices>(res_ind); 1493 1494 res.eval_all(); 1495 res.uneval(); 1496 // DebugOn("printin res in func.h"<<endl); 1497 // res.print(); 1498 // res.merge_vars(*this); 1499 // DebugOn("Eval of OA_cut in get_outer_app_insti\t"<<res.eval(0)<<endl); 1500 1501 1502 1503 1504 //res.print(); 1505 // DebugOn("Xcurrent from get_outer_app_insti"<<endl); 1506 // for(auto i=0;i<xcurrent.size();i++) 1507 // DebugOn(xcurrent[i]<<"\t"); 1508 // DebugOn(endl); 1509 // DebugOn("DF at Xcurrent from get_outer_app_insti"<<endl); 1510 // for(auto i=0;i<xcurrent.size();i++) 1511 // DebugOn(dfvector[i]<<"\t"); 1512 // DebugOn(endl); 1513 return res; 1514 } 1515 1516 /** Fill the coefficients corresponding to the OA cuts of a symbolic constraints */ get_outer_coef(size_t nb_inst,vector<double> & c,double & c0)1517 void get_outer_coef(size_t nb_inst, vector<double>& c, double& c0){ /**< Returns an outer-approximation of the function using the current value of the variables **/ 1518 double f_xstar, xv, dfv; 1519 uneval(); 1520 f_xstar=eval(nb_inst); 1521 c.clear(); 1522 // c0=f_xstar; 1523 //DebugOn("F_xstar in func.h\t"<<f_xstar<<endl); 1524 size_t posv; 1525 1526 1527 for(auto &it: *_vars){ 1528 auto v = it.second.first; 1529 auto df = compute_derivative(*v); 1530 if(v->_is_vector) 1531 { 1532 for (auto i=0;i<v->_dim[0];i++) 1533 { 1534 v->get_double_val(i, xv); 1535 df->uneval(); 1536 dfv=df->eval(i); 1537 c.push_back(dfv); 1538 c0-=dfv*xv; 1539 } 1540 } 1541 else { 1542 posv=v->get_id_inst(nb_inst); 1543 v->get_double_val(posv, xv); 1544 df->uneval(); 1545 dfv=df->eval(nb_inst); 1546 c.push_back(dfv); 1547 c0-=dfv*xv; 1548 } 1549 } 1550 c0+=f_xstar; 1551 //if(f_xstar>=active_tol) 1552 // if(scale) //assuming con is the SDP cut as it is the only nonconvex one 1553 // { 1554 // res += f_xstar*1E3; 1555 // } 1556 // else{ 1557 // res += f_xstar; 1558 // } 1559 // 1560 } 1561 1562 1563 1564 1565 1566 1567 1568 1569 /** Finds a vector of outer points perturbing along each direction */ 1570 //Algorithm finds an outer point for each index of each variable if available 1571 //First, if available,the outer point is at least at a distance perturb_distance greater than original value of variable 1572 //Else, if available, the algorithm returns any outer point produced by perturbing variable 1573 //Else, the algorithm does not return anything 1574 //Interior and outer point clasification depends on constraint type (\geq 0 or \leq 0) as input by ctype get_outer_point(size_t nb_inst,ConstraintType ctype)1575 vector<vector<double> > get_outer_point(size_t nb_inst, ConstraintType ctype) 1576 { 1577 vector<vector<double> > res(_nb_vars); 1578 vector<double> xcurrent, ub_v, lb_v; 1579 const int max_iter=1000; 1580 const double step_tol=1e-6, step_init=1e-3, perturb_dist=1e-3, zero_tol=1e-6; 1581 double step, f_start, xv=0,xv_p=0,f,ub,lb, fnew, dfdv; 1582 int count=0, iter, sign, iter_dir; 1583 bool perturb=true, dir; 1584 f_start=eval(nb_inst); 1585 xcurrent=get_x(nb_inst); 1586 int res_count=0; 1587 1588 1589 //No backtracking 1590 1591 1592 //Once feasible direction is found algorithm does not reverse direction. So shall work from any current point only for convex function and will work to identify one outer point, not necessarily at greater than perturb_dist from an active point for any nonconvex function 1593 1594 //Perturb so that distance between new point and current point is greater than perturb dist 1595 for(auto &it: *_vars) 1596 { 1597 perturb=true; 1598 dir=false; 1599 iter=0; 1600 auto v = it.second.first; 1601 size_t posv=v->get_id_inst(nb_inst); 1602 xv=xcurrent[count]; 1603 step=step_tol; 1604 iter_dir=0; 1605 ub=v->get_double_ub(posv); 1606 lb=v->get_double_lb(posv); 1607 auto df = *compute_derivative(*v); 1608 df.uneval(); 1609 dfdv=df.eval(nb_inst); 1610 // if interval zero do not perturb, perturb=false, else if x at upper bound (within perturb_dist) do not step out but set sign=-1,else if x at lower bound set sign=1, else go to white loop 1611 if((ub-lb)<=perturb_dist) 1612 { 1613 dir=false; 1614 } 1615 else if((xv-lb)<=perturb_dist) 1616 { 1617 sign=1; 1618 dir=true; 1619 1620 if(ctype==leq && dfdv<0) 1621 { 1622 dir=false; 1623 } 1624 else if(ctype==geq && dfdv>0) 1625 { 1626 dir=false; 1627 } 1628 1629 } 1630 else if((ub-xv)<=perturb_dist) 1631 { 1632 sign=-1; 1633 dir=true; 1634 if(ctype==leq && dfdv<0) 1635 { 1636 dir=false; 1637 } 1638 else if(ctype==geq && dfdv>0) 1639 { 1640 dir=false; 1641 } 1642 1643 } 1644 else 1645 { 1646 if(ctype==leq) 1647 { 1648 if(dfdv>0) 1649 { 1650 dir=true; 1651 sign=1; 1652 1653 } 1654 else if(dfdv<0) 1655 { 1656 dir=true; 1657 sign=-1; 1658 } 1659 } 1660 else if(ctype==geq) 1661 { 1662 if(dfdv<0) 1663 { 1664 dir=true; 1665 sign=1; 1666 1667 } 1668 else if(dfdv>0) 1669 { 1670 dir=true; 1671 sign=-1; 1672 } 1673 1674 } 1675 } 1676 if(dir) 1677 { 1678 step=dfdv; 1679 perturb=false; 1680 f=f_start; 1681 while(!perturb && iter<=max_iter) 1682 { 1683 if(sign==1) 1684 { 1685 xv=std::min(xv*(1+step), ub); 1686 v->set_double_val(posv, xv); 1687 } 1688 else 1689 { 1690 xv=std::max(xv*(1-step), lb); 1691 v->set_double_val(posv, xv); 1692 } 1693 uneval(); 1694 fnew=eval(nb_inst); 1695 if(ctype==leq) 1696 { 1697 if(fnew>zero_tol && std::abs(xv-xcurrent[count])>=perturb_dist) 1698 { 1699 perturb=true; 1700 xv_p=xv; 1701 break; 1702 } 1703 else if(fnew>f) 1704 { 1705 f=fnew; 1706 xv_p=xv; 1707 } 1708 else if(fnew<=f) 1709 { 1710 perturb=false; 1711 break; 1712 } 1713 } 1714 if(ctype==geq) 1715 { 1716 if(fnew<(zero_tol*(-1)) && std::abs(xv-xcurrent[count])>=perturb_dist) 1717 { 1718 perturb=true; 1719 xv_p=xv; 1720 break; 1721 } 1722 else if(fnew<f) 1723 { 1724 f=fnew; 1725 xv_p=xv; 1726 } 1727 else if(fnew>=f) 1728 { 1729 perturb=false; 1730 break; 1731 } 1732 } 1733 iter++; 1734 } 1735 if(perturb==true || (f>zero_tol && ctype==leq) || (f<(zero_tol*(-1)) && ctype==geq) ) 1736 { 1737 for(auto i=0;i<count;i++) 1738 res[res_count].push_back(xcurrent[i]); 1739 res[res_count].push_back(xv_p); 1740 for(auto i=count+1;i<_nb_vars;i++) 1741 res[res_count].push_back(xcurrent[i]); 1742 res_count++; 1743 uneval(); 1744 //DebugOn("fvalue at pos "<<nb_inst<<" at the outer point\t"<<eval(nb_inst)<<endl); 1745 1746 } 1747 } 1748 v->set_double_val(posv, xcurrent[count]); 1749 f=f_start; 1750 count++; 1751 } 1752 return(res); 1753 } 1754 /** Newton Raphson to solve for xsolution such that f(xsolution)=0 1755 @param[in] x: Current guess from which newton raphson is launched 1756 @param[in] vname: Name of variable in which the algorithm moves to solve f(x)=0 1757 @param[in] vpos: If variabel is vector type, vpos gives index of variable 1758 @param[in] nb_inst: instance number 1759 @param[in] ctype: 1760 @return Pair.first True if line search successfully solved. Pair.secod xsolution 1761 Note modifies current point to xsolution 1762 **/ newton_raphson(const vector<double> & x,string vname,size_t vpos,size_t nb_inst,ConstraintType ctype)1763 pair<bool, vector<double>> newton_raphson(const vector<double>& x, string vname, size_t vpos, size_t nb_inst, ConstraintType ctype) 1764 { 1765 pair<bool, vector<double>> res; 1766 vector<double> xk, xsolution; 1767 double xvk, xvk1, fk, dfdvk, ub,lb; 1768 const int max_iter=10000; 1769 const double active_tol=1e-12,zero_tol=1e-12; 1770 size_t posvk; 1771 1772 int counter=0,iter=0; 1773 bool solution=false; 1774 1775 set_x(nb_inst, x); 1776 xk=x; 1777 xsolution=x; 1778 fk=eval(nb_inst); 1779 auto vk=_vars->at(vname).first; 1780 1781 auto df = *compute_derivative(*vk); 1782 if(vk->_is_vector) 1783 { 1784 posvk=vpos; 1785 df.uneval(); 1786 dfdvk=df.eval(posvk); 1787 } 1788 else 1789 { 1790 posvk=vk->get_id_inst(nb_inst); 1791 df.uneval(); 1792 dfdvk=df.eval(nb_inst); 1793 } 1794 vk->get_double_val(posvk, xvk); 1795 ub=vk->get_double_ub(posvk); 1796 lb=vk->get_double_lb(posvk); 1797 1798 1799 while(iter<=max_iter && !solution) 1800 { 1801 vk->set_double_val(posvk, xvk); 1802 uneval(); 1803 fk=eval(nb_inst); 1804 if(std::abs(fk)<=active_tol) 1805 { 1806 solution=true; 1807 break; 1808 } 1809 1810 auto df = *compute_derivative(*vk); 1811 df.uneval(); 1812 1813 if(vk->_is_vector) 1814 { 1815 dfdvk=df.eval(posvk); 1816 } 1817 else 1818 { 1819 dfdvk=df.eval(nb_inst); 1820 } 1821 if(std::abs(dfdvk)>=zero_tol) 1822 xvk1=xvk-fk/dfdvk; 1823 else 1824 break; 1825 1826 if(std::abs(xvk1-xvk)<=zero_tol) 1827 break; 1828 if(xvk1>=ub) 1829 xvk=ub; 1830 else if (xvk1<=lb) 1831 xvk=lb; 1832 else 1833 xvk=xvk1; 1834 1835 1836 iter++; 1837 1838 } 1839 if(solution) 1840 { 1841 xsolution=get_x(nb_inst); 1842 } 1843 1844 1845 res.first=solution; 1846 res.second=xsolution; 1847 counter=0; 1848 return res; 1849 } 1850 /** Get any active point such that f(xsolution)=0 1851 @param[in] nb_inst: instance number 1852 @param[in] ctype: 1853 @return Pair.first True if active point found. Pair.second 1854 Note modifies current point to xsolution 1855 **/ get_any_active_point(size_t nb_inst,ConstraintType ctype)1856 pair<bool,vector<double>> get_any_active_point(size_t nb_inst, ConstraintType ctype) 1857 { 1858 pair<bool, vector<double>> res; 1859 vector<double> xcurrent; 1860 size_t posv; 1861 xcurrent=get_x(nb_inst); 1862 res.first=false; 1863 for(auto &it: *_vars) 1864 { 1865 auto vname=it.first; 1866 auto v=it.second.first; 1867 1868 if(v->_is_vector) 1869 { 1870 for (auto i=0;i<v->_dim[0];i++) 1871 { 1872 posv=i; 1873 auto res_nr=newton_raphson(xcurrent, vname, posv, nb_inst, ctype); 1874 1875 if(res_nr.first==true) 1876 { 1877 1878 res.first=true; 1879 res.second=res_nr.second; 1880 break; 1881 } 1882 1883 } 1884 1885 } 1886 else 1887 { 1888 posv=0; 1889 1890 auto res_nr=newton_raphson(xcurrent, vname, posv, nb_inst, ctype); 1891 1892 if(res_nr.first==true) 1893 { 1894 res.first=true; 1895 res.second=res_nr.second; 1896 break; 1897 } 1898 } 1899 if(res.first==true) 1900 break; 1901 1902 } 1903 return res; 1904 } 1905 get_active_point_outer(size_t nb_inst,ConstraintType ctype)1906 vector<vector<double> > get_active_point_outer(size_t nb_inst, ConstraintType ctype) 1907 { 1908 vector<vector<double> > res(_nb_vars); 1909 vector<double> xcurrent; 1910 int res_count=0; 1911 double xv; 1912 size_t posv; 1913 for(auto &it: *_vars) 1914 { 1915 auto v = it.second.first; 1916 if(v->_is_vector) 1917 { 1918 for (auto i=0;i<v->_dim[0];i++) 1919 { 1920 posv=i; 1921 v->get_double_val(posv, xv); 1922 xcurrent.push_back(xv); 1923 } 1924 1925 } 1926 else 1927 { 1928 posv=v->get_id_inst(nb_inst); 1929 v->get_double_val(posv, xv); 1930 // DebugOn("Name\t"<<name<<"Posv\t"<<posv<<"XV\t"<<xv<<endl); 1931 xcurrent.push_back(xv); 1932 1933 } 1934 } 1935 for(auto &it: *_vars) 1936 { 1937 auto vname=it.first; 1938 auto v=it.second.first; 1939 1940 if(v->_is_vector) 1941 { 1942 for (auto i=0;i<v->_dim[0];i++) 1943 { 1944 posv=i; 1945 auto res_nr=newton_raphson(xcurrent, vname, posv, nb_inst, ctype); 1946 1947 if(res_nr.first==true) 1948 { 1949 1950 for(auto i=0;i<xcurrent.size();i++) 1951 res[res_count].push_back(res_nr.second[i]); 1952 res_count++; 1953 } 1954 1955 } 1956 1957 } 1958 else 1959 { 1960 posv=0; 1961 1962 auto res_nr=newton_raphson(xcurrent, vname, posv, nb_inst, ctype); 1963 1964 if(res_nr.first==true) 1965 { 1966 1967 for(auto i=0;i<xcurrent.size();i++) 1968 res[res_count].push_back(res_nr.second[i]); 1969 res_count++; 1970 } 1971 } 1972 1973 } 1974 return res; 1975 } 1976 1977 /** Computes and stores the derivative of f with respect to all variables. */ compute_derivatives()1978 void compute_derivatives(){ 1979 size_t vid = 0, vjd = 0; 1980 param_* vi; 1981 param_* vj; 1982 DebugOff( "Computing derivatives for " << to_str() << endl); 1983 for (auto &vp: *_vars) { 1984 vi = vp.second.first.get(); 1985 vid = vi->get_id(); 1986 auto vi_name = vp.first; 1987 auto df = compute_derivative(*vi); 1988 // if (is_nonlinear()) { 1989 DebugOff( "First derivative with respect to " << vp.first << " = " << df->to_str() << endl); 1990 // df->print(); 1991 // } 1992 for (auto &vp2: *df->_vars) { 1993 vj = vp2.second.first.get(); 1994 vjd = vj->get_id(); 1995 auto vj_name = vp2.first; 1996 if (vi_name.compare(vj_name) <= 0) { //only store lower left part of hessian matrix since it is symmetric. 1997 auto d2f = df->compute_derivative(*vj); 1998 DebugOff( "Second derivative with respect to " << vp.first << " and " << vp2.first << " = " << d2f->to_str() << endl); 1999 // d2f->print(); 2000 } 2001 } 2002 2003 } 2004 } 2005 get_dfdx()2006 shared_ptr<map<string,shared_ptr<func>>> get_dfdx() const{ 2007 return _dfdx; 2008 }; 2009 set_first_derivative(const param_ & v,func && f)2010 void set_first_derivative(const param_& v, func&& f){ 2011 DebugOff(f.to_str()<<endl); 2012 (*_dfdx)[v._name] = make_shared<func>(move(f)); 2013 } 2014 set_second_derivative(const param_ & v1,const param_ & v2,func && f)2015 void set_second_derivative(const param_& v1, const param_& v2, func&& f){ 2016 DebugOff(f.to_str()<<endl); 2017 (*_dfdx)[v1._name]->_dfdx->insert(make_pair<>(v2._name, make_shared<func>(move(f)))); 2018 } 2019 get_range(shared_ptr<list<pair<shared_ptr<param_>,int>>> pterm)2020 pair<type,type> get_range(shared_ptr<list<pair<shared_ptr<param_>, int>>> pterm) const{ 2021 pair<type,type> res = {unit<type>().eval(),unit<type>().eval()}; 2022 auto it = pterm->begin(); 2023 while(it!=pterm->end()){ 2024 auto it2 = next(it); 2025 auto range1 = get_range(*it); 2026 if(it2!=pterm->end()){ 2027 auto range2 = get_range(*it2); 2028 auto prod = get_product_range(make_shared<pair<type,type>>(range1), make_shared<pair<type,type>>(range2)); 2029 res = *get_product_range(make_shared<pair<type,type>>(res),prod); 2030 advance(it,2); 2031 } 2032 else { 2033 res = *get_product_range(make_shared<pair<type,type>>(res),make_shared<pair<type,type>>(range1)); 2034 it++; 2035 } 2036 } 2037 return res; 2038 } 2039 get_range(const pair<shared_ptr<param_>,int> & p)2040 pair<type,type> get_range(const pair<shared_ptr<param_>, int>& p) const{ 2041 auto range = get_range(p.first); 2042 pair<type,type> res; 2043 res.first=pow(range.first,p.second); 2044 res.second=pow(range.second,p.second); 2045 if(p.second%2==0){ 2046 res.first=zero<type>().eval(); 2047 if(p.first->is_positive()|| p.first->is_negative()){ 2048 res.first=pow(range.first,p.second); 2049 } 2050 } 2051 return res; 2052 } 2053 2054 2055 template<typename T=type, 2056 typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr> get_range(shared_ptr<param_> p)2057 pair<type,type> get_range(shared_ptr<param_> p) const{ 2058 switch (p->get_intype()) { 2059 case binary_: 2060 return *static_pointer_cast<param<bool>>(p)->_range; 2061 break; 2062 case short_: 2063 return *static_pointer_cast<param<short>>(p)->_range; 2064 break; 2065 case integer_: 2066 return *static_pointer_cast<param<int>>(p)->_range; 2067 break; 2068 case float_: 2069 return *static_pointer_cast<param<float>>(p)->_range; 2070 break; 2071 case double_: 2072 return *((param<double>*)(p.get()))->_range; 2073 break; 2074 case long_: 2075 return *static_pointer_cast<param<long double>>(p)->_range; 2076 break; 2077 default: 2078 break; 2079 } 2080 return pair<type,type>(); 2081 }; 2082 2083 template<class T=type, typename enable_if<is_same<T, Cpx>::value>::type* = nullptr> get_range(shared_ptr<param_> p)2084 pair<type,type> get_range(shared_ptr<param_> p) const{ 2085 switch (p->get_intype()) { 2086 case binary_: 2087 return *static_pointer_cast<param<bool>>(p)->_range; 2088 break; 2089 case short_: 2090 return *static_pointer_cast<param<short>>(p)->_range; 2091 break; 2092 case integer_: 2093 return *static_pointer_cast<param<int>>(p)->_range; 2094 break; 2095 case float_: 2096 return *static_pointer_cast<param<float>>(p)->_range; 2097 break; 2098 case double_: 2099 return *((param<double>*)(p.get()))->_range; 2100 break; 2101 case long_: 2102 return *static_pointer_cast<param<long double>>(p)->_range; 2103 break; 2104 case complex_: 2105 return *static_pointer_cast<param<Cpx>>(p)->_range; 2106 break; 2107 default: 2108 break; 2109 } 2110 return pair<type,type>(); 2111 }; 2112 2113 get_derivative(const param_ & v)2114 func get_derivative(const param_& v) const{ 2115 func res; 2116 shared_ptr<pair<type,type>> term_range; 2117 if(!has_var(v)){ 2118 return res; 2119 } 2120 2121 auto name = v.get_name(false,false); 2122 for (auto <: *_qterms) { 2123 if (lt.second._p->first->get_name(false,false) == name) { 2124 res.set_max_dim(v); 2125 auto coef = lt.second._coef->copy(); 2126 if (coef->_is_transposed) { 2127 coef->transpose();//TODO is this needed? 2128 coef->_is_vector = false; 2129 } 2130 if (coef->is_function()) { 2131 auto f_cst = *((func<type>*)(coef.get())); 2132 auto var_range = make_shared<pair<type,type>>(get_range(lt.second._p->second)); 2133 term_range = get_product_range(f_cst._range,var_range); 2134 res.insert(lt.second._sign, f_cst, *lt.second._p->second); 2135 } 2136 else if(coef->is_param()) { 2137 auto p_cst = *((param<type>*)(coef.get())); 2138 auto var_range = make_shared<pair<type,type>>(get_range(lt.second._p->second)); 2139 term_range = get_product_range(p_cst._range,var_range); 2140 res.insert(lt.second._sign, p_cst, *lt.second._p->second); 2141 if(p_cst.is_indexed()){ 2142 res._indices = p_cst._indices; 2143 } 2144 } 2145 else if(coef->is_number()) { 2146 auto p_cst = *((constant<type>*)(coef.get())); 2147 auto var_range = make_shared<pair<type,type>>(get_range(lt.second._p->second)); 2148 term_range = get_product_range(make_shared<pair<type,type>>(p_cst.eval(),p_cst.eval()),var_range); 2149 res.insert(lt.second._sign, p_cst, *lt.second._p->second); 2150 } 2151 if (lt.second._coef->_is_vector || lt.second._coef->is_matrix()) { 2152 res._is_vector = true; 2153 } 2154 if(lt.second._sign){ 2155 res._range = get_plus_range(res._range, term_range); 2156 } 2157 else { 2158 res._range = get_minus_range(res._range, term_range); 2159 } 2160 } 2161 if (lt.second._p->second->get_name(false,false) == name) { 2162 res.set_max_dim(v); 2163 auto coef = lt.second._coef->copy(); 2164 if (coef->_is_transposed) { 2165 coef->transpose();//TODO is this needed? 2166 } 2167 if (coef->is_function()) { 2168 auto f_cst = *((func<type>*)(coef.get())); 2169 auto var_range = make_shared<pair<type,type>>(get_range(lt.second._p->second)); 2170 term_range = get_product_range(f_cst._range,var_range); 2171 res.insert(lt.second._sign, f_cst, *lt.second._p->first); 2172 } 2173 else if(coef->is_param()) { 2174 auto p_cst = *((param<type>*)(coef.get())); 2175 auto var_range = make_shared<pair<type,type>>(get_range(lt.second._p->second)); 2176 term_range = get_product_range(p_cst._range,var_range); 2177 res.insert(lt.second._sign, p_cst, *lt.second._p->first); 2178 if(p_cst.is_indexed()){ 2179 res._indices = p_cst._indices; 2180 } 2181 } 2182 else if(coef->is_number()) { 2183 auto p_cst = *((constant<type>*)(coef.get())); 2184 auto var_range = make_shared<pair<type,type>>(get_range(lt.second._p->second)); 2185 term_range = get_product_range(make_shared<pair<type,type>>(p_cst.eval(),p_cst.eval()),var_range); 2186 res.insert(lt.second._sign, p_cst, *lt.second._p->first); 2187 } 2188 if (lt.second._coef->_is_vector || lt.second._coef->is_matrix()) { 2189 res._is_vector = true; 2190 } 2191 if(lt.second._sign){ 2192 res._range = get_plus_range(res._range, term_range); 2193 } 2194 else { 2195 res._range = get_minus_range(res._range, term_range); 2196 } 2197 } 2198 } 2199 int expo = 0; 2200 for (auto <: *_pterms) { 2201 auto coef = lt.second._coef->copy(); 2202 if (coef->_is_transposed) { 2203 coef->transpose(); 2204 coef->_is_vector = false; 2205 } 2206 bool has_v = false; 2207 shared_ptr<list<pair<shared_ptr<param_>, int>>> newl = make_shared<list<pair<shared_ptr<param_>,int>>>(); 2208 auto it = lt.second._l->begin(); 2209 while(it!=lt.second._l->end()){ 2210 auto vv = it->first; 2211 if (vv->get_name(false,false) == name) { 2212 has_v = true; 2213 expo = it->second; 2214 if(expo!=1){ 2215 newl->push_back(make_pair<>(vv, expo-1)); 2216 res.set_max_dim(*vv); 2217 } 2218 } 2219 else { 2220 newl->push_back(*it); 2221 } 2222 it++; 2223 } 2224 if(has_v){//TODO fix range propagation for square terms 2225 res.set_max_dim(v); 2226 auto pterm_range = get_range(newl); 2227 pterm_range.first *= expo; 2228 pterm_range.second *= expo; 2229 if (coef->is_function()) { 2230 auto f_cst = *static_pointer_cast<func<type>>(lt.second._coef); 2231 if(newl->size()==1 && newl->front().second==2){ 2232 res.insert(lt.second._sign, expo*f_cst, *newl->front().first,*newl->front().first); 2233 } 2234 else if(newl->size()==2 && newl->front().second==1 && newl->back().second==1){ 2235 res.insert(lt.second._sign, expo*f_cst, *newl->front().first,*newl->back().first); 2236 } 2237 else{ 2238 res.insert(lt.second._sign, expo*f_cst, *newl); 2239 } 2240 pterm_range = *get_product_range(make_shared<pair<type,type>>(pterm_range), f_cst._range); 2241 } 2242 else if(coef->is_param()) { 2243 auto p_cst = *static_pointer_cast<param<type>>(lt.second._coef); 2244 if(newl->size()==1 && newl->front().second==2){ 2245 res.insert(lt.second._sign, expo*p_cst, *newl->front().first,*newl->front().first); 2246 } 2247 else if(newl->size()==2 && newl->front().second==1 && newl->back().second==1){ 2248 res.insert(lt.second._sign, expo*p_cst, *newl->front().first,*newl->back().first); 2249 } 2250 else{ 2251 res.insert(lt.second._sign, expo*p_cst, *newl); 2252 } 2253 pterm_range = *get_product_range(make_shared<pair<type,type>>(pterm_range), p_cst._range); 2254 } 2255 else if(coef->is_number()) { 2256 auto p_cst = *static_pointer_cast<constant<type>>(lt.second._coef); 2257 if(newl->size()==1 && newl->front().second==2){ 2258 res.insert(lt.second._sign, expo*p_cst, *newl->front().first,*newl->front().first); 2259 } 2260 else if(newl->size()==2 && newl->front().second==1 && newl->back().second==1){ 2261 res.insert(lt.second._sign, expo*p_cst, *newl->front().first,*newl->back().first); 2262 } 2263 else{ 2264 res.insert(lt.second._sign, expo*p_cst, *newl); 2265 } 2266 pterm_range.first = gravity::min(pterm_range.first*p_cst.eval(), pterm_range.second*p_cst.eval()); 2267 pterm_range.second = gravity::max(pterm_range.first*p_cst.eval(), pterm_range.second*p_cst.eval()); 2268 } 2269 if(lt.second._sign){ 2270 res._range = get_plus_range(res._range, make_shared<pair<type,type>>(pterm_range)); 2271 } 2272 else { 2273 res._range = get_minus_range(res._range, make_shared<pair<type,type>>(pterm_range)); 2274 } 2275 } 2276 } 2277 if (_expr) { 2278 res += get_derivative(_expr,v); 2279 } 2280 for (auto <: *_lterms) { 2281 if (lt.second._p->get_name(false,false) == name) { 2282 auto coef = lt.second._coef->copy(); 2283 if (coef->_is_transposed) { 2284 coef->transpose(); 2285 } 2286 if (coef->is_function()) { 2287 if(lt.second._sign){ 2288 res += *((func<type>*)(coef.get())); 2289 } 2290 else { 2291 res -= *((func<type>*)(coef.get())); 2292 } 2293 2294 } 2295 else if(coef->is_param()) { 2296 auto p_cst = *((param<type>*)(coef.get())); 2297 if(lt.second._sign){ 2298 res += p_cst; 2299 } 2300 else { 2301 res -= p_cst; 2302 } 2303 if(p_cst.is_indexed()){ 2304 res._indices = p_cst._indices; 2305 } 2306 } 2307 else if(coef->is_number()) { 2308 if(lt.second._sign){ 2309 res += *((constant<type>*)(coef.get())); 2310 } 2311 else { 2312 res -= *((constant<type>*)(coef.get())); 2313 } 2314 } 2315 break; 2316 } 2317 } 2318 if(v._is_vector){ 2319 res._is_vector=true; 2320 if(res.func_is_number()){ 2321 res._dim[0] = v._dim[0]; 2322 res._dim[1] = v._dim[1]; 2323 } 2324 } 2325 if(v.is_matrix_indexed()){ 2326 res._indices = v._indices; 2327 } 2328 res.update_double_index(); 2329 return res; 2330 } 2331 2332 insert(bool sign,const constant_ & coef,const param_ & p)2333 bool insert(bool sign, const constant_& coef, const param_& p){/**< Adds coef*p to the linear function. Returns true if added new term, false if only updated coef of p */ 2334 shared_ptr<param_> p_new = p.pcopy(); 2335 bool transpose = false; 2336 _evaluated=false; 2337 string pname; 2338 if(coef._is_transposed && !p_new->_is_vector){ 2339 p_new->_is_vector=true; 2340 p_new->_name = "["+p_new->_name+"]"; 2341 } 2342 if(coef.get_dim()>1 && p._is_transposed){// Situation where p^T*coef is transformed into coef^T*p 2343 if(coef._is_transposed){ 2344 throw invalid_argument("In bool insert(bool sign, const constant_& coef, const param_& p), both coef and p are transposed."); 2345 } 2346 p_new->transpose(); 2347 transpose = true; 2348 } 2349 pname = p_new->get_name(false,false); 2350 auto pair_it = _lterms->find(pname); 2351 if (pair_it != _lterms->end() && pair_it->second._p->get_type() != p.get_type()) { 2352 throw invalid_argument("param and var with same name: " + pname); 2353 } 2354 _evaluated = false; 2355 if (_ftype == const_ && p.is_var()) { 2356 _ftype = lin_; 2357 } 2358 2359 if (pair_it == _lterms->end()) { 2360 auto c_new = coef.copy(); 2361 if(transpose){ 2362 c_new->transpose(); 2363 } 2364 if (c_new->is_function()) { 2365 embed(*static_pointer_cast<func>(c_new)); 2366 } 2367 else if(c_new->is_param()) { 2368 auto cp = static_pointer_cast<param_>(c_new); 2369 auto pname = cp->get_name(false,false); 2370 auto p_exist = get_param(pname); 2371 if (!p_exist) { 2372 add_param(cp); 2373 } 2374 else { 2375 incr_occ_param(pname); 2376 } 2377 } 2378 if (p.is_var()) { 2379 auto p_exist = get_var(pname); 2380 if (!p_exist) { 2381 add_var(p_new); 2382 } 2383 else { 2384 incr_occ_var(pname); 2385 } 2386 } 2387 else { 2388 auto p_exist = get_param(pname); 2389 if (!p_exist) { 2390 add_param(p_new); 2391 } 2392 else { 2393 incr_occ_param(pname); 2394 } 2395 } 2396 _lterms->insert(make_pair<>(pname, lterm(sign, c_new, p_new))); 2397 return true; 2398 } 2399 else { 2400 if (pair_it->second._sign == sign) { 2401 if (coef.is_function()) { 2402 auto coef2 = *(func<type>*)(&coef); 2403 pair_it->second._coef = add(pair_it->second._coef,coef2); 2404 } 2405 else if(coef.is_param()) { 2406 auto coef2 = *(param<type>*)(&coef); 2407 pair_it->second._coef = add(pair_it->second._coef,coef2); 2408 } 2409 else if(coef.is_number()) { 2410 auto coef2 = *(constant<type>*)(&coef); 2411 pair_it->second._coef = add(pair_it->second._coef,coef2); 2412 } 2413 } 2414 else{ 2415 if (coef.is_function()) { 2416 auto coef2 = *(func<type>*)(&coef); 2417 pair_it->second._coef = subtract(pair_it->second._coef,coef2); 2418 } 2419 else if(coef.is_param()) { 2420 auto coef2 = *(param<type>*)(&coef); 2421 pair_it->second._coef = subtract(pair_it->second._coef,coef2); 2422 } 2423 else if(coef.is_number()) { 2424 auto coef2 = *(constant<type>*)(&coef); 2425 pair_it->second._coef = subtract(pair_it->second._coef,coef2); 2426 } 2427 } 2428 if (pair_it->second._coef->is_function()) { 2429 embed(*static_pointer_cast<func>(pair_it->second._coef)); 2430 } 2431 if (pair_it->second._coef->is_zero()) { 2432 if (p.is_var()) { 2433 decr_occ_var(pname); 2434 } 2435 else{ 2436 decr_occ_param(pname); 2437 } 2438 _lterms->erase(pair_it); 2439 if(is_constant()){ 2440 _ftype = const_; 2441 _val->resize(1); 2442 } 2443 } 2444 return false; 2445 } 2446 }; 2447 is_rotated_soc()2448 bool is_rotated_soc(){ 2449 if (!_lterms->empty() || _qterms->empty() || !_pterms->empty() || _expr) { 2450 return false; 2451 } 2452 unsigned nb_bilinear = 0, nb_quad = 0; 2453 Sign bilinear_sign = unknown_, quadratic_sign = unknown_, var1_sign = unknown_, var2_sign = unknown_; 2454 for (auto &qt_pair: *_qterms) { 2455 if (qt_pair.second._p->first!=qt_pair.second._p->second) { 2456 bilinear_sign = qt_pair.second.get_all_sign(); 2457 var1_sign = qt_pair.second._p->first->get_all_sign(); 2458 var2_sign = qt_pair.second._p->second->get_all_sign(); 2459 if (bilinear_sign==unknown_ || var1_sign==neg_ || var2_sign==neg_) { 2460 return false; 2461 } 2462 nb_bilinear++; 2463 if (nb_bilinear > 1) { 2464 return false; 2465 } 2466 } 2467 else{ 2468 nb_quad++; 2469 auto sign = qt_pair.second.get_all_sign(); 2470 if (quadratic_sign!=unknown_ && quadratic_sign!=sign) { 2471 return false; 2472 } 2473 if (quadratic_sign!=unknown_ && quadratic_sign==bilinear_sign) { 2474 return false; 2475 } 2476 else { 2477 quadratic_sign = sign; 2478 } 2479 } 2480 } 2481 if(nb_quad==0){ 2482 return false; 2483 } 2484 if (bilinear_sign==pos_) { 2485 _all_convexity = concave_; 2486 return true; 2487 } 2488 else if(bilinear_sign==neg_) { 2489 _all_convexity = convex_; 2490 return true; 2491 } 2492 return false; 2493 }; 2494 insert(const constant_ & coef,const param_ & p)2495 bool insert(const constant_& coef, const param_& p){ 2496 return insert(true, coef, p); 2497 } 2498 insert(const param_ & p)2499 bool insert(const param_& p){ 2500 return insert(true, unit<type>(), p); 2501 } 2502 insert(const lterm & term)2503 bool insert(const lterm& term){return insert(term._sign, *term._coef, *term._p);}; 2504 2505 bool insert(const param_& p1, const param_& p2, bool coef_p1_tr=false){ 2506 return insert(true, unit<type>(), p1, p2, coef_p1_tr); 2507 }; 2508 insert(const param_ & p2,int exp)2509 bool insert(const param_& p2, int exp){ 2510 return insert(true, unit<type>(), p2, exp); 2511 }; 2512 2513 /** 2514 Reverse the sign of all terms in the function, also reverses convexity. 2515 */ reverse_sign()2516 void reverse_sign(){ 2517 _cst->reverse_sign(); 2518 for (auto &pair: *_lterms) { 2519 pair.second.reverse_sign(); 2520 } 2521 for (auto &pair: *_qterms) { 2522 pair.second.reverse_sign(); 2523 } 2524 for (auto &pair: *_pterms) { 2525 pair.second.reverse_sign(); 2526 } 2527 if(_expr){ 2528 _expr->reverse_sign(); 2529 } 2530 if(_evaluated){ 2531 for (auto i = 0; i<_val->size(); i++) { 2532 _val->at(i) = -1.*eval(i); 2533 } 2534 } 2535 reverse_convexity(); 2536 reverse_all_sign(); 2537 reverse_range(); 2538 } 2539 reverse_range()2540 void reverse_range(){ 2541 auto temp = _range->first; 2542 _range->first = -1.*_range->second; 2543 _range->second = -1.*temp; 2544 } 2545 2546 update_all_sign()2547 template<class T=type, typename enable_if<is_same<T, Cpx>::value>::type* = nullptr> void update_all_sign(){ 2548 if (_range->first == Cpx(0,0) && _range->second == Cpx(0,0)) { 2549 _all_sign = zero_; 2550 } 2551 else if ((_range->second.real() < 0 && _range->second.imag() < 0)) { 2552 _all_sign = neg_; 2553 } 2554 else if ((_range->second.real() > 0 && _range->second.imag() > 0)) { 2555 _all_sign = pos_; 2556 } 2557 else if (_range->second == Cpx(0,0)) { 2558 _all_sign = non_pos_; 2559 } 2560 else if (_range->first == Cpx(0,0)) { 2561 _all_sign = non_neg_; 2562 } 2563 else { 2564 _all_sign = unknown_; 2565 } 2566 } 2567 2568 template<typename T=type, update_all_sign()2569 typename std::enable_if<is_arithmetic<T>::value>::type* = nullptr> void update_all_sign() { 2570 if (_range->first == 0 && _range->second == 0) { 2571 _all_sign = zero_; 2572 } 2573 else if (_range->second < 0 && _range->first < 0) { 2574 _all_sign = neg_; 2575 } 2576 else if (_range->first > 0 && _range->second > 0) { 2577 _all_sign = pos_; 2578 } 2579 else if (_range->second == 0 && _range->first < 0) { 2580 _all_sign = non_pos_; 2581 } 2582 else if (_range->first == 0 && _range->second > 0) { 2583 _all_sign = non_neg_; 2584 } 2585 else { 2586 _all_sign = unknown_; 2587 } 2588 } 2589 reverse_all_sign()2590 void reverse_all_sign(){ 2591 if(_all_sign==neg_){ 2592 _all_sign=pos_; 2593 } 2594 else if(_all_sign==pos_){ 2595 _all_sign=neg_; 2596 } 2597 else if(_all_sign==non_pos_){ 2598 _all_sign=non_neg_; 2599 } 2600 else if(_all_sign==non_neg_){ 2601 _all_sign=non_pos_; 2602 } 2603 } 2604 // shared_ptr<constant_> multiply(const func_& f){ 2605 // 2606 // // switch (f.get_return_type()) { 2607 // // case binary_: { 2608 // // auto newf = static_cast<func<bool>>(f); 2609 // // newf *= *this; 2610 // // return make_shared<func<bool>>(vv*c2); 2611 // // break; 2612 // // } 2613 // // case short_: { 2614 // // auto vv = static_pointer_cast<var<short>>(c1); 2615 // // return make_shared<func<short>>(vv*c2); 2616 // // break; 2617 // // } 2618 // // case integer_: { 2619 // // auto vv = static_pointer_cast<var<int>>(c1); 2620 // // return make_shared<func<int>>(vv*c2); 2621 // // break; 2622 // // } 2623 // // case float_: { 2624 // // auto vv = static_pointer_cast<var<float>>(c1); 2625 // // return make_shared<func<float>>(vv*c2); 2626 // // break; 2627 // // } 2628 // // case double_: { 2629 // // auto vv = static_pointer_cast<var<double>>(c1); 2630 // // return make_shared<func<double>>(vv*c2); 2631 // // break; 2632 // // } 2633 // // case long_: { 2634 // // auto vv = static_pointer_cast<var<long double>>(c1); 2635 // // return make_shared<func<long double>>(vv*c2); 2636 // // break; 2637 // // } 2638 // // case complex_: { 2639 // // auto vv = static_pointer_cast<var<Cpx>>(c1); 2640 // // return make_shared<func<Cpx>>(vv*c2); 2641 // // break; 2642 // // } 2643 // // default: 2644 // // break; 2645 // } 2646 2647 // Sign get_all_sign() const{ /**< If all instances of the current parameter/variable have the same sign, it returns it, otherwise, it returns unknown. **/ 2648 // return get_all_sign(); 2649 // }; 2650 // Sign get_sign(size_t idx = 0) const{ /**< returns the sign of one instance of the current parameter/variable. **/ 2651 // return get_sign(idx); 2652 // } 2653 2654 // template<typename... Args> 2655 // func in(const indices& vec1, Args&&... args) { 2656 // func<type> res(*this); 2657 //// res.operator=(in(vec1, forward<Args>(args)...)); 2658 // return res; 2659 // } 2660 2661 2662 2663 2664 template<typename... Args> index_in(const indices & ids)2665 void index_in(const indices& ids) { 2666 this->in(ids); 2667 } 2668 print()2669 void print() { 2670 allocate_mem(); 2671 print(10); 2672 } 2673 2674 void print(size_t index, int prec = 10) { 2675 cout << to_str(index,prec); 2676 } 2677 2678 void print(size_t i, size_t j, int prec = 10) { 2679 cout << to_str(i,j,prec); 2680 } 2681 to_str()2682 string to_str() { 2683 string str; 2684 for (auto &pair:*_pterms) { 2685 str += pair.second.to_str(); 2686 } 2687 for (auto &pair:*_qterms) { 2688 str += pair.second.to_str(); 2689 } 2690 for (auto &pair:*_lterms) { 2691 str += pair.second.to_str(); 2692 } 2693 if(!_cst->is_zero()){ 2694 auto cst_str = _cst->to_str(); 2695 if (cst_str.front()=='-'){ 2696 str += " - " + cst_str.substr(1); 2697 } 2698 else { 2699 str += " + " + cst_str; 2700 } 2701 } 2702 if (_expr) { 2703 str += " + "; 2704 str += _expr->to_str(); 2705 } 2706 if (str.size() > 2 && str.at(1)=='+') { 2707 str = str.substr(3); 2708 } 2709 if (_is_vector) { 2710 str = "[" + str +"]"; 2711 } 2712 if (_is_transposed) { 2713 str += "\u1D40"; 2714 } 2715 if(str.size()==0){ 2716 str = "0"; 2717 } 2718 _to_str = str; 2719 return str; 2720 } 2721 update_str()2722 void update_str(){ 2723 _to_str = to_str(); 2724 } 2725 update_double_index()2726 void update_double_index() { 2727 if(_expr){ 2728 _expr->update_double_index(); 2729 } 2730 for (auto &v_p:*_vars) { 2731 auto v = v_p.second.first; 2732 if(v->is_indexed() && !v->_is_transposed){ 2733 _indices = v->_indices; 2734 _dim[0] = v->_dim[0]; 2735 } 2736 if(v->is_matrix_indexed()){ 2737 _indices = v->_indices; 2738 return; 2739 } 2740 } 2741 for (auto &p_p:*_params) { 2742 auto p = p_p.second.first; 2743 if(p->is_indexed()&& !p->_is_transposed){ 2744 _indices = p->_indices; 2745 _dim[0] = p->_dim[0]; 2746 } 2747 if(p->is_matrix_indexed()){ 2748 _indices = p->_indices; 2749 return; 2750 } 2751 } 2752 } 2753 is_matrix_indexed()2754 bool is_matrix_indexed() const{ 2755 if(_indices && ((_indices->_ids && _indices->_ids->size()>1) && _indices->_type!=matrix_)){ 2756 throw invalid_argument("matrix issue"); 2757 } 2758 return (_indices && ((_indices->_ids && _indices->_ids->size()>1) || _indices->_type==matrix_)); 2759 } 2760 to_str(size_t index,int prec)2761 string to_str(size_t index, int prec) { 2762 if (is_constant() && !this->is_matrix_indexed()) { 2763 return to_string_with_precision(eval(index),prec); 2764 } 2765 string str; 2766 for (auto &pair:*_pterms) { 2767 str += pair.second.to_str(index, prec); 2768 } 2769 for (auto &pair:*_qterms) { 2770 str += pair.second.to_str(index, prec); 2771 } 2772 for (auto &pair:*_lterms) { 2773 str += pair.second.to_str(index, prec); 2774 } 2775 if (_expr) { 2776 str += _expr->to_str(index, prec); 2777 } 2778 if(!_cst->is_zero()){ 2779 auto val = _cst->to_str(index, prec); 2780 if (val.front()=='-') { 2781 str += " - " + val.substr(1); 2782 } 2783 else { 2784 str += " + "; 2785 str += val; 2786 } 2787 } 2788 if (str.size() > 2 && str.at(1)=='+') { 2789 str = str.substr(3); 2790 } 2791 // if (_is_vector && (is_number() || _vars->size()>1 || _params->size()>1)) { 2792 // str = "[" + str +"]"; 2793 // } 2794 // if (_is_transposed && (is_number() || _vars->size()>1 || _params->size()>1)) { 2795 // str += "\u1D40"; 2796 // } 2797 return str; 2798 } 2799 get_max_cell_size()2800 size_t get_max_cell_size(){ 2801 auto max_size = 0; 2802 for (size_t i = 0; i<_dim[0]; i++) { 2803 for (size_t j = 0; j<_dim[1]; j++) { 2804 eval(i,j); 2805 auto cell = to_str(i,j,5); 2806 if(max_size < cell.size()){ 2807 max_size = cell.size(); 2808 } 2809 } 2810 } 2811 return max_size; 2812 } 2813 get_dim()2814 size_t get_dim() const{ 2815 return constant_::get_dim(); 2816 } 2817 get_dim(size_t i)2818 size_t get_dim(size_t i) const{ 2819 if(is_matrix_indexed()) 2820 return _indices->_ids->at(i).size(); 2821 if (is_indexed() && !_is_transposed) { 2822 return _indices->_ids->at(0).size(); 2823 } 2824 return constant_::get_dim(i); 2825 } 2826 get_nb_inst()2827 size_t get_nb_inst() const{ 2828 if(is_matrix_indexed()) 2829 return _indices->_ids->size(); 2830 if(_indices && !_is_transposed){ 2831 return _indices->size(); 2832 } 2833 return this->_dim[0]; 2834 } 2835 print(int prec)2836 void print(int prec){ 2837 string str; 2838 if (is_constant()) { 2839 str += " (Constant"; 2840 } 2841 else if (is_linear()) { 2842 str += " (Linear"; 2843 } 2844 else if (is_convex()) { 2845 str += " (Convex"; 2846 } 2847 else if (is_concave()){ 2848 str += " (Concave"; 2849 } 2850 else { 2851 str += " (Unknown"; 2852 } 2853 if (is_complex()) { 2854 str += " Complex) : "; 2855 } 2856 else { 2857 str += ") : "; 2858 } 2859 if (false && !_embedded && !is_constant()) { 2860 str += "f("; 2861 for (auto pair_it = _vars->begin(); pair_it != _vars->end();) { 2862 str += pair_it->second.first->get_name(false,false); 2863 if (next(pair_it) != _vars->end()) { 2864 str += ","; 2865 } 2866 pair_it++; 2867 } 2868 str += ") = "; 2869 } 2870 auto space_size = str.size(); 2871 auto nb_inst = this->get_nb_inst(); 2872 allocate_mem(); 2873 if (is_matrix()) { 2874 auto max_cell_size = get_max_cell_size(); 2875 for (size_t i = 0; i<_dim[0]; i++) { 2876 if (i>0) { 2877 str.insert(str.end(), space_size, ' '); 2878 } 2879 str += "|"; 2880 for (size_t j = 0; j<_dim[1]; j++) { 2881 auto cell = to_str(i,j,prec); 2882 auto cell_size = cell.size(); 2883 cell.insert(0, floor((max_cell_size - cell_size)/2.), ' '); 2884 cell.append(ceil((max_cell_size - cell_size)/2.), ' '); 2885 str += cell; 2886 if(j!=_dim[1]-1){ 2887 str += " "; 2888 } 2889 } 2890 str += "|\n"; 2891 } 2892 } 2893 else { 2894 for (size_t inst = 0; inst<nb_inst; inst++) { 2895 eval(inst); 2896 if (inst>0) { 2897 str.insert(str.end(), space_size, ' '); 2898 } 2899 str += to_str(inst,prec); 2900 str += "\n"; 2901 } 2902 } 2903 cout << str; 2904 } 2905 2906 to_str(size_t index1,size_t index2,int prec)2907 string to_str(size_t index1, size_t index2, int prec) { 2908 if(!_indices || _indices->_type != matrix_){ 2909 return to_str(index1,prec); 2910 } 2911 if (is_constant()) { 2912 return to_string_with_precision(eval(index1,index2),prec); 2913 } 2914 string str; 2915 for (auto &pair:*_pterms) { 2916 str += pair.second.to_str(index1,index2, prec); 2917 } 2918 for (auto &pair:*_qterms) { 2919 str += pair.second.to_str(index1,index2, prec); 2920 } 2921 for (auto &pair:*_lterms) { 2922 str += pair.second.to_str(index1,index2, prec); 2923 } 2924 if(!_cst->is_zero()){ 2925 auto val = _cst->to_str(index1,index2, prec); 2926 if (val.front()=='-') { 2927 str += " - " + val.substr(1); 2928 } 2929 else { 2930 str += " + "; 2931 str += val; 2932 } 2933 } 2934 if (_expr) { 2935 str += " + "; 2936 str += _expr->to_str(index1,index2, prec); 2937 } 2938 if (str.size() > 2 && str.at(1)=='+') { 2939 str = str.substr(3); 2940 } 2941 // if (_is_vector && (func_is_number() || _vars->size()>1 || _params->size()>1)) { 2942 // str = "[" + str +"]"; 2943 // } 2944 // if (_is_transposed && (func_is_number() || _vars->size()>1 || _params->size()>1)) { 2945 // str += "\u1D40"; 2946 // } 2947 return str; 2948 } 2949 2950 propagate_dim(size_t d)2951 void propagate_dim(size_t d){ 2952 if (is_matrix()) { 2953 return; 2954 } 2955 if(_is_transposed){ 2956 _dim[1] = d; 2957 } 2958 else { 2959 _dim[0] = d; 2960 } 2961 for (auto &pair:*_lterms) { 2962 auto coef = pair.second._coef; 2963 coef->propagate_dim(d); 2964 } 2965 for (auto &pair:*_qterms) { 2966 auto coef = pair.second._coef; 2967 coef->propagate_dim(d); 2968 } 2969 for (auto &pair:*_pterms) { 2970 auto coef = pair.second._coef; 2971 coef->propagate_dim(d); 2972 } 2973 _cst->propagate_dim(d); 2974 if (_expr) { 2975 _expr->propagate_dim(d); 2976 } 2977 } 2978 allocate_mem()2979 void allocate_mem(){ 2980 // _evaluated = false; 2981 if(is_matrix_indexed()){ 2982 for(auto i = 0; i<_indices->_ids->size();i++){ 2983 for(auto j = 0; j<_indices->_ids->at(i).size();j++){ 2984 _dim[0] = max(_dim[0],_indices->_ids->at(i).at(j)+1); 2985 } 2986 } 2987 } 2988 _val->resize(get_dim()); 2989 for (auto &pair:*_lterms) { 2990 auto coef = pair.second._coef; 2991 coef->allocate_mem(); 2992 } 2993 for (auto &pair:*_qterms) { 2994 auto coef = pair.second._coef; 2995 coef->allocate_mem(); 2996 } 2997 for (auto &pair:*_pterms) { 2998 auto coef = pair.second._coef; 2999 coef->allocate_mem(); 3000 } 3001 _cst->allocate_mem(); 3002 if (_expr) { 3003 _expr->allocate_mem(); 3004 } 3005 } 3006 3007 /** Returns true if the current function is just a wrapper to a parameter */ func_is_param()3008 bool func_is_param() const{ 3009 if(_vars->size()==1 && _params->size()==0 && _vars->begin()->second.first->get_intype()==_return_type) 3010 return true; 3011 if(_vars->size()==0 && _params->size()==1 && _params->begin()->second.first->get_intype()==_return_type) 3012 return true; 3013 return false; 3014 } 3015 func_get_param()3016 param<type> func_get_param() const{ 3017 auto p = *(param<type>*)(_lterms->begin()->second._p.get()); 3018 if(_is_transposed && ! p._is_transposed){ 3019 p.transpose(); 3020 } 3021 return p; 3022 } 3023 3024 3025 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> multiply(shared_ptr<constant_> coef,const constant<T2> & c)3026 shared_ptr<constant_> multiply(shared_ptr<constant_> coef, const constant<T2>& c){ 3027 if (coef->is_function()) { 3028 auto f_cst = *((func<type>*)(coef.get())); 3029 // if(c.is_unit() && f_cst.func_is_param()){ 3030 // return f_cst.func_get_param().copy(); 3031 // } 3032 f_cst *= func<type>(c); 3033 return f_cst.copy(); 3034 } 3035 else if(coef->is_param()) { 3036 auto p_cst = *((param<type>*)(coef.get())); 3037 if(c.is_unit()){ 3038 return p_cst.pcopy(); 3039 } 3040 auto new_cst = p_cst * c; 3041 return new_cst.copy(); 3042 } 3043 else if(coef->is_number()) { 3044 auto p_cst = *((constant<type>*)(coef.get())); 3045 auto new_cst = p_cst * c; 3046 return new_cst.copy(); 3047 } 3048 return nullptr; 3049 } 3050 3051 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> multiply(shared_ptr<constant_> coef,const param<T2> & p)3052 shared_ptr<constant_> multiply(shared_ptr<constant_> coef, const param<T2>& p){ 3053 if (coef->is_function()) { 3054 auto f_cst = *((func<type>*)(coef.get())); 3055 f_cst *= func<type>(p); 3056 return f_cst.copy(); 3057 } 3058 else if(coef->is_param()) { 3059 auto p_cst = *((param<type>*)(coef.get())); 3060 auto new_cst = p_cst * p; 3061 return new_cst.copy(); 3062 } 3063 else if(coef->is_number()) { 3064 auto p_cst = *((constant<type>*)(coef.get())); 3065 auto new_cst = p_cst * p; 3066 return new_cst.copy(); 3067 } 3068 return nullptr; 3069 } 3070 3071 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> multiply(shared_ptr<constant_> coef,const func<T2> & f)3072 shared_ptr<constant_> multiply(shared_ptr<constant_> coef, const func<T2>& f){ 3073 if (coef->is_function()) { 3074 auto f_cst = *((func<type>*)(coef.get())); 3075 f_cst *= func<type>(f); 3076 embed(f_cst); 3077 return f_cst.copy(); 3078 } 3079 else if(coef->is_param()) { 3080 auto p_cst = *((param<type>*)(coef.get())); 3081 auto new_cst = p_cst * f; 3082 return new_cst.copy(); 3083 } 3084 else if(coef->is_number()) { 3085 auto p_cst = *((constant<type>*)(coef.get())); 3086 if(f.func_is_number()){ 3087 return make_shared<constant<type>>(p_cst *= eval(f.copy(),0)); 3088 } 3089 return make_shared<func<type>>(func(p_cst) *= f); 3090 } 3091 return nullptr; 3092 } 3093 3094 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> add(shared_ptr<constant_> coef,const constant<T2> & c)3095 shared_ptr<constant_> add(shared_ptr<constant_> coef, const constant<T2>& c){ 3096 if (coef->is_function()) { 3097 auto f_cst = *((func<type>*)(coef.get())); 3098 f_cst += func<type>(c); 3099 return f_cst.copy(); 3100 } 3101 else if(coef->is_param()) { 3102 auto p_cst = *((param<type>*)(coef.get())); 3103 auto new_cst = p_cst + c; 3104 return new_cst.copy(); 3105 } 3106 else if(coef->is_number()) { 3107 auto p_cst = *((constant<type>*)(coef.get())); 3108 auto new_cst = p_cst + c; 3109 return new_cst.copy(); 3110 } 3111 return nullptr; 3112 } 3113 3114 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> add(shared_ptr<constant_> coef,const param<T2> & p)3115 shared_ptr<constant_> add(shared_ptr<constant_> coef, const param<T2>& p){ 3116 if (coef->is_function()) { 3117 auto f_cst = *((func<type>*)(coef.get())); 3118 f_cst += func<type>(p); 3119 return f_cst.copy(); 3120 } 3121 else if(coef->is_param()) { 3122 auto p_cst = *((param<type>*)(coef.get())); 3123 auto new_cst = p_cst + p; 3124 return new_cst.copy(); 3125 } 3126 else if(coef->is_number()) { 3127 auto p_cst = *((constant<type>*)(coef.get())); 3128 auto new_cst = p_cst + p; 3129 return new_cst.copy(); 3130 } 3131 return nullptr; 3132 } 3133 3134 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> add(shared_ptr<constant_> coef,const func<T2> & f)3135 shared_ptr<constant_> add(shared_ptr<constant_> coef, const func<T2>& f){ 3136 if (coef->is_function()) { 3137 auto f_cst = *((func<type>*)(coef.get())); 3138 f_cst += func<type>(f); 3139 embed(f_cst); 3140 return f_cst.copy(); 3141 } 3142 else if(coef->is_param()) { 3143 auto p_cst = *((param<type>*)(coef.get())); 3144 auto new_cst = p_cst + f; 3145 return new_cst.copy(); 3146 } 3147 else if(coef->is_number()) { 3148 auto p_cst = (*((constant<type>*)(coef.get()))); 3149 if(f.func_is_number()){ 3150 return make_shared<constant<type>>(p_cst += eval(f.copy(),0)); 3151 } 3152 return make_shared<func<type>>(func(p_cst) += f); 3153 } 3154 return nullptr; 3155 } 3156 3157 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> subtract(shared_ptr<constant_> coef,const constant<T2> & c)3158 shared_ptr<constant_> subtract(shared_ptr<constant_> coef, const constant<T2>& c){ 3159 if (coef->is_function()) { 3160 auto f_cst = *((func<type>*)(coef.get())); 3161 f_cst -= func<type>(c); 3162 return f_cst.copy(); 3163 } 3164 else if(coef->is_param()) { 3165 auto p_cst = *((param<type>*)(coef.get())); 3166 if(c.is_unit()){ 3167 return p_cst.pcopy(); 3168 } 3169 auto new_cst = p_cst - c; 3170 return new_cst.copy(); 3171 } 3172 else if(coef->is_number()) { 3173 auto p_cst = *((constant<type>*)(coef.get())); 3174 auto new_cst = p_cst - c; 3175 return new_cst.copy(); 3176 } 3177 return nullptr; 3178 } 3179 3180 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> subtract(shared_ptr<constant_> coef,const param<T2> & p)3181 shared_ptr<constant_> subtract(shared_ptr<constant_> coef, const param<T2>& p){ 3182 if (coef->is_function()) { 3183 auto f_cst = *((func<type>*)(coef.get())); 3184 f_cst -= func<type>(p); 3185 return f_cst.copy(); 3186 } 3187 else if(coef->is_param()) { 3188 auto p_cst = *((param<type>*)(coef.get())); 3189 if(p_cst==p){ 3190 return constant<type>(0).copy(); 3191 } 3192 auto new_cst = p_cst - p; 3193 return new_cst.copy(); 3194 } 3195 else if(coef->is_number()) { 3196 auto p_cst = *((constant<type>*)(coef.get())); 3197 auto new_cst = p_cst - p; 3198 return new_cst.copy(); 3199 } 3200 return nullptr; 3201 } 3202 3203 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> subtract(shared_ptr<constant_> coef,func<T2> & f)3204 shared_ptr<constant_> subtract(shared_ptr<constant_> coef, func<T2>& f){ 3205 if (coef->is_function()) { 3206 auto f_cst = *((func<type>*)(coef.get())); 3207 f_cst.update_str(); 3208 f.update_str(); 3209 if(f_cst==f){ 3210 return constant<type>(0).copy(); 3211 } 3212 f_cst -= func<type>(f); 3213 embed(f_cst); 3214 return f_cst.copy(); 3215 } 3216 else if(coef->is_param()) { 3217 auto p_cst = *((param<type>*)(coef.get())); 3218 auto new_cst = p_cst - f; 3219 return new_cst.copy(); 3220 } 3221 else if(coef->is_number()) { 3222 auto p_cst = (*((constant<type>*)(coef.get()))); 3223 if(f.func_is_number()){ 3224 return make_shared<constant<type>>(p_cst -= eval(f.copy(),0)); 3225 } 3226 return make_shared<func<type>>(func(p_cst) -= f); 3227 } 3228 return nullptr; 3229 } 3230 3231 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> add_cst(const constant<T2> & f)3232 void add_cst(const constant<T2>& f){ 3233 if (_cst->is_function()) { 3234 auto f_cst = *static_pointer_cast<func<type>>(_cst); 3235 if(f_cst.func_is_number()){ 3236 embed(f_cst); 3237 _cst = make_shared<constant<type>>(eval(f_cst.copy(),0) + eval(f.copy(),0)); 3238 } 3239 else { 3240 f_cst += func<type>(f); 3241 embed(f_cst); 3242 _cst = make_shared<func<type>>(move(f_cst)); 3243 } 3244 } 3245 else if(_cst->is_param()) { 3246 auto p_cst = *static_pointer_cast<param<type>>(_cst); 3247 auto new_cst = f + p_cst; 3248 embed(new_cst); 3249 _cst = make_shared<func<type>>(move(new_cst)); 3250 } 3251 else if(_cst->is_number()) { 3252 auto p_cst = *static_pointer_cast<constant<type>>(_cst); 3253 auto new_cst = f + p_cst; 3254 _cst = make_shared<constant<type>>(new_cst); 3255 } 3256 } 3257 3258 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> add_cst(const param<T2> & f)3259 void add_cst(const param<T2>& f){ 3260 if (_cst->is_function()) { 3261 auto f_cst = *static_pointer_cast<func<type>>(_cst); 3262 f_cst += func<type>(f); 3263 embed(f_cst); 3264 _cst = make_shared<func<type>>(move(f_cst)); 3265 } 3266 else if(_cst->is_param()) { 3267 auto p_cst = *static_pointer_cast<param<type>>(_cst); 3268 auto f_cst = f + p_cst; 3269 embed(f_cst); 3270 _cst = make_shared<func<type>>(move(f_cst)); 3271 } 3272 else if(_cst->is_number()) { 3273 auto p_cst = *static_pointer_cast<constant<type>>(_cst); 3274 auto f_cst = f + p_cst; 3275 embed(f_cst); 3276 _cst = make_shared<func<type>>(move(f_cst)); 3277 } 3278 } 3279 3280 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> add_cst(const func<T2> & f)3281 void add_cst(const func<T2>& f){ 3282 if (_cst->is_function()) { 3283 auto f_cst = *static_pointer_cast<func<type>>(_cst); 3284 if(f_cst.func_is_number() && f.func_is_number()){ 3285 _cst = make_shared<constant<type>>(eval(f_cst.copy(),0) + eval(f.copy(),0)); 3286 } 3287 else { 3288 f_cst += f; 3289 embed(f_cst); 3290 _cst = make_shared<func<type>>(move(f_cst)); 3291 } 3292 } 3293 else if(_cst->is_param()) { 3294 auto p_cst = *static_pointer_cast<param<type>>(_cst); 3295 auto f_cst = f + func<type>(p_cst); 3296 embed(f_cst); 3297 _cst = make_shared<func<type>>(move(f_cst)); 3298 } 3299 else if(_cst->is_number()) { 3300 auto p_cst = *static_pointer_cast<constant<type>>(_cst); 3301 if(f.func_is_number()){ 3302 _cst = make_shared<constant<type>>(p_cst += eval(f.copy(),0)); 3303 } 3304 else { 3305 auto f_cst = f + func<type>(p_cst); 3306 embed(f_cst); 3307 _cst = make_shared<func<type>>(move(f_cst)); 3308 } 3309 } 3310 } 3311 3312 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> func(const uexpr<T2> & ue)3313 func(const uexpr<T2>& ue):func(){ 3314 _expr = make_shared<uexpr<type>>(ue); 3315 embed(_expr); 3316 if (!is_constant()) { 3317 _ftype = nlin_; 3318 } 3319 _dim[0] = ue._dim[0]; 3320 _dim[1] = ue._dim[1]; 3321 _evaluated = false; 3322 _range->first = ue._range->first; 3323 _range->second = ue._range->second; 3324 _all_convexity = ue._all_convexity; 3325 _all_sign = ue._all_sign; 3326 3327 }; 3328 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> func(const bexpr<T2> & be)3329 func(const bexpr<T2>& be):func(){ 3330 _expr = make_shared<bexpr<type>>(be); 3331 embed(_expr); 3332 if (!is_constant()) { 3333 _ftype = nlin_; 3334 } 3335 _dim[0] = be._dim[0]; 3336 _dim[1] = be._dim[1]; 3337 _evaluated = false; 3338 _range->first = be._range->first; 3339 _range->second = be._range->second; 3340 _all_convexity = be._all_convexity; 3341 _all_sign = be._all_sign; 3342 }; 3343 3344 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> func(T2 c)3345 func(T2 c):func(){ 3346 *this = constant<T2>(c); 3347 } 3348 3349 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> func(const constant<T2> & c)3350 func(const constant<T2>& c):func(){ 3351 *this = c; 3352 } 3353 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> func(const param<T2> & c)3354 func(const param<T2>& c):func(){ 3355 *this = c; 3356 } 3357 3358 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> func(const var<T2> & c)3359 func(const var<T2>& c):func(){ 3360 *this = c; 3361 } 3362 3363 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) < sizeof(type)>::type* = nullptr> 3364 func(const func<T2>& f): func(){ 3365 *this = f; 3366 } 3367 3368 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 3369 func& operator=(const constant<T2>& c){ 3370 reset(); 3371 static_pointer_cast<constant<type>>(_cst)->set_val(c.eval()); 3372 _all_sign = _cst->get_sign(); 3373 _val->resize(1); 3374 _val->at(0) = c.eval(); 3375 set_range(_val->at(0)); 3376 _all_sign = c.get_all_sign(); 3377 _is_vector = c._is_vector; 3378 _is_transposed = c._is_transposed; 3379 _dim[0] = c._dim[0]; 3380 _dim[1] = c._dim[1]; 3381 _evaluated = true; 3382 return *this; 3383 } 3384 3385 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 3386 func& operator=(const param<T2>& c){ 3387 reset(); 3388 insert(true,unit<type>(),c); 3389 _dim[0] = c.get_nb_inst(); 3390 _dim[1] = c._dim[1]; 3391 _is_transposed = c._is_transposed; 3392 _is_vector = c._is_vector; 3393 _val->clear(); 3394 _range->first = c._range->first; 3395 _range->second = c._range->second; 3396 _all_sign = c.get_all_sign(); 3397 _evaluated = false; 3398 if(c._indices){ 3399 _indices = make_shared<indices>(*c._indices); 3400 } 3401 // if(c.is_matrix_indexed()){ 3402 // _indices = c._indices; 3403 // } 3404 return *this; 3405 } 3406 3407 3408 func(func && f)3409 func(func&& f){ 3410 *this = move(f); 3411 } 3412 func(const func & f)3413 func(const func& f){ 3414 *this = f; 3415 } 3416 3417 3418 fcopy()3419 shared_ptr<func_> fcopy() const{return make_shared<func>(*this);}; 3420 copy()3421 shared_ptr<constant_> copy()const{return make_shared<func>(*this);}; 3422 deep_copy(const func & f)3423 void deep_copy(const func& f){ 3424 constant_::_type = f._type; 3425 _ftype = f._ftype; 3426 _return_type = f._return_type; 3427 _to_str = f._to_str; 3428 _all_convexity = f._all_convexity; 3429 _all_sign = f._all_sign; 3430 _params = make_shared<map<string, pair<shared_ptr<param_>, unsigned>>>(); 3431 for(auto const &pp: *f._params){ 3432 (*_params)[pp.second.first->get_name(false,false)] = {pp.second.first->ptr_deep_copy(),pp.second.second}; 3433 } 3434 _vars = make_shared<map<string, pair<shared_ptr<param_>, unsigned>>>(); 3435 for(auto const &vp: *f._vars){ 3436 (*_vars)[vp.second.first->get_name(false,false)] = {vp.second.first->ptr_deep_copy(),vp.second.second}; 3437 } 3438 _val = make_shared<vector<type>>(); 3439 _range = make_shared<pair<type,type>>(); 3440 _lterms = make_shared<map<string, lterm>>(); 3441 _qterms = make_shared<map<string, qterm>>(); 3442 _pterms = make_shared<map<string, pterm>>(); 3443 for (auto pair:*f._lterms) { 3444 if (pair.second._coef->is_function()) { 3445 auto coef = *static_pointer_cast<func<type>>(pair.second._coef); 3446 pair.second._coef = coef.copy(); 3447 embed(*static_pointer_cast<func>(pair.second._coef)); 3448 } 3449 else if(pair.second._coef->is_param()) { 3450 auto coef = *static_pointer_cast<param<type>>(pair.second._coef); 3451 pair.second._coef = func(coef).copy(); 3452 embed(*static_pointer_cast<func>(pair.second._coef)); 3453 } 3454 auto pname = pair.second._p->get_name(false,false); 3455 if (pair.second._p->is_var()) { 3456 pair.second._p = _vars->at(pname).first; 3457 } 3458 else { 3459 pair.second._p = _params->at(pname).first; 3460 } 3461 (*_lterms)[pair.first] = pair.second; 3462 } 3463 for (auto pair:*f._qterms) { 3464 if (pair.second._coef->is_function()) { 3465 auto coef = *static_pointer_cast<func<type>>(pair.second._coef); 3466 pair.second._coef = coef.copy(); 3467 embed(*static_pointer_cast<func>(pair.second._coef)); 3468 } 3469 else if(pair.second._coef->is_param()) { 3470 auto coef = *static_pointer_cast<param<type>>(pair.second._coef); 3471 pair.second._coef = func(coef).copy(); 3472 embed(*static_pointer_cast<func>(pair.second._coef)); 3473 } 3474 auto pname1 = pair.second._p->first->get_name(false,false); 3475 auto pname2 = pair.second._p->second->get_name(false,false); 3476 if (pair.second._p->first->is_var()) { 3477 pair.second._p->first = _vars->at(pname1).first; 3478 pair.second._p->second = _vars->at(pname2).first; 3479 } 3480 else { 3481 pair.second._p->first = _params->at(pname1).first; 3482 pair.second._p->second = _params->at(pname2).first; 3483 } 3484 (*_qterms)[pair.first] = pair.second; 3485 } 3486 for (auto pair:*f._pterms) { 3487 if (pair.second._coef->is_function()) { 3488 auto coef = *static_pointer_cast<func<type>>(pair.second._coef); 3489 pair.second._coef = coef.copy(); 3490 embed(*static_pointer_cast<func>(pair.second._coef)); 3491 } 3492 else if(pair.second._coef->is_param()) { 3493 auto coef = *static_pointer_cast<param<type>>(pair.second._coef); 3494 pair.second._coef = func(coef).copy(); 3495 embed(*static_pointer_cast<func>(pair.second._coef)); 3496 } 3497 auto newl = make_shared<list<std::pair<shared_ptr<param_>, int>>>(); 3498 for(auto &vp: *pair.second._l){ 3499 std::pair<shared_ptr<param_>, int> new_pair; 3500 auto pname = vp.first->get_name(false,false); 3501 if (vp.first->is_var()) { 3502 new_pair.first = _vars->at(pname).first; 3503 } 3504 else { 3505 new_pair.first = _params->at(pname).first; 3506 } 3507 new_pair.second = vp.second; 3508 newl->push_back(new_pair); 3509 } 3510 pair.second._l = newl; 3511 (*_pterms)[pair.first] = pair.second; 3512 } 3513 if(f._expr){ 3514 if (f._expr->is_uexpr()) { 3515 _expr = make_shared<uexpr<type>>(*static_pointer_cast<uexpr<type>>(f._expr)); 3516 } 3517 else { 3518 _expr = make_shared<bexpr<type>>(*static_pointer_cast<bexpr<type>>(f._expr)); 3519 } 3520 embed(_expr); 3521 } 3522 else { 3523 _expr = nullptr; 3524 } 3525 if (f._cst->is_function()) { 3526 auto coef = *static_pointer_cast<func<type>>(f._cst); 3527 _cst = func(coef).copy(); 3528 embed(*static_pointer_cast<func>(_cst)); 3529 } 3530 else if(f._cst->is_param()) { 3531 auto coef = *static_pointer_cast<param<type>>(f._cst); 3532 _cst = func(coef).copy(); 3533 embed(*static_pointer_cast<func>(_cst)); 3534 } 3535 else if(f._cst->is_number()) { 3536 auto coef = *static_pointer_cast<constant<type>>(f._cst); 3537 _cst = constant<type>(coef.eval()).copy(); 3538 } 3539 if(f._indices){ 3540 _indices = make_shared<indices>(f._indices->deep_copy()); 3541 } 3542 else { 3543 _indices = nullptr; 3544 } 3545 _range->first = f._range->first; 3546 _range->second = f._range->second; 3547 // _val->clear(); 3548 _val->resize(f._val->size()); 3549 for(auto i = 0; i< f._val->size(); i++){ 3550 _val->at(i) = f._val->at(i); 3551 } 3552 *_convexity = *f._convexity; 3553 _sign = f._sign; 3554 constant_::_is_transposed = f._is_transposed; 3555 constant_::_is_vector = f._is_vector; 3556 if(f._is_constraint) 3557 _is_constraint = true; 3558 _is_hessian = f._is_hessian; 3559 constant_::_dim[0] = f._dim[0]; 3560 constant_::_dim[1] = f._dim[1]; 3561 _embedded = f._embedded; 3562 _dfdx = make_shared<map<string,shared_ptr<func<type>>>>(); 3563 if(f._hess_link){ 3564 _hess_link = make_shared<map<size_t, set<size_t>>>(*f._hess_link); 3565 } 3566 else { 3567 _hess_link = nullptr; 3568 } 3569 _nnz_j = f._nnz_j; 3570 _nnz_h = f._nnz_h; 3571 _hess_link = f._hess_link; 3572 _nb_vars = f._nb_vars; 3573 _evaluated = f._evaluated; 3574 } 3575 3576 3577 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) < sizeof(type)>::type* = nullptr> 3578 void deep_copy(const func<T2>& f){ 3579 constant_::_type = f._type; 3580 _ftype = f._ftype; 3581 _return_type = f._return_type; 3582 _to_str = f._to_str; 3583 _all_convexity = f._all_convexity; 3584 _all_sign = f._all_sign; 3585 _params = make_shared<map<string, pair<shared_ptr<param_>, unsigned>>>(); 3586 for(auto const &pp: *f._params){ 3587 (*_params)[pp.second.first->get_name(false,false)] = {pp.second.first->ptr_deep_copy(),pp.second.second}; 3588 } 3589 _vars = make_shared<map<string, pair<shared_ptr<param_>, unsigned>>>(); 3590 for(auto const &vp: *f._vars){ 3591 (*_vars)[vp.second.first->get_name(false,false)] = {vp.second.first->ptr_deep_copy(),vp.second.second}; 3592 } 3593 _val = make_shared<vector<type>>(); 3594 _range = make_shared<pair<type,type>>(); 3595 _lterms = make_shared<map<string, lterm>>(); 3596 _qterms = make_shared<map<string, qterm>>(); 3597 _pterms = make_shared<map<string, pterm>>(); 3598 for (auto pair:*f._lterms) { 3599 if (pair.second._coef->is_function()) { 3600 auto coef = *static_pointer_cast<func<T2>>(pair.second._coef); 3601 pair.second._coef = coef.copy(); 3602 embed(*static_pointer_cast<func>(pair.second._coef)); 3603 } 3604 else if(pair.second._coef->is_param()) { 3605 auto coef = *static_pointer_cast<param<T2>>(pair.second._coef); 3606 pair.second._coef = func(coef).copy(); 3607 embed(*static_pointer_cast<func>(pair.second._coef)); 3608 } 3609 auto pname = pair.second._p->get_name(false,false); 3610 if (pair.second._p->is_var()) { 3611 pair.second._p = _vars->at(pname).first; 3612 } 3613 else { 3614 pair.second._p = _params->at(pname).first; 3615 } 3616 (*_lterms)[pair.first] = pair.second; 3617 } 3618 for (auto pair:*f._qterms) { 3619 if (pair.second._coef->is_function()) { 3620 auto coef = *static_pointer_cast<func<T2>>(pair.second._coef); 3621 pair.second._coef = coef.copy(); 3622 embed(*static_pointer_cast<func>(pair.second._coef)); 3623 } 3624 else if(pair.second._coef->is_param()) { 3625 auto coef = *static_pointer_cast<param<T2>>(pair.second._coef); 3626 pair.second._coef = func(coef).copy(); 3627 embed(*static_pointer_cast<func>(pair.second._coef)); 3628 } 3629 auto pname1 = pair.second._p->first->get_name(false,false); 3630 auto pname2 = pair.second._p->second->get_name(false,false); 3631 if (pair.second._p->first->is_var()) { 3632 pair.second._p->first = _vars->at(pname1).first; 3633 pair.second._p->second = _vars->at(pname2).first; 3634 } 3635 else { 3636 pair.second._p->first = _params->at(pname1).first; 3637 pair.second._p->second = _params->at(pname2).first; 3638 } 3639 (*_qterms)[pair.first] = pair.second; 3640 } 3641 for (auto pair:*f._pterms) { 3642 if (pair.second._coef->is_function()) { 3643 auto coef = *static_pointer_cast<func<T2>>(pair.second._coef); 3644 pair.second._coef = coef.copy(); 3645 embed(*static_pointer_cast<func>(pair.second._coef)); 3646 } 3647 else if(pair.second._coef->is_param()) { 3648 auto coef = *static_pointer_cast<param<T2>>(pair.second._coef); 3649 pair.second._coef = func(coef).copy(); 3650 embed(*static_pointer_cast<func>(pair.second._coef)); 3651 } 3652 auto newl = make_shared<list<std::pair<shared_ptr<param_>, int>>>(); 3653 for(auto &vp: *pair.second._l){ 3654 std::pair<shared_ptr<param_>, int> new_pair; 3655 auto pname = vp.first->get_name(false,false); 3656 if (vp.first->is_var()) { 3657 new_pair.first = _vars->at(pname).first; 3658 } 3659 else { 3660 new_pair.first = _params->at(pname).first; 3661 } 3662 new_pair.second = vp.second; 3663 newl->push_back(new_pair); 3664 } 3665 pair.second._l = newl; 3666 (*_pterms)[pair.first] = pair.second; 3667 } 3668 if(f._expr){ 3669 if (f._expr->is_uexpr()) { 3670 _expr = make_shared<uexpr<type>>(*static_pointer_cast<uexpr<T2>>(f._expr)); 3671 } 3672 else { 3673 _expr = make_shared<bexpr<type>>(*static_pointer_cast<bexpr<T2>>(f._expr)); 3674 } 3675 embed(_expr); 3676 } 3677 else { 3678 _expr = nullptr; 3679 } 3680 if (f._cst->is_function()) { 3681 auto coef = *static_pointer_cast<func<T2>>(f._cst); 3682 _cst = func(coef).copy(); 3683 embed(*static_pointer_cast<func>(_cst)); 3684 } 3685 else if(f._cst->is_param()) { 3686 auto coef = *static_pointer_cast<param<T2>>(f._cst); 3687 _cst = func(coef).copy(); 3688 embed(*static_pointer_cast<func>(_cst)); 3689 } 3690 else if(f._cst->is_number()) { 3691 auto coef = *static_pointer_cast<constant<T2>>(f._cst); 3692 _cst = constant<type>(coef.eval()).copy(); 3693 } 3694 if(f._indices){ 3695 _indices = make_shared<indices>(f._indices->deep_copy()); 3696 } 3697 else { 3698 _indices = nullptr; 3699 } 3700 _range->first = f._range->first; 3701 _range->second = f._range->second; 3702 // _val->clear(); 3703 _val->resize(f._val->size()); 3704 for(auto i = 0; i< f._val->size(); i++){ 3705 _val->at(i) = f._val->at(i); 3706 } 3707 *_convexity = *f._convexity; 3708 _sign = f._sign; 3709 constant_::_is_transposed = f._is_transposed; 3710 constant_::_is_vector = f._is_vector; 3711 if(f._is_constraint) 3712 _is_constraint = true; 3713 _is_hessian = f._is_hessian; 3714 constant_::_dim[0] = f._dim[0]; 3715 constant_::_dim[1] = f._dim[1]; 3716 _embedded = f._embedded; 3717 _dfdx = make_shared<map<string,shared_ptr<func<type>>>>(); 3718 if(f._hess_link){ 3719 _hess_link = make_shared<map<size_t, set<size_t>>>(*f._hess_link); 3720 } 3721 else { 3722 _hess_link = nullptr; 3723 } 3724 _nnz_j = f._nnz_j; 3725 _nnz_h = f._nnz_h; 3726 _hess_link = f._hess_link; 3727 _nb_vars = f._nb_vars; 3728 _evaluated = f._evaluated; 3729 } 3730 3731 func& operator=(const func& f){ 3732 constant_::_type = f._type; 3733 _ftype = f._ftype; 3734 _return_type = f._return_type; 3735 _to_str = f._to_str; 3736 _all_convexity = f._all_convexity; 3737 _all_sign = f._all_sign; 3738 _params = make_shared<map<string, pair<shared_ptr<param_>, unsigned>>>(); 3739 if (f._cst->is_function()) { 3740 auto coef = *static_pointer_cast<func<type>>(f._cst); 3741 _cst = func(coef).copy(); 3742 embed(*static_pointer_cast<func>(_cst)); 3743 } 3744 else if(f._cst->is_param()) { 3745 auto coef = *static_pointer_cast<param<type>>(f._cst); 3746 _cst = func(coef).copy(); 3747 embed(*static_pointer_cast<func>(_cst)); 3748 } 3749 else if(f._cst->is_number()) { 3750 auto coef = *static_pointer_cast<constant<type>>(f._cst); 3751 _cst = constant<type>(coef.eval()).copy(); 3752 } 3753 _val = make_shared<vector<type>>(); 3754 _range = make_shared<pair<type,type>>(); 3755 _vars = make_shared<map<string, pair<shared_ptr<param_>, unsigned>>>(); 3756 _lterms = make_shared<map<string, lterm>>(); 3757 _qterms = make_shared<map<string, qterm>>(); 3758 _pterms = make_shared<map<string, pterm>>(); 3759 for (auto &pair:*f._lterms) { 3760 this->insert(pair.second); 3761 } 3762 for (auto &pair:*f._qterms) { 3763 this->insert(pair.second); 3764 } 3765 for (auto &pair:*f._pterms) { 3766 this->insert(pair.second); 3767 } 3768 if(f._expr){ 3769 if (f._expr->is_uexpr()) { 3770 _expr = make_shared<uexpr<type>>(*static_pointer_cast<uexpr<type>>(f._expr)); 3771 } 3772 else { 3773 _expr = make_shared<bexpr<type>>(*static_pointer_cast<bexpr<type>>(f._expr)); 3774 } 3775 embed(_expr); 3776 } 3777 else { 3778 _expr = nullptr; 3779 } 3780 if(f._indices){ 3781 _indices = make_shared<indices>(*f._indices); 3782 } 3783 else { 3784 _indices = nullptr; 3785 } 3786 _range->first = f._range->first; 3787 _range->second = f._range->second; 3788 // _val->clear(); 3789 _val->resize(f._val->size()); 3790 for(auto i = 0; i< f._val->size(); i++){ 3791 _val->at(i) = f._val->at(i); 3792 } 3793 *_convexity = *f._convexity; 3794 _sign = f._sign; 3795 constant_::_is_transposed = f._is_transposed; 3796 constant_::_is_vector = f._is_vector; 3797 if(f._is_constraint) 3798 _is_constraint = true; 3799 _is_hessian = f._is_hessian; 3800 constant_::_dim[0] = f._dim[0]; 3801 constant_::_dim[1] = f._dim[1]; 3802 _embedded = f._embedded; 3803 _dfdx = make_shared<map<string,shared_ptr<func<type>>>>(); 3804 // copy_derivatives(f); 3805 if(f._hess_link){ 3806 _hess_link = make_shared<map<size_t, set<size_t>>>(*f._hess_link); 3807 } 3808 else { 3809 _hess_link = nullptr; 3810 } 3811 _nnz_j = f._nnz_j; 3812 _nnz_h = f._nnz_h; 3813 _hess_link = f._hess_link; 3814 _nb_vars = f._nb_vars; 3815 _evaluated = f._evaluated; 3816 return *this; 3817 } 3818 3819 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 3820 func& operator=(const func<T2>& f){ 3821 _to_str = f._to_str; 3822 _all_convexity = f._all_convexity; 3823 _all_sign = f._all_sign; 3824 _params = make_shared<map<string, pair<shared_ptr<param_>, unsigned>>>(); 3825 if (f._cst->is_function()) { 3826 auto coef = *static_pointer_cast<func<T2>>(f._cst); 3827 _cst = func(coef).copy(); 3828 embed(*static_pointer_cast<func>(_cst)); 3829 } 3830 else if(f._cst->is_param()) { 3831 auto coef = *static_pointer_cast<param<T2>>(f._cst); 3832 _cst = func(coef).copy(); 3833 embed(*static_pointer_cast<func>(_cst)); 3834 } 3835 else if(f._cst->is_number()) { 3836 auto coef = *static_pointer_cast<constant<T2>>(f._cst); 3837 _cst = constant<type>(coef.eval()).copy(); 3838 } 3839 _val = make_shared<vector<type>>(); 3840 _range = make_shared<pair<type,type>>(); 3841 _vars = make_shared<map<string, pair<shared_ptr<param_>, unsigned>>>(); 3842 _lterms = make_shared<map<string, lterm>>(); 3843 _qterms = make_shared<map<string, qterm>>(); 3844 _pterms = make_shared<map<string, pterm>>(); 3845 for (auto &pair:*f._lterms) { 3846 auto term = pair.second; 3847 if (term._coef->is_function()) { 3848 auto coef = *static_pointer_cast<func<T2>>(term._coef); 3849 term._coef = func(coef).copy(); 3850 } 3851 else if(term._coef->is_param()) { 3852 auto coef = *static_pointer_cast<param<T2>>(term._coef); 3853 term._coef = param<type>(coef).copy(); 3854 } 3855 else if(term._coef->is_number()) { 3856 auto coef = *static_pointer_cast<constant<T2>>(term._coef); 3857 term._coef = constant<type>(coef).copy(); 3858 } 3859 this->insert(term); 3860 } 3861 for (auto &pair:*f._qterms) { 3862 auto term = pair.second; 3863 if (term._coef->is_function()) { 3864 auto coef = *static_pointer_cast<func<T2>>(term._coef); 3865 term._coef = func(coef).copy(); 3866 } 3867 else if(term._coef->is_param()) { 3868 auto coef = *static_pointer_cast<param<T2>>(term._coef); 3869 term._coef = param<type>(coef).copy(); 3870 } 3871 else if(term._coef->is_number()) { 3872 auto coef = *static_pointer_cast<constant<T2>>(term._coef); 3873 term._coef = constant<type>(coef.eval()).copy(); 3874 } 3875 this->insert(term); 3876 } 3877 for (auto &pair:*f._pterms) { 3878 auto term = pair.second; 3879 if (term._coef->is_function()) { 3880 auto coef = *static_pointer_cast<func<T2>>(term._coef); 3881 term._coef = func(coef).copy(); 3882 } 3883 else if(term._coef->is_param()) { 3884 auto coef = *static_pointer_cast<param<T2>>(term._coef); 3885 term._coef = param<type>(coef).copy(); 3886 } 3887 else if(term._coef->is_number()) { 3888 auto coef = *static_pointer_cast<constant<T2>>(term._coef); 3889 term._coef = constant<type>(coef.eval()).copy(); 3890 } 3891 this->insert(term); 3892 } 3893 if(f._expr){ 3894 if (f._expr->is_uexpr()) { 3895 auto uexp = make_shared<uexpr<type>>(*static_pointer_cast<uexpr<T2>>(f._expr)); 3896 if (uexp->_son->is_function()) { 3897 auto f = static_pointer_cast<func<T2>>(uexp->_son); 3898 uexp->_son = make_shared<func>(*f); 3899 } 3900 _expr = uexp; 3901 } 3902 else { 3903 auto bexp = make_shared<bexpr<type>>(*static_pointer_cast<bexpr<T2>>(f._expr)); 3904 if (bexp->_lson->is_function()) { 3905 auto f = static_pointer_cast<func<T2>>(bexp->_lson); 3906 bexp->_lson = make_shared<func>(*f); 3907 } 3908 if (bexp->_rson->is_function()) { 3909 auto f = static_pointer_cast<func<T2>>(bexp->_rson); 3910 bexp->_rson = make_shared<func>(*f); 3911 } 3912 _expr = bexp; 3913 } 3914 } 3915 else { 3916 _expr = nullptr; 3917 } 3918 if(f._indices){ 3919 _indices = make_shared<indices>(*f._indices); 3920 } 3921 else { 3922 _indices = nullptr; 3923 } 3924 _range->first = f._range->first; 3925 _range->second = f._range->second; 3926 _val->clear(); 3927 _val->resize(f._val->size()); 3928 for(auto i = 0; i< f._val->size(); i++){ 3929 _val->at(i) = f._val->at(i); 3930 } 3931 *_convexity = *f._convexity; 3932 _sign = f._sign; 3933 constant_::_is_transposed = f._is_transposed; 3934 constant_::_is_vector = f._is_vector; 3935 if(f._is_constraint) 3936 _is_constraint = true; 3937 _is_hessian = f._is_hessian; 3938 constant_::_dim[0] = f._dim[0]; 3939 constant_::_dim[1] = f._dim[1]; 3940 _embedded = f._embedded; 3941 _dfdx = make_shared<map<string,shared_ptr<func>>>(); 3942 // copy_derivatives(f); 3943 if(f._hess_link){ 3944 _hess_link = make_shared<map<size_t, set<size_t>>>(*f._hess_link); 3945 } 3946 else { 3947 _hess_link = nullptr; 3948 } 3949 _nnz_j = f._nnz_j; 3950 _nnz_h = f._nnz_h; 3951 _hess_link = f._hess_link; 3952 _nb_vars = f._nb_vars; 3953 _evaluated = f._evaluated; 3954 return *this; 3955 } 3956 3957 func& operator=(func&& f){ 3958 constant_::_type = f._type; 3959 _ftype = f._ftype; 3960 _to_str = f._to_str; 3961 _return_type = f._return_type; 3962 _all_convexity = f._all_convexity; 3963 _all_sign = f._all_sign; 3964 _lterms = move(f._lterms); 3965 _qterms = move(f._qterms); 3966 _pterms = move(f._pterms); 3967 _expr = move(f._expr); 3968 _vars = move(f._vars); 3969 _params = move(f._params); 3970 _cst = move(f._cst); 3971 _indices = move(f._indices); 3972 _range = move(f._range); 3973 _val = move(f._val); 3974 _convexity = move(f._convexity); 3975 _sign = f._sign; 3976 f._sign = nullptr; 3977 constant_::_is_transposed = f._is_transposed; 3978 constant_::_is_vector = f._is_vector; 3979 if(f._is_constraint) 3980 _is_constraint = true; 3981 _is_hessian = f._is_hessian; 3982 constant_::_dim[0] = f._dim[0]; 3983 constant_::_dim[1] = f._dim[1]; 3984 _embedded = f._embedded; 3985 _dfdx = move(f._dfdx); 3986 _nnz_j = f._nnz_j; 3987 _nnz_h = f._nnz_h; 3988 _hess_link = f._hess_link; 3989 _nb_vars = f._nb_vars; 3990 _evaluated = f._evaluated; 3991 return *this; 3992 } 3993 3994 /* Modifiers */ set_size(const vector<size_t> & dims)3995 void set_size(const vector<size_t>& dims){ 3996 if (dims.size()==1) { 3997 set_size(dims[0]); 3998 } 3999 else if (dims.size()==2){ 4000 set_size(dims[0],dims[1]); 4001 } 4002 else { 4003 throw invalid_argument("In Function set_size(vector<size_t> dims), dims.size() should be less or equal 2. \n"); 4004 } 4005 } 4006 set_size(size_t s1,size_t s2)4007 void set_size(size_t s1, size_t s2) { 4008 _dim[0] = s1; 4009 _dim[1] = s2; 4010 auto dim = _dim[0]*_dim[1]; 4011 _val->resize(dim); 4012 if (is_matrix()) { 4013 _is_vector = true; 4014 } 4015 }; 4016 set_size(size_t s)4017 void set_size(size_t s) { 4018 _val->resize(s); 4019 _dim[0] = s; 4020 }; 4021 4022 add_val(type val)4023 void add_val(type val) { 4024 if(is_matrix()){ 4025 throw invalid_argument("Cannot call func::add_val(type val) on matrix"); 4026 } 4027 _val->push_back(val); 4028 update_range(val); 4029 _dim[0] = _val->size(); 4030 } 4031 set_range(type val)4032 void set_range(type val) { 4033 _range->first = val; 4034 _range->second = val; 4035 } 4036 update_range(type val)4037 void update_range(type val) { 4038 if (val <= _range->first) { 4039 _range->first = val; 4040 } 4041 if (val >= _range->second) { 4042 _range->second = val; 4043 } 4044 } 4045 add_val(size_t i,type val)4046 void add_val(size_t i, type val) { 4047 if(is_matrix()){ 4048 throw invalid_argument("Cannot call func::add_val(type val) on matrix"); 4049 } 4050 _dim[0] = gravity::max(_dim[0],i+1); 4051 _val->resize(gravity::max(_val->size(),i+1)); 4052 _val->at(i) = val; 4053 update_range(val); 4054 } 4055 set_val(size_t i,size_t j,type val)4056 void set_val(size_t i, size_t j, type val) { 4057 if(!is_matrix()){ 4058 throw invalid_argument("Function set_val(size_t i, size_t j, type val) should be called on a matrix"); 4059 } 4060 if(_dim[0] <= i || _dim[1] <= j){ 4061 throw invalid_argument("In Function set_val(size_t i, size_t j, type val), i or j are out of bounds"); 4062 } 4063 if (_is_transposed) { 4064 _val->at(_dim[0]*j+i) = val; 4065 } 4066 _val->at(_dim[1]*i+j) = val; 4067 update_range(val); 4068 } 4069 4070 4071 set_val(const string & key,type val)4072 size_t set_val(const string& key, type val) { 4073 auto it = _indices->_keys_map->find(key); 4074 if (it == _indices->_keys_map->end()){ 4075 throw invalid_argument("in Function size_t set_val(const string& key, type val), unknown key" + key); 4076 } 4077 _val->at(it->second) = val; 4078 update_range(val); 4079 return it->second; 4080 } 4081 add_val(const string & key,type val)4082 size_t add_val(const string& key, type val) { 4083 if(!_indices){ 4084 _indices = make_shared<indices>(); 4085 } 4086 auto index = _indices->size(); 4087 auto pp = _indices->_keys_map->insert(make_pair<>(key,index)); 4088 if (pp.second) {//new index inserted 4089 _val->resize(gravity::max(_val->size(),index+1)); 4090 _dim[0] = gravity::max(_dim[0],_val->size()); 4091 _indices->_keys->resize(_val->size()); 4092 _indices->_keys->at(index) = key; 4093 _val->at(index) = val; 4094 update_range(val); 4095 return index; 4096 } 4097 else { 4098 Warning("WARNING: calling add_val(const string& key, T val) with an existing key, overriding existing value" << endl); 4099 _val->at(pp.first->second) = val; 4100 update_range(val); 4101 return pp.first->second; 4102 } 4103 } 4104 add_val(size_t i,size_t j,type val)4105 void add_val(size_t i, size_t j, type val) { 4106 _is_vector = true; 4107 _dim[0] = gravity::max(_dim[0],i+1); 4108 _dim[1] = gravity::max(_dim[1],j+1); 4109 auto index = _dim[1]*i+j; 4110 _val->resize(gravity::max(_val->size(),index+1)); 4111 _val->at(index) = val; 4112 update_range(val); 4113 } 4114 set_val(size_t i,type val)4115 void set_val(size_t i, type val) { 4116 if(is_matrix()){ 4117 throw invalid_argument("set_val(size_t i, type val) should be called with double index here\n"); 4118 } 4119 if (is_indexed()) { 4120 if (_indices->_ids->size()>1) { 4121 throw invalid_argument("set_val(size_t i, type val) should be called with double index here\n"); 4122 } 4123 if (_val->size()<=_indices->_ids->at(0).at(i)){ 4124 throw invalid_argument("Param set_val(size_t i, type val) out of range"); 4125 } 4126 _val->at(_indices->_ids->at(0).at(i)) = val; 4127 } 4128 if (_val->size()<=i){ 4129 throw invalid_argument("Param set_val(size_t i, type val) out of range"); 4130 } 4131 _val->at(i) = val; 4132 update_range(val); 4133 } 4134 set_val(type val)4135 void set_val(type val) { 4136 if(is_indexed()){ 4137 for(auto &idx: _indices->_ids->at(0)){ 4138 _val->at(idx) = val; 4139 } 4140 } 4141 else { 4142 for (auto i = 0; i<_val->size() ;i++) { 4143 _val->at(i) = val; 4144 } 4145 } 4146 update_range(val); 4147 } 4148 4149 template<typename T=type, get_sign(size_t idx)4150 typename enable_if<is_arithmetic<T>::value>::type* = nullptr> Sign get_sign(size_t idx) const{ 4151 if (_val->at(idx)==0) { 4152 return zero_; 4153 } 4154 if (_val->at(idx)< 0) { 4155 return neg_; 4156 } 4157 if (_val->at(idx)> 0) { 4158 return pos_; 4159 } 4160 return unknown_; 4161 } 4162 get_sign(size_t idx)4163 template<class T=type, typename enable_if<is_same<T, Cpx>::value>::type* = nullptr> Sign get_sign(size_t idx) const{ 4164 if (_val->at(idx) == Cpx(0,0)) { 4165 return zero_; 4166 } 4167 if ((_val->at(idx).real() < 0 && _val->at(idx).imag() < 0)) { 4168 return neg_; 4169 } 4170 if ((_val->at(idx).real() > 0 && _val->at(idx).imag() > 0)) { 4171 return pos_; 4172 } 4173 if ((_val->at(idx).real() <= 0 && _val->at(idx).imag() <= 0)) { 4174 return non_pos_; 4175 } 4176 if ((_val->at(idx).real() >= 0 && _val->at(idx).imag() >= 0)) { 4177 return non_neg_; 4178 } 4179 return unknown_; 4180 } 4181 4182 4183 // template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 4184 // shared_ptr<constant_> add(shared_ptr<param<T2>> c1){ 4185 // 4186 // } 4187 4188 4189 get_all_sign()4190 Sign get_all_sign() const { 4191 return _all_sign; 4192 } 4193 4194 4195 get_val()4196 type get_val() const { 4197 if (is_indexed()) { 4198 return _val->at(_indices->_ids->at(0).back()); 4199 } 4200 return _val->back(); 4201 } 4202 get_val(size_t i)4203 type get_val(size_t i) const{ 4204 if(func_is_number()){ 4205 return _val->at(0); 4206 } 4207 auto idx = get_id_inst(i); 4208 if (is_indexed()) { 4209 if (_indices->_ids->size()>1) { 4210 throw invalid_argument("eval() should be called with double index here\n"); 4211 } 4212 if (_val->size()<=idx){ 4213 throw invalid_argument("Param eval out of range"); 4214 } 4215 return _val->at(idx); 4216 } 4217 if (_val->size()<=idx){ 4218 throw invalid_argument("Param eval out of range"); 4219 } 4220 return _val->at(idx); 4221 } 4222 get_val(size_t i,size_t j)4223 type get_val(size_t i, size_t j) const{ 4224 auto idx = get_id_inst(i,j); 4225 if (is_indexed()) { 4226 // if (_indices->_ids->size()>1) { 4227 // throw invalid_argument("eval() should be called with double index here\n"); 4228 // } 4229 if (_val->size()<=idx){ 4230 throw invalid_argument("Param eval out of range"); 4231 } 4232 return _val->at(idx); 4233 } 4234 if (_val->size()<=idx){ 4235 throw invalid_argument("Param eval out of range"); 4236 } 4237 return _val->at(idx); 4238 } 4239 eval_matrix()4240 void eval_matrix() { 4241 for (size_t i = 0; i < _dim[0]; i++) { 4242 for (size_t j = 0; j < _dim[1]; j++) { 4243 set_val(i,j,eval(i,j)); 4244 } 4245 } 4246 } 4247 4248 4249 uneval()4250 void uneval() { 4251 _evaluated = false; 4252 _cst->uneval(); 4253 for (auto &pair:*_lterms) { 4254 auto coef = pair.second._coef; 4255 if(coef->is_function()){ 4256 static_pointer_cast<func>(coef)->uneval(); 4257 } 4258 } 4259 for (auto &pair:*_qterms) { 4260 auto coef = pair.second._coef; 4261 if (coef->is_function()){ 4262 static_pointer_cast<func>(coef)->uneval(); 4263 } 4264 } 4265 for (auto &pair:*_pterms) { 4266 auto coef = pair.second._coef; 4267 if(coef->is_function()){ 4268 static_pointer_cast<func>(coef)->uneval(); 4269 } 4270 } 4271 if(_expr){ 4272 _expr->uneval(); 4273 } 4274 } 4275 eval_all()4276 void eval_all(){ 4277 // if(_val->size()==0){ 4278 allocate_mem(); 4279 // } 4280 auto nb_inst = get_nb_inst(); 4281 for (size_t inst = 0; inst<nb_inst; inst++) { 4282 eval(inst); 4283 } 4284 _evaluated = true; 4285 } 4286 4287 type eval(size_t i=0) { 4288 if(is_zero()){ 4289 if (func_is_number()){ 4290 assert(_val->size()>0); 4291 _val->at(0) = this->_range->first; 4292 return _val->at(0); 4293 } 4294 assert(_val->size()>i); 4295 _val->at(i) = this->_range->first; 4296 return _range->first; 4297 } 4298 // if (is_constant() && _evaluated) { 4299 if (_evaluated) { 4300 if (func_is_number()){ 4301 assert(_val->size()>0); 4302 return _val->at(0); 4303 } 4304 assert(_val->size()>i); 4305 return _val->at(i); 4306 } 4307 type res = 0; 4308 if(!_cst->is_zero()) 4309 res += eval_cst(i); 4310 if(!_lterms->empty()){ 4311 for (auto &pair:*_lterms) { 4312 if (pair.second._coef->_is_transposed || pair.second._coef->is_matrix() || pair.second._p->is_matrix_indexed()) { 4313 auto dim = pair.second._p->get_dim(i); 4314 if (pair.second._sign) { 4315 for (size_t j = 0; j<dim; j++) { 4316 res += eval_coef(pair.second._coef,i,j) * eval(pair.second._p,i,j); 4317 } 4318 } 4319 else { 4320 for (size_t j = 0; j<dim; j++) { 4321 res -= eval_coef(pair.second._coef,i,j) * eval(pair.second._p,i,j); 4322 } 4323 } 4324 } 4325 else { 4326 if (pair.second._sign) { 4327 res += eval_coef(pair.second._coef,i) * eval(pair.second._p,i); 4328 } 4329 else { 4330 res -= eval_coef(pair.second._coef,i) * eval(pair.second._p,i); 4331 } 4332 } 4333 } 4334 } 4335 // res += eval_lterms(i); 4336 if(!_qterms->empty()){ 4337 for (auto &pair:*_qterms) { 4338 type qval = 0; 4339 if (pair.second._coef_p1_tr) { // qterm = (coef*p1)^T*p2 4340 for (auto i = 0; i<pair.second._p->first->_dim[0]; i++) { 4341 for (auto j = 0; j<pair.second._p->first->_dim[0]; j++) { 4342 qval += eval_coef(pair.second._coef,i,j) * eval(pair.second._p->first,j) * eval(pair.second._p->second,i); 4343 } 4344 } 4345 } 4346 else if(pair.second._coef->_is_transposed || pair.second._p->first->is_matrix_indexed()|| pair.second._p->second->is_matrix_indexed()){/* C*element_wise(X.Y)^T */ 4347 auto dim = pair.second._p->first->get_dim(i); 4348 if (pair.second._sign) { 4349 for (size_t j = 0; j<dim; j++) { 4350 res += eval_coef(pair.second._coef,i,j) * eval(pair.second._p->first,i,j) * eval(pair.second._p->second,i,j); 4351 } 4352 } 4353 else { 4354 for (size_t j = 0; j<dim; j++) { 4355 res -= eval_coef(pair.second._coef,i,j) * eval(pair.second._p->first,i,j) * eval(pair.second._p->second,i,j); 4356 } 4357 } 4358 } 4359 else if (pair.second._p->first->is_matrix() && !pair.second._p->second->is_matrix() && !pair.second._p->second->_is_transposed) {//matrix * vect 4360 for (size_t j = 0; j<pair.second._p->second->_dim[0]; j++) { 4361 qval += eval(pair.second._p->first,i,j) * eval(pair.second._p->second,j); 4362 } 4363 qval *= eval_coef(pair.second._coef,i); 4364 } 4365 else if (!pair.second._p->first->is_matrix() && pair.second._p->first->_is_transposed && pair.second._p->second->is_matrix() ) {//transposed vect * matrix 4366 for (size_t j = 0; j<pair.second._p->first->_dim[0]; j++) { 4367 qval += eval(pair.second._p->first,j) * eval(pair.second._p->second,j,i); 4368 } 4369 qval *= eval_coef(pair.second._coef,i); 4370 } 4371 else if (!pair.second._p->first->is_matrix() && pair.second._p->first->_is_transposed && !pair.second._p->second->is_matrix() && i==0) {//TODO: what is this?? 4372 for (size_t j = 0; j<pair.second._p->first->_dim[1]; j++) { 4373 qval += eval(pair.second._p->first,j) * eval(pair.second._p->second,j); 4374 } 4375 qval *= eval_coef(pair.second._coef,i); 4376 } 4377 else if (!pair.second._coef->is_matrix() && pair.second._coef->_is_transposed && !pair.second._p->first->is_matrix()) {//transposed vect * vec, a dot product of two vectors 4378 for (size_t j = 0; j<pair.second._p->first->_dim[0]; j++) { 4379 qval += eval_coef(pair.second._coef,j) * eval(pair.second._p->first,j) * eval(pair.second._p->second,j); 4380 } 4381 } 4382 else { 4383 qval += eval_coef(pair.second._coef,i) * eval(pair.second._p->first,i) * eval(pair.second._p->second,i); 4384 } 4385 if (!pair.second._sign) { 4386 qval *= -1.; 4387 } 4388 res += qval; 4389 } 4390 } 4391 // res += eval_qterms(i); 4392 if(!_pterms->empty()){ 4393 for (auto &pair:*_pterms) { 4394 if (pair.second._coef->_is_transposed) {//transposed vect * vec, a dot product of two vectors 4395 for (size_t j = 0; j<pair.second._coef->_dim[1]; j++) { 4396 res += eval_pterm(pair.second,j); 4397 } 4398 } 4399 else { 4400 type pval = unit<type>().eval(); 4401 for (auto &vpair: *pair.second._l) { 4402 pval *= std::pow(eval(vpair.first, i), vpair.second); 4403 } 4404 pval *= eval_coef(pair.second._coef,i); 4405 if (!pair.second._sign) { 4406 pval *= -1.; 4407 } 4408 res += pval; 4409 } 4410 } 4411 // res += eval_pterms(i); 4412 } 4413 if(_expr) 4414 res += eval_expr(_expr,i); 4415 if (func_is_number()) { 4416 assert(_val->size()>0); 4417 _val->at(0) = res; 4418 _evaluated = true; 4419 } 4420 else { 4421 // if (is_constant() && i==_val->size()-1) { 4422 // if (i==_val->size()-1) { 4423 // _evaluated = true; 4424 // } 4425 assert(_val->size()>i); 4426 _val->at(i) = res; 4427 } 4428 return res; 4429 } 4430 4431 // type eval(size_t i=0) { 4432 // if(is_zero()){ 4433 // if (func_is_number()){ 4434 // assert(_val->size()>0); 4435 // _val->at(0) = this->_range->first; 4436 // return _val->at(0); 4437 // } 4438 // assert(_val->size()>i); 4439 // _val->at(i) = this->_range->first; 4440 // return _range->first; 4441 // } 4442 // // if (is_constant() && _evaluated) { 4443 // if (_evaluated) { 4444 // if (func_is_number()){ 4445 // assert(_val->size()>0); 4446 // return _val->at(0); 4447 // } 4448 // assert(_val->size()>i); 4449 // return _val->at(i); 4450 // } 4451 // type res = 0; 4452 // if(!_cst->is_zero()) 4453 // res += eval_cst(i); 4454 // if(!_lterms->empty()){ 4455 // for (auto &pair:*_lterms) { 4456 // if (pair.second._coef->_is_transposed || pair.second._coef->is_matrix() || pair.second._p->is_matrix_indexed()) { 4457 // auto dim = pair.second._p->get_dim(i); 4458 // if (pair.second._sign) { 4459 // for (size_t j = 0; j<dim; j++) { 4460 // res += eval_coef(pair.second._coef,i,j) * eval(pair.second._p,i,j); 4461 // } 4462 // } 4463 // else { 4464 // for (size_t j = 0; j<dim; j++) { 4465 // res -= eval_coef(pair.second._coef,i,j) * eval(pair.second._p,i,j); 4466 // } 4467 // } 4468 // } 4469 // else { 4470 // if (pair.second._sign) { 4471 // res += eval_coef(pair.second._coef,i) * eval(pair.second._p,i); 4472 // } 4473 // else { 4474 // res -= eval_coef(pair.second._coef,i) * eval(pair.second._p,i); 4475 // } 4476 // } 4477 // } 4478 // } 4479 // // res += eval_lterms(i); 4480 // if(!_qterms->empty()){ 4481 // for (auto &pair:*_qterms) { 4482 // type qval = 0; 4483 // if (pair.second._coef_p1_tr) { // qterm = (coef*p1)^T*p2 4484 // for (auto i = 0; i<pair.second._p->first->_dim[0]; i++) { 4485 // for (auto j = 0; j<pair.second._p->first->_dim[0]; j++) { 4486 // qval += eval_coef(pair.second._coef,i,j) * eval(pair.second._p->first,j) * eval(pair.second._p->second,i); 4487 // } 4488 // } 4489 // } 4490 // else if(pair.second._coef->_is_transposed || pair.second._p->second->is_matrix_indexed()){ 4491 // auto dim = pair.second._p->first->get_dim(i);// min? 4492 // if (pair.second._sign) { 4493 // for (size_t j = 0; j<dim; j++) { 4494 // res += eval_coef(pair.second._coef,i,j) * eval(pair.second._p->first,i,j) * eval(pair.second._p->second,i,j); 4495 // } 4496 // } 4497 // else { 4498 // for (size_t j = 0; j<dim; j++) { 4499 // res -= eval_coef(pair.second._coef,i,j) * eval(pair.second._p->first,i,j) * eval(pair.second._p->second,i,j); 4500 // } 4501 // } 4502 // } 4503 // else if (pair.second._p->first->is_matrix() && !pair.second._p->second->is_matrix() && !pair.second._p->second->_is_transposed) {//matrix * vect 4504 // for (size_t j = 0; j<pair.second._p->second->_dim[0]; j++) { 4505 // qval += eval(pair.second._p->first,i,j) * eval(pair.second._p->second,j); 4506 // } 4507 // qval *= eval_coef(pair.second._coef,i); 4508 // } 4509 // else if (!pair.second._p->first->is_matrix() && pair.second._p->first->_is_transposed && pair.second._p->second->is_matrix() ) {//transposed vect * matrix 4510 // for (size_t j = 0; j<pair.second._p->first->_dim[0]; j++) { 4511 // qval += eval(pair.second._p->first,j) * eval(pair.second._p->second,j,i); 4512 // } 4513 // qval *= eval_coef(pair.second._coef,i); 4514 // } 4515 // else if (!pair.second._p->first->is_matrix() && pair.second._p->first->_is_transposed && !pair.second._p->second->is_matrix() && i==0) {//transposed vect * vec, a dot product of two vectors 4516 // for (size_t j = 0; j<pair.second._p->first->_dim[1]; j++) { 4517 // qval += eval(pair.second._p->first,j) * eval(pair.second._p->second,j); 4518 // } 4519 // qval *= eval_coef(pair.second._coef,i); 4520 // } 4521 // else if (!pair.second._coef->is_matrix() && pair.second._coef->_is_transposed && !pair.second._p->first->is_matrix()) {//transposed vect * vec, a dot product of two vectors 4522 // for (size_t j = 0; j<pair.second._p->first->_dim[0]; j++) { 4523 // qval += eval_coef(pair.second._coef,j) * eval(pair.second._p->first,j) * eval(pair.second._p->second,j); 4524 // } 4525 // } 4526 // else { 4527 // qval += eval_coef(pair.second._coef,i) * eval(pair.second._p->first,i) * eval(pair.second._p->second,i); 4528 // } 4529 // if (!pair.second._sign) { 4530 // qval *= -1.; 4531 // } 4532 // res += qval; 4533 // } 4534 // } 4535 // // res += eval_qterms(i); 4536 // if(!_pterms->empty()){ 4537 // for (auto &pair:*_pterms) { 4538 // type pval = unit<type>().eval(); 4539 // if (pair.second._coef->_is_transposed || pair.second._l->begin()->is_matrix_indexed()) {//transposed vect * vec, a dot product of two vectors 4540 // type pval_row = unit<type>().eval(); 4541 // for (size_t i = 0; i<vpair.first->get_dim(); i++) { 4542 // for (auto &vpair: *pair.second._l) { 4543 // for (size_t j = 0; j<vpair.first->get_dim(i); j++) { 4544 // pval *= std::pow(eval(vpair.first, i,j), vpair.second); 4545 // } 4546 // } 4547 // } 4548 // } 4549 // else { 4550 // type pval = unit<type>().eval(); 4551 // for (auto &vpair: *pair.second._l) { 4552 // pval *= std::pow(eval(vpair.first, i), vpair.second); 4553 // } 4554 // } 4555 //// for (auto &vpair: *pair.second._l) { 4556 //// auto dim = vpair.first->get_dim(i);// min? 4557 //// for (size_t j = 0; j<dim; j++) { 4558 //// pval *= std::pow(eval(vpair.first, i, j), vpair.second); 4559 //// } 4560 //// } 4561 // pval *= eval_coef(pair.second._coef,i); 4562 // if (!pair.second._sign) { 4563 // pval *= -1.; 4564 // } 4565 // res += pval; 4566 // } 4567 // } 4568 // // res += eval_pterms(i); 4569 // } 4570 // if(_expr) 4571 // res += eval_expr(_expr,i); 4572 // if (func_is_number()) { 4573 // assert(_val->size()>0); 4574 // _val->at(0) = res; 4575 // _evaluated = true; 4576 // } 4577 // else { 4578 // // if (is_constant() && i==_val->size()-1) { 4579 // // if (i==_val->size()-1) { 4580 // // _evaluated = true; 4581 // // } 4582 // assert(_val->size()>i); 4583 // _val->at(i) = res; 4584 // } 4585 // return res; 4586 // } 4587 has_matrix_indexed_vars()4588 bool has_matrix_indexed_vars() const{ 4589 for (auto &vp:*_vars) { 4590 if(vp.second.first->is_matrix_indexed()) 4591 return true; 4592 } 4593 return false; 4594 } 4595 4596 template<typename T> 4597 func<type> replace(const var<T>& v, const func<T>& f) const;/**< Replace v with function f everywhere it appears */ 4598 4599 /* Get the scaling factor needed to make sure all coefficients are in [-unit,unit] */ 4600 double get_scale_factor(double unit); 4601 4602 // /* Make sure all variables' bounds are in [-unit,unit] */ 4603 // void scale_vars(double unit); 4604 4605 /* Make sure all coefficient values are in [-unit,unit] */ 4606 void scale_coefs(double unit); 4607 eval_cst(size_t i)4608 inline type eval_cst(size_t i) { 4609 return eval_coef(_cst, i); 4610 } 4611 eval_cst(size_t i,size_t j)4612 inline type eval_cst(size_t i, size_t j) { 4613 return eval_coef(_cst, i, j); 4614 } 4615 4616 template<class T=type, typename enable_if<is_same<T, Cpx>::value>::type* = nullptr> 4617 inline type get_val(const shared_ptr<constant_>& c, size_t i=0) { 4618 switch (c->get_type()) { 4619 case binary_c: 4620 return static_pointer_cast<constant<bool>>(c)->eval(); 4621 break; 4622 case short_c: 4623 return static_pointer_cast<constant<short>>(c)->eval(); 4624 break; 4625 case integer_c: 4626 return static_pointer_cast<constant<int>>(c)->eval(); 4627 break; 4628 case float_c: 4629 return static_pointer_cast<constant<float>>(c)->eval(); 4630 break; 4631 case double_c: 4632 return ((constant<double>*)(c.get()))->eval(); 4633 break; 4634 case long_c: 4635 return static_pointer_cast<constant<long double>>(c)->eval(); 4636 break; 4637 case complex_c: 4638 return static_pointer_cast<constant<Cpx>>(c)->eval(); 4639 break; 4640 case func_c:{ 4641 auto f = ((func_*)(c.get())); 4642 switch (f->get_return_type()) { 4643 case complex_: 4644 return ((func<Cpx>*)(f))->_val->at(i); 4645 break; 4646 default: 4647 break; 4648 } 4649 break; 4650 } 4651 case uexp_c:{ 4652 return eval_uexpr((uexpr<type>*)(c.get()),i); 4653 break; 4654 } 4655 case bexp_c:{ 4656 return eval_bexpr((bexpr<type>*)(c.get()),i); 4657 break; 4658 } 4659 default:{ 4660 auto p = ((param_*)(c.get())); 4661 switch (p->get_intype()) { 4662 case binary_: 4663 return ((param<bool>*)(p))->eval(i); 4664 break; 4665 case short_: 4666 return ((param<short>*)(p))->eval(i); 4667 break; 4668 case integer_: 4669 return ((param<int>*)(p))->eval(i); 4670 break; 4671 case float_: 4672 return ((param<float>*)(p))->eval(i); 4673 break; 4674 case double_: 4675 return ((param<double>*)(p))->eval(i); 4676 break; 4677 case long_: 4678 return ((param<long double>*)(p))->eval(i); 4679 break; 4680 case complex_: 4681 return ((param<Cpx>*)(p))->eval(i); 4682 break; 4683 default: 4684 break; 4685 } 4686 break; 4687 } 4688 } 4689 throw invalid_argument("Unsupported type"); 4690 } 4691 4692 template<class T=type, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> 4693 inline type get_val(const shared_ptr<constant_>& c, size_t i=0) { 4694 switch (c->get_type()) { 4695 case binary_c: 4696 return static_pointer_cast<constant<bool>>(c)->eval(); 4697 break; 4698 case short_c: 4699 return static_pointer_cast<constant<short>>(c)->eval(); 4700 break; 4701 case integer_c: 4702 return static_pointer_cast<constant<int>>(c)->eval(); 4703 break; 4704 case float_c: 4705 return static_pointer_cast<constant<float>>(c)->eval(); 4706 break; 4707 case double_c: 4708 return ((constant<double>*)(c.get()))->eval(); 4709 break; 4710 case long_c: 4711 return static_pointer_cast<constant<long double>>(c)->eval(); 4712 break; 4713 case func_c:{ 4714 auto f = ((func_*)(c.get())); 4715 switch (f->get_return_type()) { 4716 case binary_: 4717 if(f->func_is_number()){ 4718 return ((func<bool>*)(f))->_val->at(0); 4719 } 4720 return ((func<bool>*)(f))->_val->at(i); 4721 break; 4722 case short_: 4723 if(f->func_is_number()){ 4724 return ((func<short>*)(f))->_val->at(0); 4725 } 4726 return ((func<short>*)(f))->_val->at(i); 4727 break; 4728 case integer_: 4729 if(f->func_is_number()){ 4730 return ((func<int>*)(f))->_val->at(0); 4731 } 4732 return ((func<int>*)(f))->_val->at(i); 4733 break; 4734 case float_: 4735 if(f->func_is_number()){ 4736 return ((func<float>*)(f))->_val->at(0); 4737 } 4738 return ((func<float>*)(f))->_val->at(i); 4739 break; 4740 case double_: 4741 if(f->func_is_number()){ 4742 return ((func<double>*)(f))->_val->at(0); 4743 } 4744 return ((func<double>*)(f))->_val->at(i); 4745 break; 4746 case long_: 4747 if(f->func_is_number()){ 4748 return ((func<long double>*)(f))->_val->at(0); 4749 } 4750 return ((func<long double>*)(f))->_val->at(i); 4751 break; 4752 default: 4753 break; 4754 } 4755 break; 4756 } 4757 case uexp_c:{ 4758 return eval_uexpr(((uexpr<type>*)c.get()),i); 4759 break; 4760 } 4761 case bexp_c:{ 4762 return eval_bexpr(((bexpr<type>*)c.get()),i); 4763 break; 4764 } 4765 default:{ 4766 auto p = ((param_*)(c.get())); 4767 switch (p->get_intype()) { 4768 case binary_: 4769 return ((param<bool>*)(p))->eval(i); 4770 break; 4771 case short_: 4772 return ((param<short>*)(p))->eval(i); 4773 break; 4774 case integer_: 4775 return ((param<int>*)(p))->eval(i); 4776 break; 4777 case float_: 4778 return ((param<float>*)(p))->eval(i); 4779 break; 4780 case double_: 4781 return ((param<double>*)(p))->eval(i); 4782 break; 4783 case long_: 4784 return ((param<long double>*)(p))->eval(i); 4785 break; 4786 default: 4787 break; 4788 } 4789 break; 4790 } 4791 } 4792 throw invalid_argument("Unsupported type"); 4793 } 4794 4795 template<class T=type, typename enable_if<is_same<T, Cpx>::value>::type* = nullptr> get_val(const shared_ptr<constant_> & c,size_t i,size_t j)4796 inline type get_val(const shared_ptr<constant_>& c, size_t i, size_t j) { 4797 switch (c->get_type()) { 4798 case binary_c: 4799 return static_pointer_cast<constant<bool>>(c)->eval(); 4800 break; 4801 case short_c: 4802 return static_pointer_cast<constant<short>>(c)->eval(); 4803 break; 4804 case integer_c: 4805 return static_pointer_cast<constant<int>>(c)->eval(); 4806 break; 4807 case float_c: 4808 return static_pointer_cast<constant<float>>(c)->eval(); 4809 break; 4810 case double_c: 4811 return ((constant<double>*)(c.get()))->eval(); 4812 break; 4813 case long_c: 4814 return static_pointer_cast<constant<long double>>(c)->eval(); 4815 break; 4816 case complex_c: 4817 return static_pointer_cast<constant<Cpx>>(c)->eval(); 4818 break; 4819 case func_c:{ 4820 auto f = ((func_*)(c.get())); 4821 switch (f->get_return_type()) { 4822 case complex_: 4823 return ((func<Cpx>*)(f))->get_val(i,j); 4824 break; 4825 default: 4826 break; 4827 } 4828 break; 4829 } 4830 case uexp_c:{ 4831 return eval_uexpr((uexpr<type>*)(c.get()),i,j); 4832 break; 4833 } 4834 case bexp_c:{ 4835 return eval_bexpr((bexpr<type>*)(c.get()),i,j); 4836 break; 4837 } 4838 default:{ 4839 auto p = ((param_*)(c.get())); 4840 switch (p->get_intype()) { 4841 case binary_: 4842 return ((param<bool>*)(p))->eval(i,j); 4843 break; 4844 case short_: 4845 return ((param<short>*)(p))->eval(i,j); 4846 break; 4847 case integer_: 4848 return ((param<int>*)(p))->eval(i,j); 4849 break; 4850 case float_: 4851 return ((param<float>*)(p))->eval(i,j); 4852 break; 4853 case double_: 4854 return ((param<double>*)(p))->eval(i,j); 4855 break; 4856 case long_: 4857 return ((param<long double>*)(p))->eval(i,j); 4858 break; 4859 case complex_: 4860 return ((param<Cpx>*)(p))->eval(i,j); 4861 break; 4862 default: 4863 break; 4864 } 4865 break; 4866 } 4867 } 4868 throw invalid_argument("Unsupported type"); 4869 } 4870 4871 template<class T=type, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> get_val(const shared_ptr<constant_> & c,size_t i,size_t j)4872 inline type get_val(const shared_ptr<constant_>& c, size_t i, size_t j) { 4873 switch (c->get_type()) { 4874 case binary_c: 4875 return static_pointer_cast<constant<bool>>(c)->eval(); 4876 break; 4877 case short_c: 4878 return static_pointer_cast<constant<short>>(c)->eval(); 4879 break; 4880 case integer_c: 4881 return static_pointer_cast<constant<int>>(c)->eval(); 4882 break; 4883 case float_c: 4884 return static_pointer_cast<constant<float>>(c)->eval(); 4885 break; 4886 case double_c: 4887 return ((constant<double>*)(c.get()))->eval(); 4888 break; 4889 case long_c: 4890 return static_pointer_cast<constant<long double>>(c)->eval(); 4891 break; 4892 case func_c:{ 4893 auto f = ((func_*)(c.get())); 4894 switch (f->get_return_type()) { 4895 case binary_: 4896 return ((func<bool>*)(f))->get_val(i,j); 4897 break; 4898 case short_: 4899 return ((func<short>*)(f))->get_val(i,j); 4900 break; 4901 case integer_: 4902 return ((func<int>*)(f))->get_val(i,j); 4903 break; 4904 case float_: 4905 return ((func<float>*)(f))->get_val(i,j); 4906 break; 4907 case double_: 4908 return ((func<double>*)(f))->get_val(i,j); 4909 break; 4910 case long_: 4911 return ((func<long double>*)(f))->get_val(i,j); 4912 break; 4913 default: 4914 break; 4915 } 4916 break; 4917 } 4918 case uexp_c:{ 4919 return eval_uexpr(((uexpr<type>*)c.get()),i,j); 4920 break; 4921 } 4922 case bexp_c:{ 4923 return eval_bexpr(((bexpr<type>*)c.get()),i,j); 4924 break; 4925 } 4926 default:{ 4927 auto p = ((param_*)(c.get())); 4928 switch (p->get_intype()) { 4929 case binary_: 4930 return ((param<bool>*)(p))->eval(i,j); 4931 break; 4932 case short_: 4933 return ((param<short>*)(p))->eval(i,j); 4934 break; 4935 case integer_: 4936 return ((param<int>*)(p))->eval(i,j); 4937 break; 4938 case float_: 4939 return ((param<float>*)(p))->eval(i,j); 4940 break; 4941 case double_: 4942 return ((param<double>*)(p))->eval(i,j); 4943 break; 4944 case long_: 4945 return ((param<long double>*)(p))->eval(i,j); 4946 break; 4947 default: 4948 break; 4949 } 4950 break; 4951 } 4952 } 4953 throw invalid_argument("Unsupported type"); 4954 } 4955 4956 template<class T=type, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> 4957 inline type eval(const shared_ptr<constant_>& c, size_t i=0) { 4958 switch (c->get_type()) { 4959 case binary_c: 4960 return static_pointer_cast<constant<bool>>(c)->eval(); 4961 break; 4962 case short_c: 4963 return static_pointer_cast<constant<short>>(c)->eval(); 4964 break; 4965 case integer_c: 4966 return static_pointer_cast<constant<int>>(c)->eval(); 4967 break; 4968 case float_c: 4969 return static_pointer_cast<constant<float>>(c)->eval(); 4970 break; 4971 case double_c: 4972 return ((constant<double>*)(c.get()))->eval(); 4973 break; 4974 case long_c: 4975 return static_pointer_cast<constant<long double>>(c)->eval(); 4976 break; 4977 case func_c:{ 4978 auto f = ((func_*)(c.get())); 4979 switch (f->get_return_type()) { 4980 case binary_: 4981 return ((func<bool>*)(f))->eval(i); 4982 break; 4983 case short_: 4984 return ((func<short>*)(f))->eval(i); 4985 break; 4986 case integer_: 4987 return ((func<int>*)(f))->eval(i); 4988 break; 4989 case float_: 4990 return ((func<float>*)(f))->eval(i); 4991 break; 4992 case double_: 4993 return ((func<double>*)(f))->eval(i); 4994 break; 4995 case long_: 4996 return ((func<long double>*)(f))->eval(i); 4997 break; 4998 default: 4999 break; 5000 } 5001 break; 5002 } 5003 case uexp_c:{ 5004 return eval_uexpr(((uexpr<type>*)c.get()),i); 5005 break; 5006 } 5007 case bexp_c:{ 5008 return eval_bexpr(((bexpr<type>*)c.get()),i); 5009 break; 5010 } 5011 default:{ 5012 auto p = ((param_*)(c.get())); 5013 switch (p->get_intype()) { 5014 case binary_: 5015 return ((param<bool>*)(p))->eval(i); 5016 break; 5017 case short_: 5018 return ((param<short>*)(p))->eval(i); 5019 break; 5020 case integer_: 5021 return ((param<int>*)(p))->eval(i); 5022 break; 5023 case float_: 5024 return ((param<float>*)(p))->eval(i); 5025 break; 5026 case double_: 5027 return ((param<double>*)(p))->eval(i); 5028 break; 5029 case long_: 5030 return ((param<long double>*)(p))->eval(i); 5031 break; 5032 default: 5033 break; 5034 } 5035 break; 5036 } 5037 } 5038 throw invalid_argument("Unsupported type"); 5039 } 5040 5041 5042 template<class T=type, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> eval(const shared_ptr<constant_> & c,size_t i,size_t j)5043 inline type eval(const shared_ptr<constant_>& c, size_t i, size_t j) { 5044 switch (c->get_type()) { 5045 case binary_c: 5046 return static_pointer_cast<constant<bool>>(c)->eval(); 5047 break; 5048 case short_c: 5049 return static_pointer_cast<constant<short>>(c)->eval(); 5050 break; 5051 case integer_c: 5052 return static_pointer_cast<constant<int>>(c)->eval(); 5053 break; 5054 case float_c: 5055 return static_pointer_cast<constant<float>>(c)->eval(); 5056 break; 5057 case double_c: 5058 return ((constant<double>*)(c.get()))->eval(); 5059 break; 5060 case long_c: 5061 return static_pointer_cast<constant<long double>>(c)->eval(); 5062 break; 5063 case func_c:{ 5064 auto f = ((func_*)(c.get())); 5065 switch (f->get_return_type()) { 5066 case binary_: 5067 return ((func<bool>*)(f))->eval(i,j); 5068 break; 5069 case short_: 5070 return ((func<short>*)(f))->eval(i,j); 5071 break; 5072 case integer_: 5073 return ((func<int>*)(f))->eval(i,j); 5074 break; 5075 case float_: 5076 return ((func<float>*)(f))->eval(i,j); 5077 break; 5078 case double_: 5079 return ((func<double>*)(f))->eval(i,j); 5080 break; 5081 case long_: 5082 return ((func<long double>*)(f))->eval(i,j); 5083 break; 5084 default: 5085 break; 5086 } 5087 break; 5088 } 5089 case uexp_c:{ 5090 return eval_uexpr((uexpr<type>*)(c.get()),i,j); 5091 break; 5092 } 5093 case bexp_c:{ 5094 return eval_bexpr((bexpr<type>*)(c.get()),i,j); 5095 break; 5096 } 5097 default:{ 5098 auto p = ((param_*)(c.get())); 5099 switch (p->get_intype()) { 5100 case binary_: 5101 return ((param<bool>*)(p))->eval(i,j); 5102 break; 5103 case short_: 5104 return ((param<short>*)(p))->eval(i,j); 5105 break; 5106 case integer_: 5107 return ((param<int>*)(p))->eval(i,j); 5108 break; 5109 case float_: 5110 return ((param<float>*)(p))->eval(i,j); 5111 break; 5112 case double_: 5113 return ((param<double>*)(p))->eval(i,j); 5114 break; 5115 case long_: 5116 return ((param<long double>*)(p))->eval(i,j); 5117 break; 5118 default: 5119 break; 5120 } 5121 break; 5122 } 5123 } 5124 throw invalid_argument("Unsupported type"); 5125 } 5126 5127 template<class T=type, typename enable_if<is_same<T, Cpx>::value>::type* = nullptr> 5128 inline type eval(const shared_ptr<constant_>& c, size_t i=0) { 5129 switch (c->get_type()) { 5130 case binary_c: 5131 return static_pointer_cast<constant<bool>>(c)->eval(); 5132 break; 5133 case short_c: 5134 return static_pointer_cast<constant<short>>(c)->eval(); 5135 break; 5136 case integer_c: 5137 return static_pointer_cast<constant<int>>(c)->eval(); 5138 break; 5139 case float_c: 5140 return static_pointer_cast<constant<float>>(c)->eval(); 5141 break; 5142 case double_c: 5143 return ((constant<double>*)(c.get()))->eval(); 5144 break; 5145 case long_c: 5146 return static_pointer_cast<constant<long double>>(c)->eval(); 5147 break; 5148 case complex_c: 5149 return static_pointer_cast<constant<Cpx>>(c)->eval(); 5150 break; 5151 case func_c:{ 5152 auto f = ((func_*)(c.get())); 5153 switch (f->get_return_type()) { 5154 case binary_: 5155 return ((func<bool>*)(f))->eval(i); 5156 break; 5157 case short_: 5158 return ((func<short>*)(f))->eval(i); 5159 break; 5160 case integer_: 5161 return ((func<int>*)(f))->eval(i); 5162 break; 5163 case float_: 5164 return ((func<float>*)(f))->eval(i); 5165 break; 5166 case double_: 5167 return ((func<double>*)(f))->eval(i); 5168 break; 5169 case long_: 5170 return ((func<long double>*)(f))->eval(i); 5171 break; 5172 case complex_: 5173 return ((func<Cpx>*)(f))->eval(i); 5174 break; 5175 default: 5176 break; 5177 } 5178 break; 5179 } 5180 case uexp_c:{ 5181 return eval_uexpr((uexpr<type>*)(c.get()),i); 5182 break; 5183 } 5184 case bexp_c:{ 5185 return eval_bexpr((bexpr<type>*)(c.get()),i); 5186 break; 5187 } 5188 default:{ 5189 auto p = ((param_*)(c.get())); 5190 switch (p->get_intype()) { 5191 case binary_: 5192 return ((param<bool>*)(p))->eval(i); 5193 break; 5194 case short_: 5195 return ((param<short>*)(p))->eval(i); 5196 break; 5197 case integer_: 5198 return ((param<int>*)(p))->eval(i); 5199 break; 5200 case float_: 5201 return ((param<float>*)(p))->eval(i); 5202 break; 5203 case double_: 5204 return ((param<double>*)(p))->eval(i); 5205 break; 5206 case long_: 5207 return ((param<long double>*)(p))->eval(i); 5208 break; 5209 case complex_: 5210 return ((param<Cpx>*)(p))->eval(i); 5211 break; 5212 default: 5213 break; 5214 } 5215 break; 5216 } 5217 } 5218 throw invalid_argument("Unsupported type"); 5219 } 5220 5221 template<class T=type, typename enable_if<is_same<T, Cpx>::value>::type* = nullptr> eval(const shared_ptr<constant_> & c,size_t i,size_t j)5222 inline type eval(const shared_ptr<constant_>& c, size_t i, size_t j) { 5223 switch (c->get_type()) { 5224 case binary_c: 5225 return static_pointer_cast<constant<bool>>(c)->eval(); 5226 break; 5227 case short_c: 5228 return static_pointer_cast<constant<short>>(c)->eval(); 5229 break; 5230 case integer_c: 5231 return static_pointer_cast<constant<int>>(c)->eval(); 5232 break; 5233 case float_c: 5234 return static_pointer_cast<constant<float>>(c)->eval(); 5235 break; 5236 case double_c: 5237 return ((constant<double>*)(c.get()))->eval(); 5238 break; 5239 case long_c: 5240 return static_pointer_cast<constant<long double>>(c)->eval(); 5241 break; 5242 case complex_c: 5243 return static_pointer_cast<constant<Cpx>>(c)->eval(); 5244 break; 5245 case func_c:{ 5246 auto f = ((func_*)(c.get())); 5247 switch (f->get_return_type()) { 5248 case binary_: 5249 return ((func<bool>*)(f))->eval(i,j); 5250 break; 5251 case short_: 5252 return ((func<short>*)(f))->eval(i,j); 5253 break; 5254 case integer_: 5255 return ((func<int>*)(f))->eval(i,j); 5256 break; 5257 case float_: 5258 return ((func<float>*)(f))->eval(i,j); 5259 break; 5260 case double_: 5261 return ((func<double>*)(f))->eval(i,j); 5262 break; 5263 case long_: 5264 return ((func<long double>*)(f))->eval(i,j); 5265 break; 5266 case complex_: 5267 return ((func<Cpx>*)(f))->eval(i,j); 5268 break; 5269 default: 5270 break; 5271 } 5272 break; 5273 } 5274 case uexp_c:{ 5275 return eval(static_pointer_cast<uexpr<type>>(c),i,j); 5276 break; 5277 } 5278 case bexp_c:{ 5279 return eval(static_pointer_cast<bexpr<type>>(c),i,j); 5280 break; 5281 } 5282 default:{ 5283 auto p = ((param_*)(c.get())); 5284 switch (p->get_intype()) { 5285 case binary_: 5286 return ((param<bool>*)(p))->eval(i,j); 5287 break; 5288 case short_: 5289 return ((param<short>*)(p))->eval(i,j); 5290 break; 5291 case integer_: 5292 return ((param<int>*)(p))->eval(i,j); 5293 break; 5294 case float_: 5295 return ((param<float>*)(p))->eval(i,j); 5296 break; 5297 case double_: 5298 return ((param<double>*)(p))->eval(i,j); 5299 break; 5300 case long_: 5301 return ((param<long double>*)(p))->eval(i,j); 5302 break; 5303 case complex_: 5304 return ((param<Cpx>*)(p))->eval(i,j); 5305 break; 5306 default: 5307 break; 5308 } 5309 break; 5310 } 5311 } 5312 throw invalid_argument("Unsupported type"); 5313 } 5314 eval_coef(const shared_ptr<constant_> & coef,size_t i)5315 inline type eval_coef(const shared_ptr<constant_>& coef, size_t i) { 5316 auto coef_type = coef->get_type(); 5317 if (coef_type==func_c) { 5318 auto f_cst = ((func<type>*)(coef.get())); 5319 return f_cst->eval(i); 5320 } 5321 else if(coef_type==par_c || coef_type==var_c) { 5322 auto p_cst = ((param<type>*)(coef.get())); 5323 return p_cst->eval(i); 5324 } 5325 else { 5326 auto p_cst = ((constant<type>*)(coef.get())); 5327 return p_cst->eval(); 5328 } 5329 throw invalid_argument("in function eval_coef(shared_ptr<constant_> coef, size_t i), coef should be a constant"); 5330 } 5331 eval_coef(const shared_ptr<constant_> & coef,size_t i,size_t j)5332 inline type eval_coef(const shared_ptr<constant_>& coef, size_t i, size_t j) { 5333 auto coef_type = coef->get_type(); 5334 if (coef_type==func_c) { 5335 auto f_cst = ((func<type>*)(coef.get())); 5336 // if(f_cst->_indices && f_cst->_indices->_type == matrix_){ 5337 return f_cst->eval(i,j); 5338 // } 5339 // else { 5340 // return f_cst->eval(i); 5341 // } 5342 } 5343 else if(coef_type==par_c || coef_type==var_c) { 5344 auto p_cst = ((param<type>*)(coef.get())); 5345 return p_cst->eval(i,j); 5346 } 5347 else { 5348 auto p_cst = ((constant<type>*)(coef.get())); 5349 return p_cst->eval(); 5350 } 5351 throw invalid_argument("in function eval_coef(shared_ptr<constant_> coef, size_t i), coef should be a constant"); 5352 } 5353 eval_lterm(const lterm & lt,size_t i)5354 type eval_lterm(const lterm& lt, size_t i){ 5355 type res = zero<type>().eval(); 5356 if ((lt._coef->_is_transposed || lt._coef->is_matrix() || (lt._p->is_indexed() && lt._p->_indices->_ids->size()>1)) && !lt._p->is_matrix()) { 5357 auto dim = lt._p->get_dim(i); 5358 if (lt._sign) { 5359 for (size_t j = 0; j<dim; j++) { 5360 res += eval_coef(lt._coef,i,j) * eval(lt._p,i,j); 5361 } 5362 } 5363 else { 5364 for (size_t j = 0; j<dim; j++) { 5365 res -= eval_coef(lt._coef,i,j) * eval(lt._p,i,j); 5366 } 5367 } 5368 } 5369 else { 5370 if (lt._sign) { 5371 res += eval_coef(lt._coef,i) * eval(lt._p, i); 5372 } 5373 else { 5374 res -= eval_coef(lt._coef,i) * eval(lt._p, i); 5375 } 5376 } 5377 return res; 5378 } 5379 eval_lterm(const lterm & lt,size_t i,size_t j)5380 type eval_lterm(const lterm& lt, size_t i, size_t j){ 5381 type res = zero<type>().eval(); 5382 if (lt._coef->is_matrix() && lt._p->is_matrix()) { 5383 //matrix product 5384 if(lt._sign){ 5385 for (size_t col = 0; col<lt._coef->_dim[1]; col++) { 5386 res += eval_coef(lt._coef, i,col) * eval(lt._p,col,j); 5387 } 5388 } 5389 else { 5390 for (size_t col = 0; col<lt._coef->_dim[1]; col++) { 5391 res -= eval_coef(lt._coef, i,col) * eval(lt._p,col,j); 5392 } 5393 } 5394 return res; 5395 } 5396 if (lt._coef->is_matrix() && !lt._p->is_matrix() && lt._p->_is_transposed) {//matrix * transposed vect 5397 if(lt._sign){ 5398 return eval_coef(lt._coef, i,j) * eval(lt._p,j); 5399 } 5400 else { 5401 return res -= eval_coef(lt._coef, i,j) * eval(lt._p,j); 5402 } 5403 } 5404 5405 if (!lt._coef->is_matrix() && !lt._coef->_is_transposed && lt._p->is_matrix()) {//vect * matrix 5406 if(lt._sign) { 5407 return eval_coef(lt._coef, i) * eval(lt._p,i,j); 5408 } 5409 else { 5410 return res -= eval_coef(lt._coef, i) * eval(lt._p,i,j); 5411 } 5412 } 5413 if (lt._coef->is_matrix() && lt._p->_is_vector) {//matrix*vect 5414 if(lt._sign) { 5415 return eval_coef(lt._coef, i,j) * eval(lt._p,i); 5416 } 5417 else { 5418 return res -= eval_coef(lt._coef, i,j) * eval(lt._p,i); 5419 } 5420 } 5421 if(lt._sign) { 5422 return eval_coef(lt._coef, i,j) * eval(lt._p,i,j); 5423 } 5424 else { 5425 return res -= eval_coef(lt._coef, i,j) * eval(lt._p,i,j); 5426 } 5427 } 5428 5429 eval_qterm(const qterm & qt,size_t i)5430 type eval_qterm(const qterm& qt, size_t i){ 5431 type res = zero<type>().eval(); 5432 if (qt._coef_p1_tr) { // qterm = (coef*p1)^T*p2 5433 assert(qt._p->first->_dim[1]==1 && qt._coef->_dim[0]==qt._p->second->_dim[0]); 5434 for (auto i = 0; i<qt._p->first->_dim[0]; i++) { 5435 for (auto j = 0; j<qt._p->first->_dim[0]; j++) { 5436 res += eval_coef(qt._coef,i,j) * eval(qt._p->first,i) * eval(qt._p->second,j); 5437 } 5438 } 5439 if (!qt._sign) { 5440 res *= -1; 5441 } 5442 return res; 5443 5444 } 5445 if (qt._p->first->is_matrix() && !qt._p->second->is_matrix() && !qt._p->second->_is_transposed) {//matrix * vect 5446 for (size_t j = 0; j<qt._p->second->_dim[0]; j++) { 5447 res += eval(qt._p->first,i,j) * eval(qt._p->second,j); 5448 } 5449 res *= eval_coef(qt._coef,i); 5450 } 5451 else if (!qt._p->first->is_matrix() && qt._p->first->_is_transposed && qt._p->second->is_matrix() ) {//transposed vect * matrix 5452 for (size_t j = 0; j<qt._p->first->_dim[0]; j++) { 5453 res += eval(qt._p->first,j) * eval(qt._p->second,j,i); 5454 } 5455 res *= eval_coef(qt._coef,i); 5456 } 5457 else if (!qt._p->first->is_matrix() && qt._p->first->_is_transposed && !qt._p->second->is_matrix() && i==0) {//transposed vect * vec, a dot product of two vectors 5458 for (size_t j = 0; j<qt._p->first->_dim[1]; j++) { 5459 res += eval(qt._p->first,j) * eval(qt._p->second,j); 5460 } 5461 res *= eval_coef(qt._coef,i); 5462 } 5463 else if (!qt._coef->is_matrix() && qt._coef->_is_transposed && !qt._p->first->is_matrix()) {//transposed vect * vec, a dot product of two vectors 5464 for (size_t j = 0; j<qt._p->first->_dim[0]; j++) { 5465 res += eval_coef(qt._coef,j) * eval(qt._p->first,j) * eval(qt._p->second,j); 5466 } 5467 } 5468 else { 5469 res = eval_coef(qt._coef,i) * eval(qt._p->first,i) * eval(qt._p->second,i); 5470 } 5471 if (!qt._sign) { 5472 res *= -1; 5473 } 5474 return res; 5475 } 5476 eval_qterm(const qterm & qt,size_t i,size_t j)5477 type eval_qterm(const qterm& qt, size_t i, size_t j){ 5478 type res = zero<type>().eval(); 5479 if (qt._p->first->is_matrix() && qt._p->second->is_matrix()) { 5480 //matrix product 5481 for (size_t col = 0; col<qt._p->first->_dim[1]; col++) { 5482 res += eval(qt._p->first,i,col) * eval(qt._p->second,col,j); 5483 } 5484 res *= eval_coef(qt._coef,i); 5485 } 5486 else if (qt._p->first->is_matrix() && !qt._p->second->is_matrix() && qt._p->second->_is_transposed) {//matrix * transposed vect 5487 res = eval_coef(qt._coef,i) * eval(qt._p->first,i,j) * eval(qt._p->second,j); 5488 } 5489 else if (!qt._p->first->is_matrix() && !qt._p->first->_is_transposed && qt._p->second->is_matrix() ) {//vect * matrix 5490 res = eval_coef(qt._coef,i) * eval(qt._p->first,i) * eval(qt._p->second,i,j); 5491 } 5492 else { 5493 throw invalid_argument("eval(i,j) on non-matrix function"); 5494 } 5495 if (!qt._sign) { 5496 res *= -1; 5497 } 5498 return res; 5499 } 5500 eval_pterm(const pterm & pt,size_t i)5501 type eval_pterm(const pterm& pt, size_t i){ 5502 type res = zero<type>().eval(); 5503 // if (pt._coef->_is_transposed) { 5504 //// throw invalid_argument("Unspported operation\n"); 5505 // }// TREAT TRANSPOSED VECTORS IN POLYNOMIAL TERMS HERE 5506 // else { 5507 res += 1; 5508 for (auto &pair: *pt._l) { 5509 res *= pow(eval(pair.first, i), pair.second); 5510 } 5511 res *= eval_coef(pt._coef,i); 5512 // } 5513 if (!pt._sign) { 5514 res *= -1; 5515 } 5516 return res; 5517 } 5518 eval_pterm(const pterm & pt,size_t i,size_t j)5519 type eval_pterm(const pterm& pt, size_t i, size_t j){ 5520 type res = zero<type>().eval(); 5521 res += 1; 5522 for (auto &pair: *pt._l) { 5523 res *= pow(eval(pair.first,i,j), pair.second); 5524 } 5525 5526 res *= eval_coef(pt._coef,i,j); 5527 if (!pt._sign) { 5528 res *= -1; 5529 } 5530 return res; 5531 } 5532 eval_lterms(size_t i)5533 type eval_lterms(size_t i) { 5534 type res = zero<type>().eval(); 5535 auto it = _lterms->begin(); 5536 while(it!=_lterms->end()){ 5537 res += eval_lterm(it->second,i); 5538 it++; 5539 } 5540 return res; 5541 } 5542 eval_qterms(size_t i)5543 type eval_qterms(size_t i) { 5544 type res = zero<type>().eval(); 5545 auto it = _qterms->begin(); 5546 while(it!=_qterms->end()){ 5547 res += eval_qterm(it->second,i); 5548 it++; 5549 } 5550 return res; 5551 } 5552 eval_pterms(size_t i)5553 type eval_pterms(size_t i) { 5554 type res = zero<type>().eval(); 5555 auto it = _pterms->begin(); 5556 while(it!=_pterms->end()){ 5557 res += eval_pterm(it->second,i); 5558 it++; 5559 } 5560 return res; 5561 } 5562 is_evaluated()5563 bool is_evaluated() const{ 5564 return _evaluated; 5565 } 5566 evaluate(bool v)5567 void evaluate(bool v){ 5568 _evaluated = v; 5569 } 5570 eval_expr(const shared_ptr<expr<type>> & exp,size_t i)5571 inline type eval_expr(const shared_ptr<expr<type>>& exp, size_t i) { 5572 if (exp->is_uexpr()) { 5573 return eval_uexpr(((uexpr<type>*)exp.get()),i); 5574 } 5575 return eval_bexpr(((bexpr<type>*)exp.get()),i); 5576 } 5577 eval_expr(const shared_ptr<expr<type>> & exp,size_t i,size_t j)5578 inline type eval_expr(const shared_ptr<expr<type>>& exp, size_t i, size_t j) { 5579 if (exp->is_uexpr()) { 5580 return eval_uexpr(((uexpr<type>*)exp.get()),i, j); 5581 } 5582 return eval_bexpr(((bexpr<type>*)exp.get()),i,j); 5583 } 5584 5585 template<class T=type, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> eval_uexpr(uexpr<type> * exp,size_t i)5586 inline T eval_uexpr(uexpr<type>* exp, size_t i) { 5587 if (exp->_son->is_constant() && !exp->_son->is_evaluated()) { 5588 exp->_son->eval_all(); 5589 } 5590 T res = get_val(exp->_son,i); 5591 switch (exp->_otype) { 5592 case cos_: 5593 return exp->_coef*std::cos(res); 5594 break; 5595 case sin_: 5596 return exp->_coef*std::sin(res); 5597 break; 5598 case tan_: 5599 return exp->_coef*std::tan(res); 5600 break; 5601 case atan_: 5602 return exp->_coef*std::atan(res); 5603 break; 5604 case acos_: 5605 return exp->_coef*std::acos(res); 5606 break; 5607 case asin_: 5608 return exp->_coef*std::asin(res); 5609 break; 5610 case sqrt_: 5611 return exp->_coef*std::sqrt(res); 5612 break; 5613 case log_: 5614 return exp->_coef*std::log(res); 5615 break; 5616 case exp_: 5617 return exp->_coef*std::exp(res); 5618 break; 5619 case abs_:{ 5620 return exp->_coef*std::abs(res); 5621 } 5622 case relu_:{ 5623 if(res < 0) 5624 res = 0; 5625 return exp->_coef*res; 5626 } 5627 case df_abs_:{ 5628 if(res == 0) 5629 return 0; 5630 if(res < 0) 5631 return -1*exp->_coef; 5632 return exp->_coef; 5633 } 5634 case unit_step_:{ 5635 if(res <= 0) 5636 return 0; 5637 return exp->_coef; 5638 } 5639 break; 5640 default: 5641 throw invalid_argument("Unsupported unary operator"); 5642 break; 5643 } 5644 } 5645 template<class T=type, typename enable_if<is_same<T, Cpx>::value>::type* = nullptr> eval_uexpr(uexpr<T> * exp,size_t i)5646 Cpx eval_uexpr(uexpr<T>* exp, size_t i) { 5647 if (exp->_son->is_constant() && !exp->_son->is_evaluated()) { 5648 exp->_son->eval_all(); 5649 } 5650 Cpx res = get_val(exp->_son,i); 5651 switch (exp->_otype) { 5652 case cos_: 5653 return exp->_coef*std::cos(res); 5654 break; 5655 case sin_: 5656 return exp->_coef*std::sin(res); 5657 break; 5658 case tan_: 5659 return exp->_coef*std::tan(res); 5660 break; 5661 case atan_: 5662 return exp->_coef*std::atan(res); 5663 break; 5664 case acos_: 5665 return exp->_coef*std::acos(res); 5666 break; 5667 case asin_: 5668 return exp->_coef*std::asin(res); 5669 break; 5670 case sqrt_: 5671 return exp->_coef*std::sqrt(res); 5672 break; 5673 case log_: 5674 return exp->_coef*std::log(res); 5675 break; 5676 case exp_: 5677 return exp->_coef*std::exp(res); 5678 break; 5679 case abs_:{ 5680 return exp->_coef*std::abs(res); 5681 } 5682 case relu_:{ 5683 if(res.real() < 0) 5684 res.real(0); 5685 if(res.imag() < 0) 5686 res.imag(0); 5687 return exp->_coef*res; 5688 } 5689 case df_abs_:{ 5690 if(res.real() == 0 && res.imag() == 0) 5691 return Cpx(0,0); 5692 if(res.real() <= 0 || res.imag() <= 0) 5693 return Cpx(-1,0)*exp->_coef; 5694 return exp->_coef*Cpx(1,0); 5695 } 5696 case unit_step_:{ 5697 if(res.real() <= 0 || res.imag() <= 0) 5698 return Cpx(0,0); 5699 return exp->_coef*Cpx(1,0); 5700 } 5701 break; 5702 default: 5703 throw invalid_argument("Unsupported unary operator"); 5704 break; 5705 } 5706 } 5707 5708 template<class T=type, typename enable_if<is_same<T, Cpx>::value>::type* = nullptr> eval_bexpr(bexpr<type> * exp,size_t i)5709 inline T eval_bexpr(bexpr<type>* exp, size_t i){ 5710 if (exp->_lson->is_constant() && !exp->_lson->is_evaluated()) { 5711 exp->_lson->eval_all(); 5712 } 5713 if (exp->_rson->is_constant() && !exp->_rson->is_evaluated()) { 5714 exp->_rson->eval_all(); 5715 } 5716 if(exp->_otype==product_ && (exp->_lson->is_matrix_indexed() || exp->_rson->is_matrix_indexed())) 5717 { 5718 auto dim = exp->_lson->get_dim(i); 5719 if(exp->_rson->is_matrix_indexed()){ 5720 dim = exp->_rson->get_dim(i); 5721 } 5722 if(dim==0){ 5723 return 0; 5724 } 5725 T res = 0.; 5726 for (auto idx = 0; idx <dim; idx++) { 5727 T lval = get_val(exp->_lson,i,idx); 5728 T rval = get_val(exp->_rson,i,idx); 5729 res += exp->_coef*(lval*rval); 5730 } 5731 return res; 5732 } 5733 T lval = get_val(exp->_lson,i); 5734 T rval = get_val(exp->_rson,i); 5735 switch (exp->_otype) { 5736 case plus_: 5737 return exp->_coef*(lval + rval); 5738 break; 5739 case minus_: 5740 return exp->_coef*(lval - rval); 5741 break; 5742 case product_: 5743 return exp->_coef*(lval*rval); 5744 break; 5745 case div_: 5746 return exp->_coef*(lval/rval); 5747 break; 5748 case min_: 5749 return exp->_coef*(gravity::min(lval,rval)); 5750 break; 5751 case max_: 5752 return exp->_coef*(gravity::max(lval,rval)); 5753 break; 5754 default: 5755 throw invalid_argument("Unsupported binary operator"); 5756 break; 5757 } 5758 5759 } 5760 5761 template<class T=type, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> eval_bexpr(bexpr<type> * exp,size_t i)5762 inline T eval_bexpr(bexpr<type>* exp, size_t i){ 5763 if (exp->_lson->is_constant() && !exp->_lson->is_evaluated()) { 5764 exp->_lson->eval_all(); 5765 } 5766 if (exp->_rson->is_constant() && !exp->_rson->is_evaluated()) { 5767 exp->_rson->eval_all(); 5768 } 5769 if(exp->_otype==product_ && (exp->_lson->is_matrix_indexed() || exp->_rson->is_matrix_indexed())) 5770 { 5771 auto dim = exp->_lson->get_dim(i); 5772 if(exp->_rson->is_matrix_indexed()){ 5773 dim = exp->_rson->get_dim(i); 5774 } 5775 if(dim==0){ 5776 return 0; 5777 } 5778 T res = 0.; 5779 for (auto idx = 0; idx <dim; idx++) { 5780 T lval = get_val(exp->_lson,i,idx); 5781 T rval = get_val(exp->_rson,i,idx); 5782 res += exp->_coef*(lval*rval); 5783 } 5784 return res; 5785 } 5786 T lval = get_val(exp->_lson,i); 5787 T rval = get_val(exp->_rson,i); 5788 switch (exp->_otype) { 5789 case plus_: 5790 return exp->_coef*(lval + rval); 5791 break; 5792 case minus_: 5793 return exp->_coef*(lval - rval); 5794 break; 5795 case product_: 5796 return exp->_coef*(lval*rval); 5797 break; 5798 case div_: 5799 return exp->_coef*(lval/rval); 5800 break; 5801 case power_: 5802 return exp->_coef*(powl(lval,rval)); 5803 break; 5804 case min_: 5805 return exp->_coef*(gravity::min(lval,rval)); 5806 break; 5807 case max_: 5808 return exp->_coef*(gravity::max(lval,rval)); 5809 break; 5810 default: 5811 throw invalid_argument("Unsupported binary operator"); 5812 break; 5813 } 5814 5815 } 5816 5817 5818 template<class T=type, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> eval_uexpr(const uexpr<type> * exp,size_t i,size_t j)5819 T eval_uexpr(const uexpr<type>* exp, size_t i, size_t j) { 5820 T res = eval(exp->_son,i,j); 5821 switch (exp->_otype) { 5822 case cos_: 5823 return exp->_coef*std::cos(res); 5824 break; 5825 case sin_: 5826 return exp->_coef*std::sin(res); 5827 break; 5828 case acos_: 5829 return exp->_coef*std::acos(res); 5830 break; 5831 case asin_: 5832 return exp->_coef*std::asin(res); 5833 break; 5834 case sqrt_: 5835 return exp->_coef*std::sqrt(res); 5836 break; 5837 case log_: 5838 return exp->_coef*std::log(res); 5839 break; 5840 case exp_: 5841 return exp->_coef*std::exp(res); 5842 break; 5843 case abs_:{ 5844 return exp->_coef*std::abs(res); 5845 } 5846 case relu_:{ 5847 if(res < 0) 5848 res = 0; 5849 return exp->_coef*res; 5850 } 5851 case df_abs_:{ 5852 if(res == 0) 5853 return 0; 5854 if(res < 0) 5855 return -1*exp->_coef; 5856 return exp->_coef; 5857 } 5858 case unit_step_:{ 5859 if(res <= 0) 5860 return 0; 5861 return exp->_coef; 5862 } 5863 break; 5864 default: 5865 throw invalid_argument("Unsupported unary operator"); 5866 break; 5867 } 5868 } 5869 template<class T=type, typename enable_if<is_same<T, Cpx>::value>::type* = nullptr> eval_uexpr(const uexpr<type> * exp,size_t i,size_t j)5870 Cpx eval_uexpr(const uexpr<type>* exp, size_t i, size_t j) { 5871 Cpx res = eval(exp->_son,i,j); 5872 switch (exp->_otype) { 5873 case cos_: 5874 return exp->_coef*std::cos(res); 5875 break; 5876 case sin_: 5877 return exp->_coef*std::sin(res); 5878 break; 5879 case acos_: 5880 return exp->_coef*std::acos(res); 5881 break; 5882 case asin_: 5883 return exp->_coef*std::asin(res); 5884 break; 5885 case sqrt_: 5886 return exp->_coef*std::sqrt(res); 5887 break; 5888 case log_: 5889 return exp->_coef*std::log(res); 5890 break; 5891 case exp_: 5892 return exp->_coef*std::exp(res); 5893 break; 5894 case abs_:{ 5895 return exp->_coef*std::abs(res); 5896 } 5897 case relu_:{ 5898 if(res.real() < 0) 5899 res.real(0); 5900 if(res.imag() < 0) 5901 res.imag(0); 5902 return exp->_coef*res; 5903 } 5904 case df_abs_:{ 5905 if(res.real() == 0 && res.imag() == 0) 5906 return Cpx(0,0); 5907 if(res.real() <= 0 || res.imag() <= 0) 5908 return Cpx(-1,0)*exp->_coef; 5909 return exp->_coef*Cpx(1,0); 5910 } 5911 case unit_step_:{ 5912 if(res.real() <= 0 || res.imag() <= 0) 5913 return Cpx(0,0); 5914 return exp->_coef*Cpx(1,0); 5915 } 5916 default: 5917 throw invalid_argument("Unsupported unary operator"); 5918 break; 5919 } 5920 } 5921 5922 template<class T=type, typename enable_if<is_same<T, Cpx>::value>::type* = nullptr> eval_bexpr(const bexpr<type> * exp,size_t i,size_t j)5923 T eval_bexpr(const bexpr<type>* exp, size_t i, size_t j){ 5924 if (exp->_lson->is_constant() && !exp->_lson->is_evaluated()) { 5925 exp->_lson->eval_all(); 5926 } 5927 if (exp->_rson->is_constant() && !exp->_rson->is_evaluated()) { 5928 exp->_rson->eval_all(); 5929 } 5930 T lval = eval(exp->_lson,i,j); 5931 T rval = eval(exp->_rson,i,j); 5932 switch (exp->_otype) { 5933 case plus_: 5934 return exp->_coef*(lval + rval); 5935 break; 5936 case minus_: 5937 return exp->_coef*(lval - rval); 5938 break; 5939 case product_: 5940 return exp->_coef*(lval*rval); 5941 break; 5942 case div_: 5943 return exp->_coef*(lval/rval); 5944 break; 5945 case min_: 5946 return exp->_coef*(gravity::min(lval,rval)); 5947 break; 5948 case max_: 5949 return exp->_coef*(gravity::max(lval,rval)); 5950 break; 5951 default: 5952 throw invalid_argument("Unsupported binary operator"); 5953 break; 5954 } 5955 5956 } 5957 5958 template<class T=type, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> eval_bexpr(const bexpr<type> * exp,size_t i,size_t j)5959 T eval_bexpr(const bexpr<type>* exp, size_t i, size_t j){ 5960 if (exp->_lson->is_constant() && !exp->_lson->is_evaluated()) { 5961 exp->_lson->eval_all(); 5962 } 5963 if (exp->_rson->is_constant() && !exp->_rson->is_evaluated()) { 5964 exp->_rson->eval_all(); 5965 } 5966 T lval = eval(exp->_lson,i,j); 5967 T rval = eval(exp->_rson,i,j); 5968 switch (exp->_otype) { 5969 case plus_: 5970 return exp->_coef*(lval + rval); 5971 break; 5972 case minus_: 5973 return exp->_coef*(lval - rval); 5974 break; 5975 case product_: 5976 return exp->_coef*(lval*rval); 5977 break; 5978 case div_: 5979 return exp->_coef*(lval/rval); 5980 break; 5981 case power_: 5982 return exp->_coef*(powl(lval,rval)); 5983 break; 5984 case min_: 5985 return exp->_coef*(gravity::min(lval,rval)); 5986 break; 5987 case max_: 5988 return exp->_coef*(gravity::max(lval,rval)); 5989 break; 5990 default: 5991 throw invalid_argument("Unsupported binary operator"); 5992 break; 5993 } 5994 5995 } 5996 5997 eval(const string & key)5998 type eval(const string& key) { 5999 return _val->at(_indices->_keys_map->at(key)); 6000 } 6001 eval(size_t i,size_t j)6002 type eval(size_t i, size_t j){ 6003 if(is_zero()){ 6004 return 0.; 6005 } 6006 // if (is_constant() && _evaluated) { 6007 if (_evaluated) { 6008 if (func_is_number()){ 6009 return _val->at(0); 6010 } 6011 if (is_indexed() && _indices->_ids->size()>1) { 6012 if (_indices->_ids->at(i).at(j) >= _val->size()) { 6013 throw invalid_argument("eval(i,j): out of range"); 6014 } 6015 return _val->at(_indices->_ids->at(i).at(j)); 6016 } 6017 if (!is_matrix()) { 6018 return eval(j); 6019 } 6020 if (_is_transposed) { 6021 return _val->at(j*_dim[0]+i); 6022 } 6023 return _val->at(i*_dim[1]+j); 6024 } 6025 type res = 0; 6026 if(!_cst->is_zero()) 6027 res += eval_cst(i,j); 6028 if(!_lterms->empty()){ 6029 for (auto &pair:*_lterms) { 6030 if ((pair.second._coef->_is_transposed || pair.second._coef->_is_transposed || pair.second._coef->is_matrix()) && !pair.second._p->is_matrix()) { 6031 auto dim = pair.second._p->get_dim(i); 6032 if (pair.second._sign) { 6033 for (size_t j = 0; j<dim; j++) { 6034 res += eval_coef(pair.second._coef,i,j) * eval(pair.second._p,i,j); 6035 } 6036 } 6037 else { 6038 for (size_t j = 0; j<dim; j++) { 6039 res -= eval_coef(pair.second._coef,i,j) * eval(pair.second._p,i,j); 6040 } 6041 } 6042 } 6043 else { 6044 if (pair.second._sign) { 6045 res += eval_coef(pair.second._coef,i,j) * eval(pair.second._p,i,j); 6046 } 6047 else { 6048 res -= eval_coef(pair.second._coef,i,j) * eval(pair.second._p,i,j); 6049 } 6050 } 6051 } 6052 } 6053 // res += eval_lterms(i); 6054 if(!_qterms->empty()){ 6055 for (auto &pair:*_qterms) { 6056 type qval = 0; 6057 if (pair.second._coef_p1_tr) { // qterm = (coef*p1)^T*p2 6058 assert(pair.second._p->first->_dim[1]==1 && pair.second._coef->_dim[0]==pair.second._p->second->_dim[0]); 6059 for (auto i = 0; i<pair.second._p->first->_dim[0]; i++) { 6060 for (auto j = 0; j<pair.second._p->first->_dim[0]; j++) { 6061 qval += eval_coef(pair.second._coef,i,j) * eval(pair.second._p->first,i) * eval(pair.second._p->second,j); 6062 } 6063 } 6064 } 6065 else if (pair.second._p->first->is_matrix() && !pair.second._p->second->is_matrix() && !pair.second._p->second->_is_transposed) {//matrix * vect 6066 for (size_t j = 0; j<pair.second._p->second->_dim[0]; j++) { 6067 qval += eval(pair.second._p->first,i,j) * eval(pair.second._p->second,j); 6068 } 6069 qval *= eval_coef(pair.second._coef,i); 6070 } 6071 else if (!pair.second._p->first->is_matrix() && pair.second._p->first->_is_transposed && pair.second._p->second->is_matrix() ) {//transposed vect * matrix 6072 for (size_t j = 0; j<pair.second._p->first->_dim[0]; j++) { 6073 qval += eval(pair.second._p->first,j) * eval(pair.second._p->second,j,i); 6074 } 6075 qval *= eval_coef(pair.second._coef,i); 6076 } 6077 else if (!pair.second._p->first->is_matrix() && pair.second._p->first->_is_transposed && !pair.second._p->second->is_matrix() && i==0) {//transposed vect * vec, a dot product of two vectors 6078 for (size_t j = 0; j<pair.second._p->first->_dim[1]; j++) { 6079 qval += eval(pair.second._p->first,j) * eval(pair.second._p->second,j); 6080 } 6081 qval *= eval_coef(pair.second._coef,i); 6082 } 6083 else if (!pair.second._coef->is_matrix() && pair.second._coef->_is_transposed && !pair.second._p->first->is_matrix()) {//transposed vect * vec, a dot product of two vectors 6084 for (size_t j = 0; j<pair.second._p->first->_dim[0]; j++) { 6085 qval += eval_coef(pair.second._coef,j) * eval(pair.second._p->first,j) * eval(pair.second._p->second,j); 6086 } 6087 } 6088 else { 6089 qval += eval_coef(pair.second._coef,i,j) * eval(pair.second._p->first,i,j) * eval(pair.second._p->second,i,j); 6090 } 6091 if (!pair.second._sign) { 6092 qval *= -1.; 6093 } 6094 res += qval; 6095 } 6096 } 6097 // res += eval_qterms(i); 6098 if(!_pterms->empty()){ 6099 for (auto &pair:*_pterms) { 6100 type pval = unit<type>().eval(); 6101 for (auto &vpair: *pair.second._l) { 6102 pval *= std::pow(eval(vpair.first, i,j), vpair.second); 6103 } 6104 pval *= eval_coef(pair.second._coef,i,j); 6105 if (!pair.second._sign) { 6106 pval *= -1.; 6107 } 6108 res += pval; 6109 } 6110 // res += eval_pterms(i); 6111 } 6112 if(_expr) 6113 res += eval_expr(_expr,i,j); 6114 if (func_is_number()) { 6115 _val->at(0) = res; 6116 _evaluated = true; 6117 } 6118 else { 6119 // if (is_constant() && i==_val->size()-1) { 6120 if(is_matrix_indexed()){ 6121 _val->at(_indices->_ids->at(i).at(j)) = res; 6122 } 6123 else{ 6124 if (_is_transposed) { 6125 if(j*_dim[0]+i>=_val->size()){ 6126 throw invalid_argument("out of range assignment in eval"); 6127 } 6128 _val->at(j*_dim[0]+i) = res; 6129 // if (j*_dim[0]+i==_val->size()-1) { 6130 // _evaluated = true; 6131 // } 6132 } 6133 else { 6134 if(i*_dim[1]+j>=_val->size()){ 6135 throw invalid_argument("out of range assignment in eval"); 6136 } 6137 _val->at(i*_dim[1]+j) = res; 6138 // if (i*_dim[1]+j==_val->size()-1) { 6139 // _evaluated = true; 6140 // } 6141 } 6142 } 6143 } 6144 return res; 6145 } 6146 6147 // type eval(size_t i, size_t j) { 6148 // 6149 // 6150 // if (is_indexed() && _indices->_ids->size()>1) { 6151 // if (_indices->_ids->at(i).at(j) >= _val->size()) { 6152 // throw invalid_argument("eval(i,j): out of range"); 6153 // } 6154 // return _val->at(_indices->_ids->at(i).at(j)); 6155 // } 6156 // 6157 // if (!is_matrix()) { 6158 // return eval(j); 6159 // } 6160 // if (_is_transposed) { 6161 // return _val->at(j*_dim[0]+i); 6162 // } 6163 // return _val->at(i*_dim[1]+j); 6164 // } 6165 is_unit()6166 template<class T=type, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> bool is_unit() const { /**< Returns true if all values of this paramter are 1 **/ 6167 return (!_is_vector && !_is_transposed && func_is_number() && _range->first == 1 && _range->second == 1); 6168 // return (_range->first == 1 && _range->second == 1); 6169 } 6170 is_unit()6171 template<class T=type, typename enable_if<is_same<T, Cpx>::value>::type* = nullptr> bool is_unit() const{ 6172 // return (func_is_number() && _range->first == Cpx(1,0) && _range->second == Cpx(1,0)); 6173 return (!_is_vector && _range->first == Cpx(1,0) && _range->second == Cpx(1,0)); 6174 } 6175 is_zero()6176 inline bool is_zero() const { return zero_range();}; 6177 zero_range()6178 template<class T=type, typename enable_if<is_same<T, Cpx>::value>::type* = nullptr> bool zero_range() const{ 6179 // return (func_is_number() && _range->first == Cpx(0,0) && _range->second == Cpx(0,0)); 6180 return (get_dim()==0 || (_range->first == Cpx(0,0) && _range->second == Cpx(0,0))); 6181 } 6182 6183 template<typename T=type, zero_range()6184 typename enable_if<is_arithmetic<T>::value>::type* = nullptr> inline bool zero_range() const{ 6185 // return (get_dim()==0 || (_range->first == 0 && _range->second == 0)); 6186 return (get_dim()==0 || (func_is_number() && _range->first == 0 && _range->second == 0)); 6187 } 6188 6189 6190 6191 is_non_positive()6192 bool is_non_positive() const { /**< Returns true if all values of this paramter are <= 0 **/ 6193 auto sgn = get_all_sign(); 6194 return (sgn==non_pos_ || sgn==zero_ || sgn==neg_); 6195 } 6196 is_positive()6197 bool is_positive() const { /**< Returns true if all values of this paramter are positive **/ 6198 return (get_all_sign()==pos_); 6199 } 6200 is_non_negative()6201 bool is_non_negative() const { /**< Returns true if all values of this paramter are >= 0 **/ 6202 auto sgn = get_all_sign(); 6203 return (sgn==non_neg_ || sgn==zero_ || sgn==pos_); 6204 } 6205 is_negative()6206 bool is_negative() const { /**< Returns true if all values of this paramter are positive **/ 6207 return (get_all_sign()==neg_); 6208 } 6209 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 6210 func& operator*=(const T2 c){ 6211 return *this *= func<type>(c); 6212 } 6213 6214 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 6215 func& operator*=(const constant<T2>& c){ 6216 return *this *= func<type>(c); 6217 } 6218 6219 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 6220 func& operator*=(const param<T2>& p){ 6221 return *this *= func<type>(p); 6222 } 6223 6224 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 6225 func& operator/=(T2 c){ 6226 return *this /= constant<type>(c); 6227 } 6228 6229 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 6230 func& operator/=(const constant<T2>& c){ 6231 return *this *= 1./c.eval(); 6232 } 6233 6234 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 6235 func& operator/=(const param<T2>& p){ 6236 auto be = bexpr<type>(div_, make_shared<func>(*this), make_shared<param<T2>>(p)); 6237 auto range = get_div_range(_range,p._range); 6238 *this = func(be); 6239 _range = range; 6240 _evaluated = false; 6241 _all_convexity = undet_; 6242 _expr->_range->first = _range->first; 6243 _expr->_range->second = _range->second; 6244 _expr->_all_convexity = _all_convexity; 6245 _expr->_all_sign = _all_sign; 6246 return *this; 6247 } 6248 6249 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 6250 func& operator/=(const func<T2>& f){ 6251 if(!is_constant() && f.is_constant()){ 6252 if(f.func_is_number()){ 6253 return *this *= 1./f.get_val(); 6254 } 6255 return *this *= 1./f; 6256 } 6257 auto be = bexpr<type>(div_, make_shared<func>(*this), make_shared<func>(f)); 6258 auto range = get_div_range(_range,f._range); 6259 *this = func(be); 6260 _range = range; 6261 _evaluated = false; 6262 _all_convexity = undet_; 6263 _expr->_range->first = _range->first; 6264 _expr->_range->second = _range->second; 6265 _expr->_all_convexity = _all_convexity; 6266 _expr->_all_sign = _all_sign; 6267 return *this; 6268 } 6269 6270 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 6271 func& operator*=(const func<T2>& f){ 6272 if (is_zero()) { 6273 return *this; 6274 } 6275 if (f.is_zero()) { 6276 reset(); 6277 return *this; 6278 } 6279 if (is_unit()) { 6280 *this = func(f); 6281 return *this; 6282 } 6283 if (f.is_unit()) { 6284 return *this; 6285 } 6286 6287 /* Case where c is a number */ 6288 // if (c.is_number()){ 6289 // return *this *= constant<T2>(c.eval()); 6290 // } 6291 /* Case where the current function is not constant and the other operand is */ 6292 if((!is_constant() && f.is_constant()) || f.func_is_number()) { 6293 bool transp = false; 6294 func fc = f; 6295 if(is_linear() && _is_transposed && f._is_vector){// Situation where (*this)^T * f is transformed into (f^T*(*this))^T 6296 fc.transpose(); 6297 this->transpose(); 6298 transp = true; 6299 } 6300 if (!_cst->is_zero()) { 6301 _cst = multiply(_cst,fc); 6302 } 6303 for (auto &pair:*_lterms) { 6304 pair.second._coef = multiply(pair.second._coef, fc); 6305 } 6306 for (auto &pair:*_qterms) { 6307 pair.second._coef = multiply(pair.second._coef, fc); 6308 } 6309 for (auto &pair:*_pterms) { 6310 pair.second._coef = multiply(pair.second._coef, fc); 6311 } 6312 if (_expr) { 6313 if(_expr->is_uexpr()){ 6314 if(fc.func_is_number()){ 6315 _expr->_coef *= fc.eval(); 6316 } 6317 else { 6318 _expr = make_shared<bexpr<type>>(bexpr<type>(product_, make_shared<func>(*static_pointer_cast<uexpr<type>>(_expr)), make_shared<func>(fc))); 6319 } 6320 } 6321 else { 6322 if(fc.func_is_number()){ 6323 _expr->_coef *= fc.eval(); 6324 } 6325 else { 6326 _expr = make_shared<bexpr<type>>(bexpr<type>(product_, make_shared<func>(*static_pointer_cast<bexpr<type>>(_expr)), make_shared<func>(fc))); 6327 } 6328 } 6329 embed(_expr); 6330 } 6331 if (fc.get_all_sign()==unknown_ && !_qterms->empty()) { 6332 _all_convexity = undet_; 6333 } 6334 6335 update_sign_multiply(fc); 6336 if(f.is_non_positive()){ 6337 reverse_convexity(); 6338 } 6339 _evaluated = false; 6340 _range = get_product_range(_range,fc._range); 6341 if(transp){ 6342 this->transpose(); 6343 _range->first = extended_mult(_range->first,(type)_dim[0]); 6344 _range->second = extended_mult(_range->second,(type)_dim[0]); 6345 } 6346 update_dot_dim(fc); 6347 return *this; 6348 } 6349 /* Case where the current function is constant and the other operand is not. */ 6350 if (func_is_number() || (is_constant() && !f.is_constant())) { 6351 auto cpy = this->copy(); 6352 func res = f; 6353 res.update_dot_dim(*this,f); 6354 if(res.get_dim()==1 && res._indices && res._indices->size()>1){ /* Vectorial product, ignore the indices of f */ 6355 res._indices = nullptr; 6356 } 6357 if(is_non_positive()){ 6358 res.reverse_convexity(); 6359 } 6360 update_sign_multiply(f); 6361 res._all_sign = _all_sign; 6362 res._range = get_product_range(_range,f._range); 6363 if(_is_transposed){ 6364 res._range->first = extended_mult(res._range->first,(type)_dim[0]); 6365 res._range->second = extended_mult(res._range->second,(type)_dim[0]); 6366 } 6367 if (!res._cst->is_zero()) { 6368 if (res._cst->is_function()) { 6369 auto f_cst = *static_pointer_cast<func<type>>(res._cst); 6370 res._cst = multiply(cpy,f_cst); 6371 } 6372 else if(res._cst->is_param()) { 6373 auto f_cst = *static_pointer_cast<param<type>>(res._cst); 6374 res._cst = multiply(cpy,f_cst); 6375 } 6376 else if(res._cst->is_number()) { 6377 auto f_cst = *static_pointer_cast<constant<type>>(res._cst); 6378 res._cst = multiply(cpy,f_cst); 6379 } 6380 6381 } 6382 for (auto &pair:*res._lterms) { 6383 if (pair.second._coef->is_function()) { 6384 auto f_cst = *static_pointer_cast<func<type>>(pair.second._coef); 6385 pair.second._coef = multiply(cpy,f_cst); 6386 } 6387 else if(pair.second._coef->is_param()) { 6388 auto f_cst = *static_pointer_cast<param<type>>(pair.second._coef); 6389 pair.second._coef = multiply(cpy,f_cst); 6390 } 6391 else if(pair.second._coef->is_number()) { 6392 auto f_cst = *static_pointer_cast<constant<type>>(pair.second._coef); 6393 pair.second._coef = multiply(cpy,f_cst); 6394 } 6395 } 6396 for (auto &pair:*res._qterms) { 6397 if (pair.second._coef->is_function()) { 6398 auto f_cst = *static_pointer_cast<func<type>>(pair.second._coef); 6399 pair.second._coef = multiply(cpy,f_cst); 6400 } 6401 else if(pair.second._coef->is_param()) { 6402 auto f_cst = *static_pointer_cast<param<type>>(pair.second._coef); 6403 pair.second._coef = multiply(cpy,f_cst); 6404 } 6405 else if(pair.second._coef->is_number()) { 6406 auto f_cst = *static_pointer_cast<constant<type>>(pair.second._coef); 6407 pair.second._coef = multiply(cpy,f_cst); 6408 } 6409 } 6410 for (auto &pair:*res._pterms) { 6411 if (pair.second._coef->is_function()) { 6412 auto f_cst = *static_pointer_cast<func<type>>(pair.second._coef); 6413 pair.second._coef = multiply(cpy,f_cst); 6414 } 6415 else if(pair.second._coef->is_param()) { 6416 auto f_cst = *static_pointer_cast<param<type>>(pair.second._coef); 6417 pair.second._coef = multiply(cpy,f_cst); 6418 } 6419 else if(pair.second._coef->is_number()) { 6420 auto f_cst = *static_pointer_cast<constant<type>>(pair.second._coef); 6421 pair.second._coef = multiply(cpy,f_cst); 6422 } 6423 } 6424 if (res._expr) { 6425 if(res._expr->is_uexpr()){ 6426 if(func_is_number()){ 6427 res._expr->_coef *= this->eval(); 6428 } 6429 else { 6430 res._expr = make_shared<bexpr<type>>(bexpr<type>(product_, make_shared<func<type>>(*this), make_shared<func>(*static_pointer_cast<uexpr<type>>(res._expr)))); 6431 } 6432 } 6433 else { 6434 if(func_is_number()){ 6435 res._expr->_coef *= this->eval(); 6436 } 6437 else { 6438 res._expr = make_shared<bexpr<type>>(bexpr<type>(product_, make_shared<func<type>>(*this), make_shared<func>(*static_pointer_cast<bexpr<type>>(res._expr)))); 6439 } 6440 } 6441 res.embed(res._expr); 6442 } 6443 *this = res; 6444 _evaluated = false; 6445 return *this; 6446 } 6447 /* Both functions are either constant or non-constant at this stage */ 6448 if (_expr || (f._expr)) { 6449 auto be = bexpr<type>(product_, make_shared<func>(*this), make_shared<func>(f)); 6450 auto res = func(be); 6451 update_dot_dim(f); 6452 update_sign_multiply(f); 6453 res._dim[0] = _dim[0]; 6454 res._dim[1] = _dim[1]; 6455 res._is_vector = _is_vector; 6456 res._is_transposed = _is_transposed; 6457 res._all_sign = _all_sign; 6458 res._range = get_product_range(_range,f._range); 6459 if(_is_transposed){ 6460 res._range->first = extended_mult(res._range->first,(type)_dim[0]); 6461 res._range->second = extended_mult(res._range->second,(type)_dim[0]); 6462 } 6463 *this = move(res); 6464 _evaluated = false; 6465 _all_convexity = undet_; 6466 return *this; 6467 } 6468 func res; 6469 /* Case where the multiplication invlolves multiplying variables/parameters together, i.e., they are both parametric or both include variables */ 6470 // if(_to_str==f._to_str){ 6471 if(false){ 6472 auto signp = get_all_sign(); 6473 if(signp==neg_ || signp==pos_){ 6474 res._all_sign = pos_; 6475 } 6476 else { 6477 res._all_sign = non_neg_; 6478 } 6479 res._range->first=zero<type>().eval(); 6480 if(is_positive()|| is_negative()){ 6481 res._range->first=extended_mult(_range->first,_range->first); 6482 } 6483 res._range->second=extended_mult(_range->second,_range->second); 6484 } 6485 else { 6486 res._range = get_product_range(_range,f._range); 6487 } 6488 if(_is_transposed){ 6489 res._range->first = extended_mult(res._range->first,(type)_dim[0]); 6490 res._range->second = extended_mult(res._range->second,(type)_dim[0]); 6491 } 6492 for (auto& t1: *_pterms) { 6493 // if (t1.second._coef->_is_transposed) {// If the coefficient in front is transposed: a^T.(polynomial function), we cannot factor the coefficients. Just create a binary expression and return it. 6494 // auto be = bexpr<type>(product_, make_shared<func>(*this), make_shared<func>(f)); 6495 // *this = func(be); 6496 // _evaluated = false; 6497 // _range = res._range; 6498 // _expr->_range->first = _range->first; 6499 // _expr->_range->second = _range->second; 6500 // _expr->_all_convexity = _all_convexity; 6501 // _expr->_all_sign = _all_sign; 6502 // return *this; 6503 // } 6504 for (auto& t2: *f._pterms) { 6505 // if (t2.second._coef->_is_transposed) {// If the coefficient in front is transposed: a^T.(polynomial function), see comment above. 6506 // auto be = bexpr<type>(product_, make_shared<func>(*this), make_shared<func>(f)); 6507 // *this = func(be); 6508 // _evaluated = false; 6509 // _range = res._range; 6510 // _expr->_range->first = _range->first; 6511 // _expr->_range->second = _range->second; 6512 // _expr->_all_convexity = _all_convexity; 6513 // _expr->_all_sign = _all_sign; 6514 // return *this; 6515 // } 6516 auto newl(*t1.second._l); 6517 for (auto& it: *t2.second._l) {// TODO check if same l 6518 newl.push_back(make_pair<>(it.first, it.second)); 6519 } 6520 if (t2.second._coef->is_function()) { 6521 auto f_cst = *static_pointer_cast<func<T2>>(t2.second._coef); 6522 auto coef = multiply(t1.second._coef, f_cst); 6523 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6524 } 6525 else if(t2.second._coef->is_param()) { 6526 auto p_cst = *static_pointer_cast<param<T2>>(t2.second._coef); 6527 auto coef = multiply(t1.second._coef, p_cst); 6528 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6529 } 6530 else if(t2.second._coef->is_number()) { 6531 auto p_cst = *static_pointer_cast<constant<T2>>(t2.second._coef); 6532 auto coef = multiply(t1.second._coef, p_cst); 6533 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6534 } 6535 } 6536 for (auto& t2: *f._qterms) { 6537 // if (t2.second._coef->_is_transposed) {// If the coefficient in front is transposed: a^T.(polynomial function), see comment above. 6538 // auto be = bexpr<type>(product_, make_shared<func>(*this), make_shared<func>(f)); 6539 // *this = func(be); 6540 // _evaluated = false; 6541 // _range = res._range; 6542 // _expr->_range->first = _range->first; 6543 // _expr->_range->second = _range->second; 6544 // _expr->_all_convexity = _all_convexity; 6545 // _expr->_all_sign = _all_sign; 6546 // return *this; 6547 // } 6548 auto newl(*t1.second._l); 6549 newl.push_back(make_pair<>((t2.second._p->first), 1)); 6550 newl.push_back(make_pair<>((t2.second._p->second), 1)); 6551 if (t2.second._coef->is_function()) { 6552 auto f_cst = *static_pointer_cast<func<T2>>(t2.second._coef); 6553 auto coef = multiply(t1.second._coef, f_cst); 6554 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6555 } 6556 else if(t2.second._coef->is_param()) { 6557 auto p_cst = *static_pointer_cast<param<T2>>(t2.second._coef); 6558 auto coef = multiply(t1.second._coef, p_cst); 6559 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6560 } 6561 else if(t2.second._coef->is_number()) { 6562 auto p_cst = *static_pointer_cast<constant<T2>>(t2.second._coef); 6563 auto coef = multiply(t1.second._coef, p_cst); 6564 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6565 } 6566 } 6567 for (auto& t2: *f._lterms) { 6568 // if (t2.second._coef->_is_transposed) {// If the coefficient in front is transposed: a^T.(polynomial function) 6569 // auto be = bexpr<type>(product_, make_shared<func>(*this), make_shared<func>(f)); 6570 // *this = func(be); 6571 // _evaluated = false; 6572 // _range = res._range; 6573 // _expr->_range->first = _range->first; 6574 // _expr->_range->second = _range->second; 6575 // _expr->_all_convexity = _all_convexity; 6576 // _expr->_all_sign = _all_sign; 6577 // return *this; 6578 // } 6579 auto newl(*t1.second._l); 6580 newl.push_back(make_pair<>((t2.second._p), 1)); 6581 if (t2.second._coef->is_function()) { 6582 auto f_cst = *static_pointer_cast<func<T2>>(t2.second._coef); 6583 auto coef = multiply(t1.second._coef, f_cst); 6584 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6585 } 6586 else if(t2.second._coef->is_param()) { 6587 auto p_cst = *static_pointer_cast<param<T2>>(t2.second._coef); 6588 auto coef = multiply(t1.second._coef, p_cst); 6589 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6590 } 6591 else if(t2.second._coef->is_number()) { 6592 auto p_cst = *static_pointer_cast<constant<T2>>(t2.second._coef); 6593 auto coef = multiply(t1.second._coef, p_cst); 6594 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6595 } 6596 } 6597 if (!f._cst->is_zero()) { 6598 auto newl(*t1.second._l); 6599 if (f._cst->is_function()) { 6600 auto f_cst = *static_pointer_cast<func<T2>>(f._cst); 6601 auto coef = multiply(t1.second._coef, f_cst); 6602 res.insert(t1.second._sign, *coef, newl); 6603 } 6604 else if(f._cst->is_param()) { 6605 auto p_cst = *static_pointer_cast<param<T2>>(f._cst); 6606 auto coef = multiply(t1.second._coef, p_cst); 6607 res.insert(t1.second._sign, *coef, newl); 6608 } 6609 else if(f._cst->is_number()) { 6610 auto p_cst = *static_pointer_cast<constant<T2>>(f._cst); 6611 auto coef = multiply(t1.second._coef, p_cst); 6612 res.insert(t1.second._sign, *coef, newl); 6613 } 6614 } 6615 } 6616 for (auto& t1: *_qterms) { 6617 // if (t1.second._coef->_is_transposed) {// If the coefficient in front is transposed: a^T.(Quadratic term) 6618 // auto be = bexpr<type>(product_, make_shared<func>(*this), make_shared<func>(f)); 6619 // *this = func(be); 6620 // _evaluated = false; 6621 // _range = res._range; 6622 // return *this; 6623 // } 6624 for (auto& t2: *f._pterms) { 6625 // if (t2.second._coef->_is_transposed) {// If the coefficient in front is transposed: a^T.(polynomial term) 6626 // auto range = get_product_range(_range,f._range); 6627 // auto be = bexpr<type>(product_, make_shared<func>(*this), make_shared<func>(f)); 6628 // *this = func(be); 6629 // _evaluated = false; 6630 // _range = res._range; 6631 // return *this; 6632 // } 6633 auto newl(*t2.second._l); 6634 newl.push_front(make_pair<>(t1.second._p->first, 1)); 6635 newl.push_front(make_pair<>(t1.second._p->second, 1)); 6636 if (t2.second._coef->is_function()) { 6637 auto f_cst = *static_pointer_cast<func<T2>>(t2.second._coef); 6638 auto coef = multiply(t1.second._coef, f_cst); 6639 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6640 } 6641 else if(t2.second._coef->is_param()) { 6642 auto p_cst = *static_pointer_cast<param<T2>>(t2.second._coef); 6643 auto coef = multiply(t1.second._coef, p_cst); 6644 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6645 } 6646 else if(t2.second._coef->is_number()) { 6647 auto p_cst = *static_pointer_cast<constant<T2>>(t2.second._coef); 6648 auto coef = multiply(t1.second._coef, p_cst); 6649 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6650 } 6651 } 6652 for (auto& t2: *f._qterms) { 6653 // if (t2.second._coef->_is_transposed) {// If the coefficient in front is transposed: a^T.(polynomial term) 6654 // auto range = get_product_range(_range,f._range); 6655 // auto be = bexpr<type>(product_, make_shared<func>(*this), make_shared<func>(f)); 6656 // *this = func(be); 6657 // _evaluated = false; 6658 // _range = res._range; 6659 // return *this; 6660 // } 6661 list<pair<shared_ptr<param_>, int>> newl; 6662 newl.push_back(make_pair<>(t1.second._p->first, 1)); 6663 newl.push_back(make_pair<>(t1.second._p->second, 1)); 6664 newl.push_back(make_pair<>(t2.second._p->first, 1)); 6665 newl.push_back(make_pair<>(t2.second._p->second, 1)); 6666 if (t2.second._coef->is_function()) { 6667 auto f_cst = *static_pointer_cast<func<T2>>(t2.second._coef); 6668 auto coef = multiply(t1.second._coef, f_cst); 6669 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6670 } 6671 else if(t2.second._coef->is_param()) { 6672 auto p_cst = *static_pointer_cast<param<T2>>(t2.second._coef); 6673 auto coef = multiply(t1.second._coef, p_cst); 6674 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6675 } 6676 else if(t2.second._coef->is_number()) { 6677 auto p_cst = *static_pointer_cast<constant<T2>>(t2.second._coef); 6678 auto coef = multiply(t1.second._coef, p_cst); 6679 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6680 } 6681 } 6682 for (auto& t2: *f._lterms) { 6683 // if (t2.second._coef->_is_transposed) {// If the coefficient in front is transposed: a^T.(polynomial term) 6684 // auto range = get_product_range(_range,f._range); 6685 // auto be = bexpr<type>(product_, make_shared<func>(*this), make_shared<func>(f)); 6686 // *this = func(be); 6687 // _evaluated = false; 6688 // _range = res._range; 6689 // return *this; 6690 // } 6691 list<pair<shared_ptr<param_>, int>> newl; 6692 newl.push_back(make_pair<>(t1.second._p->first, 1)); 6693 newl.push_back(make_pair<>(t1.second._p->second, 1)); 6694 newl.push_back(make_pair<>(t2.second._p, 1)); 6695 if (t2.second._coef->is_function()) { 6696 auto f_cst = *static_pointer_cast<func<T2>>(t2.second._coef); 6697 auto coef = multiply(t1.second._coef, f_cst); 6698 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6699 } 6700 else if(t2.second._coef->is_param()) { 6701 auto p_cst = *static_pointer_cast<param<T2>>(t2.second._coef); 6702 auto coef = multiply(t1.second._coef, p_cst); 6703 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6704 } 6705 else if(t2.second._coef->is_number()) { 6706 auto p_cst = *static_pointer_cast<constant<T2>>(t2.second._coef); 6707 auto coef = multiply(t1.second._coef, p_cst); 6708 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6709 } } 6710 if (!f._cst->is_zero()) { 6711 if (f._cst->is_function()) { 6712 auto f_cst = *static_pointer_cast<func<T2>>(f._cst); 6713 auto coef = multiply(t1.second._coef, f_cst); 6714 res.insert(t1.second._sign, *coef, *t1.second._p->first, *t1.second._p->second); 6715 } 6716 else if(f._cst->is_param()) { 6717 auto p_cst = *static_pointer_cast<param<T2>>(f._cst); 6718 auto coef = multiply(t1.second._coef, p_cst); 6719 res.insert(t1.second._sign, *coef, *t1.second._p->first, *t1.second._p->second); 6720 } 6721 else if(f._cst->is_number()) { 6722 auto p_cst = *static_pointer_cast<constant<T2>>(f._cst); 6723 auto coef = multiply(t1.second._coef, p_cst); 6724 res.insert(t1.second._sign, *coef, *t1.second._p->first, *t1.second._p->second); 6725 } 6726 } 6727 } 6728 for (auto& t1: *_lterms) { 6729 for (auto& t2: *f._pterms) { 6730 // if (t1.second._coef->_is_transposed || t2.second._coef->_is_transposed) {// If the coefficient in front is transposed: a^T.(polynomial term) 6731 // auto be = bexpr<type>(product_, make_shared<func>(*this), make_shared<func>(f)); 6732 // *this = func(be); 6733 // _evaluated = false; 6734 // _range = res._range; 6735 // return *this; 6736 // } 6737 auto newl(*t2.second._l); 6738 newl.push_front(make_pair<>((t1.second._p), 1)); 6739 if (t2.second._coef->is_function()) { 6740 auto f_cst = *static_pointer_cast<func<T2>>(t2.second._coef); 6741 auto coef = multiply(t1.second._coef, f_cst); 6742 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6743 } 6744 else if(t2.second._coef->is_param()) { 6745 auto p_cst = *static_pointer_cast<param<T2>>(t2.second._coef); 6746 auto coef = multiply(t1.second._coef, p_cst); 6747 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6748 } 6749 else if(t2.second._coef->is_number()) { 6750 auto p_cst = *static_pointer_cast<constant<T2>>(t2.second._coef); 6751 auto coef = multiply(t1.second._coef, p_cst); 6752 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6753 } 6754 } 6755 for (auto& t2: *f._qterms) { 6756 // if (t1.second._coef->_is_transposed || t2.second._coef->_is_transposed) {// If the coefficient in front is transposed: a^T.(polynomial term) 6757 // auto be = bexpr<type>(product_, make_shared<func>(*this), make_shared<func>(f)); 6758 // *this = func(be); 6759 // _evaluated = false; 6760 // _range = res._range; 6761 // return *this; 6762 // } 6763 list<pair<shared_ptr<param_>, int>> newl; 6764 newl.push_back(make_pair<>(t1.second._p, 1)); 6765 newl.push_back(make_pair<>(t2.second._p->first, 1)); 6766 newl.push_back(make_pair<>(t2.second._p->second, 1)); 6767 if (t2.second._coef->is_function()) { 6768 auto f_cst = *static_pointer_cast<func<T2>>(t2.second._coef); 6769 auto coef = multiply(t1.second._coef, f_cst); 6770 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6771 } 6772 else if(t2.second._coef->is_param()) { 6773 auto p_cst = *static_pointer_cast<param<T2>>(t2.second._coef); 6774 auto coef = multiply(t1.second._coef, p_cst); 6775 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6776 } 6777 else if(t2.second._coef->is_number()) { 6778 auto p_cst = *static_pointer_cast<constant<T2>>(t2.second._coef); 6779 auto coef = multiply(t1.second._coef, p_cst); 6780 res.insert(!(t1.second._sign^t2.second._sign), *coef, newl); 6781 } 6782 } 6783 for (auto& t2: *f._lterms) { 6784 if (t2.second._coef->is_function()) { 6785 auto f_cst = *static_pointer_cast<func<T2>>(t2.second._coef); 6786 auto coef = multiply(t1.second._coef, f_cst); 6787 auto p1 = t1.second._p; 6788 if(_is_transposed){ 6789 coef->transpose(); 6790 p1->transpose(); 6791 } 6792 res.insert(!(t1.second._sign^t2.second._sign), *coef, *p1, *t2.second._p, _is_transposed); 6793 } 6794 else if(t2.second._coef->is_param()) { 6795 auto p_cst = *static_pointer_cast<param<T2>>(t2.second._coef); 6796 auto coef = multiply(t1.second._coef, p_cst); 6797 auto p1 = t1.second._p; 6798 if(_is_transposed){ 6799 coef->transpose(); 6800 p1->transpose(); 6801 } 6802 res.insert(!(t1.second._sign^t2.second._sign), *coef, *p1, *t2.second._p, _is_transposed); 6803 } 6804 else if(t2.second._coef->is_number()) { 6805 auto p_cst = *static_pointer_cast<constant<T2>>(t2.second._coef); 6806 auto coef = multiply(t1.second._coef, p_cst); 6807 auto p1 = t1.second._p; 6808 if(_is_transposed){ 6809 coef->transpose(); 6810 p1->transpose(); 6811 } 6812 res.insert(!(t1.second._sign^t2.second._sign), *coef, *p1, *t2.second._p, _is_transposed); 6813 } 6814 } 6815 if (!f._cst->is_zero()) { 6816 if (f._cst->is_function()) { 6817 auto f_cst = *static_pointer_cast<func<T2>>(f._cst); 6818 auto coef = multiply(t1.second._coef, f_cst); 6819 res.insert(t1.second._sign, *coef, *t1.second._p); 6820 } 6821 else if(f._cst->is_param()) { 6822 auto p_cst = *static_pointer_cast<param<T2>>(f._cst); 6823 auto coef = multiply(t1.second._coef, p_cst); 6824 res.insert(t1.second._sign, *coef, *t1.second._p); 6825 } 6826 else if(f._cst->is_number()) { 6827 auto p_cst = *static_pointer_cast<constant<T2>>(f._cst); 6828 auto coef = multiply(t1.second._coef, p_cst); 6829 res.insert(t1.second._sign, *coef, *t1.second._p); 6830 } 6831 } 6832 } 6833 if (!_cst->is_zero()) { 6834 for (auto& t2: *f._pterms) { 6835 // if (t2.second._coef->_is_transposed) {// If the coefficient in front is transposed: a^T.(polynomial term) 6836 // auto be = bexpr<type>(product_, make_shared<func>(*this), make_shared<func>(f)); 6837 // *this = func(be); 6838 // _evaluated = false; 6839 // return *this; 6840 // } 6841 if (t2.second._coef->is_function()) { 6842 auto f_cst = *static_pointer_cast<func<T2>>(t2.second._coef); 6843 auto coef = multiply(_cst, f_cst); 6844 res.insert(t2.second._sign, *coef, *t2.second._l); 6845 } 6846 else if(t2.second._coef->is_param()) { 6847 auto p_cst = *static_pointer_cast<param<T2>>(t2.second._coef); 6848 auto coef = multiply(_cst, p_cst); 6849 res.insert(t2.second._sign, *coef, *t2.second._l); 6850 } 6851 else if(t2.second._coef->is_number()) { 6852 auto p_cst = *static_pointer_cast<constant<T2>>(t2.second._coef); 6853 auto coef = multiply(_cst, p_cst); 6854 res.insert(t2.second._sign, *coef, *t2.second._l); 6855 } 6856 } 6857 for (auto& t2: *f._qterms) { 6858 // if (t2.second._coef->_is_transposed) {// If the coefficient in front is transposed: a^T.(polynomial term) 6859 // auto be = bexpr<type>(product_, make_shared<func>(*this), make_shared<func>(f)); 6860 // *this = func(be); 6861 // _evaluated = false; 6862 // return *this; 6863 // } 6864 if (t2.second._coef->is_function()) { 6865 auto f_cst = *static_pointer_cast<func<T2>>(t2.second._coef); 6866 auto coef = multiply(_cst, f_cst); 6867 res.insert(t2.second._sign, *coef, *t2.second._p->first, *t2.second._p->second); 6868 } 6869 else if(t2.second._coef->is_param()) { 6870 auto p_cst = *static_pointer_cast<param<T2>>(t2.second._coef); 6871 auto coef = multiply(_cst, p_cst); 6872 res.insert(t2.second._sign, *coef, *t2.second._p->first, *t2.second._p->second); 6873 } 6874 else if(t2.second._coef->is_number()) { 6875 auto p_cst = *static_pointer_cast<constant<T2>>(t2.second._coef); 6876 auto coef = multiply(_cst, p_cst); 6877 res.insert(t2.second._sign, *coef, *t2.second._p->first, *t2.second._p->second); 6878 } 6879 } 6880 for (auto& t2: *f._lterms) { 6881 // if (t2.second._coef->_is_transposed) {// If the coefficient in front is transposed: a^T.(polynomial term) 6882 // auto be = bexpr<type>(product_, make_shared<func>(*this), make_shared<func>(f)); 6883 // *this = func(be); 6884 // _evaluated = false; 6885 // return *this; 6886 // } 6887 if (t2.second._coef->is_function()) { 6888 auto f_cst = *static_pointer_cast<func<T2>>(t2.second._coef); 6889 auto coef = multiply(_cst, f_cst); 6890 res.insert(t2.second._sign, *coef, *t2.second._p); 6891 } 6892 else if(t2.second._coef->is_param()) { 6893 auto p_cst = *static_pointer_cast<param<T2>>(t2.second._coef); 6894 auto coef = multiply(_cst, p_cst); 6895 res.insert(t2.second._sign, *coef, *t2.second._p); 6896 } 6897 else if(t2.second._coef->is_number()) { 6898 auto p_cst = *static_pointer_cast<constant<T2>>(t2.second._coef); 6899 auto coef = multiply(_cst, p_cst); 6900 res.insert(t2.second._sign, *coef, *t2.second._p); 6901 } 6902 } 6903 if (!f._cst->is_zero()) { 6904 if (f._cst->is_function()) { 6905 auto f_cst = *static_pointer_cast<func<T2>>(f._cst); 6906 res._cst = multiply(_cst, f_cst); 6907 } 6908 else if(f._cst->is_param()) { 6909 auto p_cst = *static_pointer_cast<param<T2>>(f._cst); 6910 res._cst = multiply(_cst, p_cst); 6911 } 6912 else if(f._cst->is_number()) { 6913 auto p_cst = *static_pointer_cast<constant<T2>>(f._cst); 6914 res._cst = multiply(_cst, p_cst); 6915 } 6916 } 6917 } 6918 res.update_dot_dim(*this, f); 6919 if(res.is_quadratic()){res.update_quad_convexity();} 6920 else {_all_convexity = undet_;} 6921 res._all_sign = sign_product(_all_sign, f.get_all_sign()); 6922 *this = move(res); 6923 _evaluated = false; 6924 return *this; 6925 } 6926 6927 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 6928 func& operator+=(const T2 c){ 6929 return *this += func<type>(c); 6930 } 6931 6932 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 6933 func& operator+=(const constant<T2>& c){ 6934 return *this += func<type>(c); 6935 } 6936 6937 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 6938 func& operator+=(const param<T2>& p){ 6939 return *this += func<type>(p); 6940 } 6941 6942 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 6943 func& operator+=(const func<T2>& f){ 6944 if (f.is_zero()) { 6945 return *this; 6946 } 6947 if(is_zero()){ 6948 return *this = f; 6949 } 6950 _evaluated = false; 6951 set_max_dim(f); 6952 if (is_constant() && !f.is_constant()) { 6953 func res(f); 6954 res += *this; 6955 return *this = res; 6956 } 6957 if (!is_constant() && f.is_constant()) { 6958 this->add_cst(f); 6959 update_sign_add(f); 6960 _range = get_plus_range(_range, f._range); 6961 return *this; 6962 } 6963 if (!f.get_cst()->is_zero()) { 6964 if (f.get_cst()->is_number()) { 6965 auto f_cst = static_pointer_cast<constant<T2>>(f.get_cst()); 6966 add_cst(*f_cst); 6967 } 6968 else if (f.get_cst()->is_param()) { 6969 auto f_cst = static_pointer_cast<param<T2>>(f.get_cst()); 6970 add_cst(*f_cst); 6971 } 6972 else { 6973 auto f_cst = static_pointer_cast<func<T2>>(f.get_cst()); 6974 add_cst(*f_cst); 6975 } 6976 if (_cst->is_function()) { 6977 embed(*static_pointer_cast<func>(_cst)); 6978 } 6979 } 6980 for (auto &pair:*f._lterms) { 6981 auto term = pair.second; 6982 if (term._coef->is_function()) { 6983 auto coef = *static_pointer_cast<func<T2>>(term._coef); 6984 term._coef = func(coef).copy(); 6985 } 6986 else if(term._coef->is_param()) { 6987 auto coef = *static_pointer_cast<param<T2>>(term._coef); 6988 term._coef = param<type>(coef).copy(); 6989 } 6990 else if(term._coef->is_number()) { 6991 auto coef = *static_pointer_cast<constant<T2>>(term._coef); 6992 term._coef = constant<type>(coef).copy();//TODO if T2==type no need to cast 6993 } 6994 this->insert(term); 6995 } 6996 for (auto &pair:*f._qterms) { 6997 auto term = pair.second; 6998 if (term._coef->is_function()) { 6999 auto coef = *static_pointer_cast<func<T2>>(term._coef); 7000 term._coef = func(coef).copy(); 7001 } 7002 else if(term._coef->is_param()) { 7003 auto coef = *static_pointer_cast<param<T2>>(term._coef); 7004 term._coef = param<type>(coef).copy(); 7005 } 7006 else if(term._coef->is_number()) { 7007 auto coef = *static_pointer_cast<constant<T2>>(term._coef); 7008 term._coef = constant<type>(coef).copy(); 7009 } 7010 this->insert(term); 7011 } 7012 for (auto &pair:*f._pterms) { 7013 auto term = pair.second; 7014 if (term._coef->is_function()) { 7015 auto coef = *static_pointer_cast<func<T2>>(term._coef); 7016 term._coef = func(coef).copy(); 7017 } 7018 else if(term._coef->is_param()) { 7019 auto coef = *static_pointer_cast<param<T2>>(term._coef); 7020 term._coef = param<type>(coef).copy(); 7021 } 7022 else if(term._coef->is_number()) { 7023 auto coef = *static_pointer_cast<constant<T2>>(term._coef); 7024 term._coef = constant<type>(coef).copy(); 7025 } 7026 this->insert(term); 7027 } 7028 if (_expr && f.get_expr()) { 7029 func f1,f2; 7030 if (_expr->is_uexpr()) { 7031 f1 = func(*static_pointer_cast<uexpr<type>>(_expr)); 7032 } 7033 else { 7034 f1 = func(*static_pointer_cast<bexpr<type>>(_expr)); 7035 } 7036 if (f.get_expr()->is_uexpr()) { 7037 auto ue = *static_pointer_cast<uexpr<T2>>(f.get_expr()); 7038 if (ue._son->is_function()) { 7039 auto son = static_pointer_cast<func<T2>>(ue._son); 7040 ue._son = make_shared<func>(*son); 7041 } 7042 f2 = func(ue); 7043 } 7044 else { 7045 auto bexp = *static_pointer_cast<bexpr<T2>>(f.get_expr()); 7046 if (bexp._lson->is_function()) { 7047 auto lson = static_pointer_cast<func<T2>>(bexp._lson); 7048 bexp._lson = make_shared<func>(*lson); 7049 } 7050 if (bexp._rson->is_function()) { 7051 auto rson = static_pointer_cast<func<T2>>(bexp._rson); 7052 bexp._rson = make_shared<func>(*rson); 7053 } 7054 f2 = func(bexp); 7055 } 7056 _expr = make_shared<bexpr<type>>(bexpr<type>(plus_, f1.copy(), f2.copy())); 7057 embed(_expr); 7058 } 7059 else if (!_expr && f.get_expr()) { 7060 if (f.get_expr()->is_uexpr()) { 7061 auto ue = *static_pointer_cast<uexpr<T2>>(f.get_expr()); 7062 if (ue._son->is_function()) { 7063 auto son = static_pointer_cast<func<T2>>(ue._son); 7064 ue._son = make_shared<func>(*son); 7065 } 7066 _expr = make_shared<uexpr<type>>(ue); 7067 } 7068 else { 7069 auto bexp = make_shared<bexpr<type>>(*static_pointer_cast<bexpr<T2>>(f.get_expr())); 7070 if (bexp->_lson->is_function()) { 7071 auto son = static_pointer_cast<func<T2>>(bexp->_lson); 7072 bexp->_lson = make_shared<func>(*son); 7073 } 7074 if (bexp->_rson->is_function()) { 7075 auto son = static_pointer_cast<func<T2>>(bexp->_rson); 7076 bexp->_rson = make_shared<func>(*son); 7077 } 7078 _expr = bexp; 7079 } 7080 embed(_expr); 7081 if (!_vars->empty()) { 7082 _ftype = nlin_; 7083 } 7084 } 7085 update_sign_add(f); 7086 if(is_quadratic()){ 7087 update_quad_convexity(); 7088 } 7089 else { 7090 update_convexity_add(f._all_convexity); 7091 } 7092 _range = get_plus_range(_range, f._range); 7093 if(func_is_number()){ 7094 _ftype = const_; 7095 set_range(eval(_cst)); 7096 } 7097 _evaluated = false; 7098 return *this; 7099 } 7100 7101 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 7102 func& operator-=(const T2 c){ 7103 return *this -= func<type>(c); 7104 } 7105 7106 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 7107 func& operator-=(const constant<T2>& c){ 7108 return *this -= func<type>(c); 7109 } 7110 7111 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 7112 func& operator-=(const param<T2>& p){ 7113 return *this -= func<type>(p); 7114 } 7115 7116 template<class T2, typename enable_if<is_convertible<T2, type>::value && sizeof(T2) <= sizeof(type)>::type* = nullptr> 7117 func& operator-=(const func<T2>& f){ 7118 auto res = f; 7119 res.reverse_sign(); 7120 return *this += res; 7121 } 7122 // 7123 // func_ tr() const { 7124 // auto f = func_(*this); 7125 // f.transpose(); 7126 // return f; 7127 // } 7128 // 7129 /** Reset all fields to default values */ reset()7130 void reset(){ 7131 _to_str = ""; 7132 update_range(); 7133 _all_range = nullptr; 7134 _vars->clear(); 7135 _val->clear(); 7136 _params->clear(); 7137 if(_dfdx){ 7138 _dfdx->clear(); 7139 } 7140 if(_hess_link){ 7141 _hess_link->clear(); 7142 } 7143 _convexity = nullptr; 7144 _sign = nullptr; 7145 _expr = nullptr; 7146 _ftype = const_; 7147 _all_convexity = linear_; 7148 _all_sign = zero_; 7149 _is_transposed = false; 7150 _is_vector = false; 7151 _evaluated = true; 7152 _embedded = false; 7153 _dim[0] = 1; 7154 _dim[1] = 1; 7155 this->_val->clear();//TODO all_range? 7156 _lterms->clear(); 7157 _qterms->clear(); 7158 _pterms->clear(); 7159 _cst = make_shared<constant<type>>(); 7160 _nb_vars = 0; 7161 _nnz_h = 0; 7162 _nnz_j = 0; 7163 }; 7164 tr()7165 func tr() const { 7166 auto f = *this; 7167 f.transpose(); 7168 return f; 7169 } 7170 update_quad_convexity()7171 void update_quad_convexity(){ 7172 if(is_unitary()){ 7173 //TODO check second derivative 7174 } 7175 if (!_pterms->empty()) { 7176 _all_convexity = undet_; 7177 return; 7178 } 7179 if (_qterms->empty() && !_expr) { 7180 _all_convexity = linear_; 7181 return; 7182 } 7183 if (!_qterms->empty() && !_expr) { 7184 _all_convexity = get_convexity(_qterms->begin()->second); 7185 for (auto pair_it = next(_qterms->begin()); pair_it != _qterms->end(); pair_it++) { 7186 Convexity conv = get_convexity(pair_it->second); 7187 if (_all_convexity==undet_ || conv ==undet_ || (_all_convexity==convex_ && conv==concave_) || (_all_convexity==concave_ && conv==convex_)) { 7188 _all_convexity = undet_; 7189 return; 7190 } 7191 else { 7192 _all_convexity = conv; 7193 } 7194 } 7195 } 7196 } 7197 7198 /** 7199 Returns the convexity of current function if quadratic term q was to be added. 7200 @param[in] q quadratic term to be added. 7201 @return convexity of function if q = coef*p1*p2 was to be added. 7202 */ get_convexity(const qterm & q)7203 Convexity get_convexity(const qterm& q){ 7204 if(q._p->first == q._p->second){ 7205 if (q._sign && (q._coef->is_positive() || q._coef->is_non_negative())) { 7206 return convex_; 7207 } 7208 if (q._sign && (q._coef->is_negative() || q._coef->is_non_positive())) { 7209 return concave_; 7210 } 7211 if (!q._sign && (q._coef->is_negative() || q._coef->is_non_positive())) { 7212 return convex_; 7213 } 7214 if (!q._sign && (q._coef->is_negative() || q._coef->is_non_positive())) { 7215 return concave_; 7216 } 7217 } 7218 // At this stage, we know that q._p->first !=q._p->second 7219 // Checking if the product can be factorized 7220 auto sqr1 = get_square(q._p->first); 7221 auto sqr2 = get_square(q._p->second); 7222 if (sqr1 && sqr2){ 7223 auto c1 = sqr1->_coef; 7224 auto c2 = sqr2->_coef; 7225 if ((sqr1->_sign^c1->is_positive())==(sqr2->_sign^c2->is_positive())) { 7226 if (c1->func_is_number() && c2->func_is_number() && q._coef->func_is_number()) { 7227 if (2.*std::sqrt(eval<type>(c1)*eval<type>(c2)) >= eval<type>(q._coef)) { 7228 if (!(sqr1->_sign^c1->is_positive())) { 7229 return convex_; 7230 } 7231 return concave_; 7232 } 7233 } 7234 return undet_; 7235 } 7236 } 7237 return undet_; 7238 } 7239 check_rotated_soc()7240 bool check_rotated_soc(){ 7241 if (!_lterms->empty() || _qterms->empty() || !_pterms->empty() || _expr) { 7242 return false; 7243 } 7244 unsigned nb_bilinear = 0, nb_quad = 0; 7245 Sign bilinear_sign = unknown_, quadratic_sign = unknown_, var1_sign = unknown_, var2_sign = unknown_; 7246 for (auto &qt_pair: *_qterms) { 7247 if (qt_pair.second._p->first!=qt_pair.second._p->second) { 7248 bilinear_sign = qt_pair.second.get_all_sign(); 7249 var1_sign = qt_pair.second._p->first->get_all_sign(); 7250 var2_sign = qt_pair.second._p->second->get_all_sign(); 7251 if (bilinear_sign==unknown_ || var1_sign==neg_ || var2_sign==neg_) { 7252 return false; 7253 } 7254 nb_bilinear++; 7255 if (nb_bilinear > 1) { 7256 return false; 7257 } 7258 } 7259 else{ 7260 nb_quad++; 7261 auto sign = qt_pair.second.get_all_sign(); 7262 if (quadratic_sign!=unknown_ && quadratic_sign!=sign) { 7263 return false; 7264 } 7265 if (quadratic_sign!=unknown_ && quadratic_sign==bilinear_sign) { 7266 return false; 7267 } 7268 else { 7269 quadratic_sign = sign; 7270 } 7271 } 7272 } 7273 if(nb_quad==0){ 7274 return false; 7275 } 7276 if (bilinear_sign==pos_) { 7277 _all_convexity = concave_; 7278 return true; 7279 } 7280 else if(bilinear_sign==neg_) { 7281 _all_convexity = convex_; 7282 return true; 7283 } 7284 return false; 7285 } 7286 check_soc()7287 bool check_soc(){ 7288 if (!_lterms->empty() || _qterms->empty() || !_pterms->empty() || _expr) { 7289 return false; 7290 } 7291 unsigned nb_neg = 0, nb_pos = 0; 7292 for (auto &qt_pair: *_qterms) { 7293 if (qt_pair.second._p->first!=qt_pair.second._p->second) { 7294 return false; 7295 } 7296 auto sign = qt_pair.second.get_all_sign(); 7297 if (sign==unknown_) { 7298 return false; 7299 } 7300 if (sign==pos_) { 7301 nb_pos++; 7302 } 7303 else if(sign==neg_){ 7304 nb_neg++; 7305 } 7306 } 7307 if (nb_neg==1 && nb_pos>1) { 7308 _all_convexity = convex_; 7309 return true; 7310 } 7311 else if (nb_pos==1 && nb_neg>1){ 7312 _all_convexity = concave_; 7313 return true; 7314 } 7315 return false; 7316 } 7317 7318 7319 7320 /* 7321 Subfuntion 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. If x does not appear in this, add it. THIS SHOULD ONLY BE CALLED BY A SUBEXPRESSION OF THIS. 7322 @param[in] f function to merge variables and parameters with. 7323 */ merge_vars(func & f)7324 void merge_vars(func& f){ 7325 for (auto &pair:*f._lterms) { 7326 auto coef = pair.second._coef; 7327 if(coef->is_function()){ 7328 embed(*static_pointer_cast<func>(coef)); 7329 } 7330 else if(coef->is_param()) { 7331 auto cp = static_pointer_cast<param_>(coef); 7332 auto pname = cp->get_name(false,false); 7333 auto p_exist = get_param(pname); 7334 if (!p_exist) { 7335 add_param(cp); 7336 } 7337 else { 7338 incr_occ_param(pname); 7339 } 7340 } 7341 auto p = pair.second._p; 7342 if (p->is_var()) { 7343 auto pname = p->get_name(false,false); 7344 auto it = _vars->find(pname); 7345 if (it==_vars->end()) { 7346 add_var(f.get_var(pname)); 7347 } 7348 else{ 7349 pair.second._p = it->second.first; 7350 it->second.second++; 7351 } 7352 } 7353 else { 7354 auto pname = p->get_name(false,false); 7355 auto it = _params->find(pname); 7356 if (it==_params->end()) { 7357 add_param(f.get_param(pname)); 7358 } 7359 else{ 7360 pair.second._p = it->second.first; 7361 it->second.second++; 7362 } 7363 } 7364 } 7365 for (auto &pair:*f._qterms) { 7366 auto coef = pair.second._coef; 7367 if (coef->is_function()){ 7368 embed(*static_pointer_cast<func>(coef)); 7369 } 7370 else if(coef->is_param()) { 7371 auto cp = static_pointer_cast<param_>(coef); 7372 auto pname = cp->get_name(false,false); 7373 auto p_exist = get_param(pname); 7374 if (!p_exist) { 7375 add_param(cp); 7376 } 7377 else { 7378 incr_occ_param(pname); 7379 } 7380 } 7381 auto p1 = pair.second._p->first; 7382 auto p2 = pair.second._p->second; 7383 if (p1->is_var()) { 7384 auto it1 = _vars->find(p1->get_name(false,false)); 7385 if (it1==_vars->end()) { 7386 add_var(f.get_var(p1->get_name(false,false))); 7387 } 7388 else{ 7389 pair.second._p->first = it1->second.first; 7390 it1->second.second++; 7391 } 7392 auto it2 = _vars->find(p2->get_name(false,false)); 7393 if (it2==_vars->end()) { 7394 add_var(f.get_var(p2->get_name(false,false))); 7395 } 7396 else{ 7397 pair.second._p->second = it2->second.first; 7398 it2->second.second++; 7399 } 7400 } 7401 else { 7402 auto it1 = _params->find(p1->get_name(false,false)); 7403 if (it1==_params->end()) { 7404 add_param(f.get_param(p1->get_name(false,false))); 7405 } 7406 else{ 7407 pair.second._p->first = it1->second.first; 7408 it1->second.second++; 7409 } 7410 auto it2 = _params->find(p2->get_name(false,false)); 7411 if (it2==_params->end()) { 7412 add_param(f.get_param(p2->get_name(false,false))); 7413 } 7414 else{ 7415 pair.second._p->second = it2->second.first; 7416 it2->second.second++; 7417 } 7418 } 7419 } 7420 for (auto &pair:*f._pterms) { 7421 auto coef = pair.second._coef; 7422 if(coef->is_function()){ 7423 embed(*static_pointer_cast<func>(coef)); 7424 } 7425 else if(coef->is_param()) { 7426 auto cp = static_pointer_cast<param_>(coef); 7427 auto pname = cp->get_name(false,false); 7428 auto p_exist = get_param(pname); 7429 if (!p_exist) { 7430 add_param(cp); 7431 } 7432 else { 7433 incr_occ_param(pname); 7434 } 7435 } 7436 auto list = pair.second._l; 7437 for (auto &ppi: *list) { 7438 auto p = ppi.first; 7439 if (p->is_var()) { 7440 auto it = _vars->find(p->get_name(false,false)); 7441 if (it==_vars->end()) { 7442 add_var(f.get_var(p->get_name(false,false))); 7443 } 7444 else{ 7445 ppi.first = it->second.first; 7446 it->second.second++; 7447 } 7448 } 7449 else { 7450 auto it = _params->find(p->get_name(false,false)); 7451 if (it==_params->end()) { 7452 add_param(f.get_param(p->get_name(false,false))); 7453 } 7454 else{ 7455 ppi.first = it->second.first; 7456 it->second.second++; 7457 } 7458 } 7459 } 7460 } 7461 if (f._expr) { 7462 embed(f._expr); 7463 } 7464 if(f._cst->is_function()){ 7465 embed(*static_pointer_cast<func>(f._cst)); 7466 } 7467 7468 auto old_vars = *f._vars; 7469 for (auto &vp: old_vars) { 7470 auto vv = (*_vars)[vp.first].first; 7471 auto vv_f = (*f._vars)[vp.first].first; 7472 if (vv != vv_f) { 7473 // delete vv_f; 7474 f._vars->erase(vp.first); 7475 f._vars->insert(make_pair<>(vp.first, make_pair<>(vv, vp.second.second))); 7476 } 7477 } 7478 auto old_params = *f._params; 7479 for (auto &pp: old_params) { 7480 auto p = (*_params)[pp.first].first; 7481 auto p_f = (*f._params)[pp.first].first; 7482 if (p != p_f) { 7483 // delete p_f; 7484 f._params->erase(pp.first); 7485 f._params->insert(make_pair<>(pp.first, make_pair<>(p, pp.second.second))); 7486 } 7487 } 7488 } 7489 7490 /** 7491 Merge variables and parameters with expression e. If a variable x in e exists in the current funtion, x will now point to the same variable appearing in the current function. 7492 @param[in] e expression to merge variables and parameters with. 7493 */ embed(shared_ptr<expr<type>> e)7494 void embed(shared_ptr<expr<type>> e){ 7495 _evaluated = false; 7496 switch (e->get_type()) { 7497 case uexp_c:{ 7498 auto ue = static_pointer_cast<uexpr<type>>(e); 7499 if (ue->_son->is_function()) { 7500 embed(*static_pointer_cast<func>(ue->_son)); 7501 } 7502 else if(ue->_son->is_expr()){ 7503 embed(static_pointer_cast<expr<type>>(ue->_son)); 7504 } 7505 else if (ue->_son->is_param() || ue->_son->is_var() ){ 7506 auto p = static_pointer_cast<param_>(ue->_son); 7507 auto name = p->get_name(false,false); 7508 if (p->is_var()) { 7509 auto pnew = get_var(name); 7510 if (!pnew) { 7511 pnew = p; 7512 add_var(pnew,1); 7513 } 7514 else { 7515 ue->_son = pnew; 7516 } 7517 } 7518 else { 7519 auto pnew = get_param(name); 7520 if (!pnew) { 7521 pnew = p; 7522 add_param(pnew); 7523 } 7524 else { 7525 ue->_son = pnew; 7526 } 7527 } 7528 } 7529 break; 7530 } 7531 case bexp_c:{ 7532 auto be = static_pointer_cast<bexpr<type>>(e); 7533 if (be->_lson->is_function()) { 7534 embed(*static_pointer_cast<func>(be->_lson)); 7535 } 7536 else if(be->_lson->is_expr()){ 7537 embed(static_pointer_cast<expr<type>>(be->_lson)); 7538 } 7539 else if (be->_lson->is_param() || be->_lson->is_var() ){ 7540 auto p = static_pointer_cast<param_>(be->_lson); 7541 auto name = p->get_name(false,false); 7542 if (p->is_var()) { 7543 auto pnew = get_var(name); 7544 if (!pnew) { 7545 pnew = p; 7546 add_var(pnew,1); 7547 } 7548 else { 7549 be->_lson = pnew; 7550 } 7551 } 7552 else { 7553 auto pnew = get_param(name); 7554 if (!pnew) { 7555 pnew = p; 7556 add_param(pnew); 7557 } 7558 else { 7559 be->_lson = pnew; 7560 } 7561 } 7562 } 7563 if (be->_rson->is_function()) { 7564 embed(*static_pointer_cast<func>(be->_rson)); 7565 } 7566 else if(be->_rson->is_expr()){ 7567 embed(static_pointer_cast<expr<type>>(be->_rson)); 7568 } 7569 else if (be->_rson->is_param() || be->_rson->is_var() ){ 7570 auto p = static_pointer_cast<param_>(be->_rson); 7571 auto name = p->get_name(false,false); 7572 if (p->is_var()) { 7573 auto pnew = get_var(name); 7574 if (!pnew) { 7575 pnew = p; 7576 add_var(pnew,1); 7577 } 7578 else { 7579 be->_rson = pnew; 7580 } 7581 } 7582 else { 7583 auto pnew = get_param(name); 7584 if (!pnew) { 7585 pnew = p; 7586 add_param(pnew); 7587 } 7588 else { 7589 be->_rson = pnew; 7590 } 7591 } 7592 } 7593 } 7594 default: 7595 break; 7596 } 7597 } 7598 7599 /** 7600 Mark f as embeded and merge variables and parameters with f (by calling merge_vars(func_&& f). If a variable x in f exists in the current funtion, x will now point to the same variable appearing in current function. 7601 @param[in] f function to merge variables and parameters with. 7602 */ embed(func & f)7603 void embed(func& f){ 7604 f._embedded = true; 7605 merge_vars(f); 7606 } 7607 7608 7609 7610 /** 7611 Relax and replace integer variables with continuous ones provided in argument vars. 7612 @param[in] vars set with continuous variables replacements. 7613 */ relax(const map<size_t,shared_ptr<param_>> & vars)7614 void relax(const map<size_t, shared_ptr<param_>>& vars){ 7615 auto new_vars = make_shared<map<string, pair<shared_ptr<param_>, unsigned>>>(); 7616 bool has_int = false; 7617 for (auto &v_p:*_vars) { 7618 auto old_var = v_p.second.first; 7619 auto nb_occ = v_p.second.second; 7620 auto new_var = vars.at(old_var->get_vec_id())->pcopy(); 7621 new_var->shallow_copy(*old_var); 7622 (*new_vars)[new_var->get_name(false,false)] = make_pair<>(new_var,nb_occ); 7623 if (old_var->is_binary() || old_var->is_short() || old_var->is_integer()) { 7624 has_int = true; 7625 new_var->_is_relaxed = true; 7626 } 7627 } 7628 if (!has_int) { 7629 return; 7630 } 7631 7632 for (auto <:get_lterms()) { 7633 lt.second._p = new_vars->at(lt.second._p->get_name(false,false)).first; 7634 } 7635 for (auto <:get_qterms()) { 7636 lt.second._p->first = new_vars->at(lt.second._p->first->get_name(false,false)).first; 7637 lt.second._p->second = new_vars->at(lt.second._p->second->get_name(false,false)).first; 7638 } 7639 for (auto <:get_pterms()) { 7640 for (auto &v_p:*lt.second._l) { 7641 v_p.first = new_vars->at(v_p.first->get_name(false,false)).first; 7642 } 7643 } 7644 if (_expr) { 7645 if (_expr->is_uexpr()) { 7646 auto ue = static_pointer_cast<uexpr<type>>(_expr); 7647 ue->_son->relax(vars); 7648 } 7649 else { 7650 auto be = static_pointer_cast<bexpr<type>>(_expr); 7651 be->_lson->relax(vars); 7652 be->_rson->relax(vars); 7653 } 7654 } 7655 _vars = new_vars; 7656 } 7657 update_vars()7658 void update_vars(){merge_vars(*this);}; 7659 7660 bool insert(const constant_& coef, const param_& p1, const param_& p2, bool coef_p1_tr=false){ 7661 return insert(true, coef, p1, p2, coef_p1_tr); 7662 }; 7663 7664 bool insert(bool sign, const constant_& coef, const param_& p1, const param_& p2, bool c_p1_transposed=false){/**< Adds coef*p1*p2 to the function. Returns true if added new term, false if only updated coef of p1*p2 */ 7665 auto ps1 = p1.get_name(false,false); 7666 auto ps2 = p2.get_name(false,false); 7667 auto qname = ps1+","+ps2; 7668 auto pair_it = _qterms->find(qname); 7669 if (pair_it == _qterms->end()) { 7670 qname = ps2+","+ps1; 7671 pair_it = _qterms->find(qname); 7672 } 7673 if (pair_it == _qterms->end()) { 7674 qname = ps1+","+ps2; 7675 } 7676 shared_ptr<param_> p_new1; 7677 shared_ptr<param_> p_new2; 7678 _evaluated=false; 7679 if (_ftype <= lin_ && p1.is_var()) { 7680 _ftype = quad_; 7681 } 7682 if (pair_it == _qterms->end()) { 7683 if (p1.is_var()) { 7684 p_new1 = get_var(ps1); 7685 if (!p_new1) { 7686 p_new1 = p1.pcopy(); 7687 add_var(p_new1); 7688 } 7689 else { 7690 incr_occ_var(ps1); 7691 } 7692 } 7693 else { 7694 p_new1 = get_param(ps1); 7695 if (!p_new1) { 7696 p_new1 = p1.pcopy(); 7697 add_param(p_new1); 7698 } 7699 else { 7700 incr_occ_param(ps1); 7701 } 7702 7703 } 7704 if (p2.is_var()) { 7705 p_new2 = get_var(ps2); 7706 if (!p_new2) { 7707 p_new2 = p2.pcopy(); 7708 add_var(p_new2); 7709 } 7710 else { 7711 incr_occ_var(ps2); 7712 } 7713 } 7714 else { 7715 p_new2 = get_param(ps2); 7716 if (!p_new2) { 7717 p_new2 = p2.pcopy(); 7718 add_param(p_new2); 7719 } 7720 else { 7721 incr_occ_param(ps2); 7722 } 7723 } 7724 auto c_new = coef.copy(); 7725 if (c_new->is_function()) { 7726 embed(*static_pointer_cast<func>(c_new)); 7727 } 7728 else if(c_new->is_param()) { 7729 auto cp = static_pointer_cast<param_>(c_new); 7730 auto pname = cp->get_name(false,false); 7731 auto p_exist = get_param(pname); 7732 if (!p_exist) { 7733 add_param(cp); 7734 } 7735 else { 7736 incr_occ_param(pname); 7737 } 7738 } 7739 qterm q(sign, c_new, p_new1, p_new2); 7740 q._coef_p1_tr = c_p1_transposed; 7741 _qterms->insert(make_pair<>(qname, move(q))); 7742 if(p_new1->is_var()){ 7743 _evaluated = false; 7744 } 7745 // update_convexity(); 7746 return true; 7747 } 7748 else { 7749 if (pair_it->second._sign == sign) { 7750 if (coef.is_function()) { 7751 auto coef2 = *(func<type>*)(&coef); 7752 pair_it->second._coef = add(pair_it->second._coef,coef2); 7753 } 7754 else if(coef.is_param()) { 7755 auto coef2 = *(param<type>*)(&coef); 7756 pair_it->second._coef = add(pair_it->second._coef,coef2); 7757 } 7758 else if(coef.is_number()) { 7759 auto coef2 = *(constant<type>*)(&coef); 7760 pair_it->second._coef = add(pair_it->second._coef,coef2); 7761 } 7762 } 7763 else{ 7764 if (coef.is_function()) { 7765 auto coef2 = *(func<type>*)(&coef); 7766 pair_it->second._coef = subtract(pair_it->second._coef,coef2); 7767 } 7768 else if(coef.is_param()) { 7769 auto coef2 = *(param<type>*)(&coef); 7770 pair_it->second._coef = subtract(pair_it->second._coef,coef2); 7771 } 7772 else if(coef.is_number()) { 7773 auto coef2 = *(constant<type>*)(&coef); 7774 pair_it->second._coef = subtract(pair_it->second._coef,coef2); 7775 } 7776 } 7777 if (pair_it->second._coef->is_function()) { 7778 embed(*static_pointer_cast<func>(pair_it->second._coef)); 7779 } 7780 if (pair_it->second._coef->is_zero()) { 7781 if (p1.is_var()) { 7782 decr_occ_var(ps1); 7783 } 7784 else { 7785 decr_occ_param(ps1); 7786 } 7787 if (p2.is_var()) { 7788 decr_occ_var(ps2); 7789 } 7790 else { 7791 decr_occ_param(ps2); 7792 } 7793 _qterms->erase(pair_it); 7794 if(_qterms->empty()){ 7795 _ftype = lin_; 7796 } 7797 if(is_constant()){ 7798 _ftype = const_; 7799 _val->resize(1); 7800 } 7801 // update_sign(); 7802 // update_convexity(); 7803 } 7804 // else { 7805 // update_sign(pair_it->second); 7806 // update_convexity(pair_it->second); 7807 // } 7808 return false; 7809 } 7810 };/**< Adds coef*p1*p2 to the function. Returns true if added new term, false if only updated coef of p1*p2 */ 7811 func(const pterm & term)7812 func(const pterm& term){ 7813 func res = unit<type>(); 7814 for (auto& it:*term._l) { 7815 auto p = it.first; 7816 if(p->is_var()){ 7817 auto v = dynamic_pointer_cast<var<type>>(p); 7818 if(!v){ 7819 throw invalid_argument("In function: func(const pterm& term), cannot cast variable, make sure all variables in term have the same numerical type of f"); 7820 } 7821 res *= pow(*v,it.second); 7822 } 7823 else { 7824 auto v = dynamic_pointer_cast<param<type>>(p); 7825 if(!v){ 7826 throw invalid_argument("In function: func(const pterm& term), cannot cast parameter, make sure all parameters in term have the same numerical type of f"); 7827 } 7828 res *= pow(*v,it.second); 7829 } 7830 } 7831 if (term._coef->is_function()) { 7832 auto coef = *static_pointer_cast<func<type>>(term._coef); 7833 res *= coef; 7834 } 7835 else if(term._coef->is_param()) { 7836 auto coef = *static_pointer_cast<param<type>>(term._coef); 7837 res *= coef; 7838 } 7839 else if(term._coef->is_number()) { 7840 auto coef = *static_pointer_cast<constant<type>>(term._coef); 7841 res *= coef; 7842 } 7843 *this = res; 7844 } 7845 insert(bool sign,const constant_ & coef,const list<pair<shared_ptr<param_>,int>> & l)7846 bool insert(bool sign, const constant_& coef, const list<pair<shared_ptr<param_>, int>>& l){/**< Adds polynomial term to the function. Returns true if added new term, false if only updated corresponding coef */ 7847 _all_convexity = undet_; 7848 string name; 7849 string s; 7850 bool newv = true; 7851 _evaluated=false; 7852 // int i = 0; 7853 for (auto &pair:l) { 7854 name += pair.first->get_name(false,false); 7855 name += "^"+to_string(pair.second); 7856 name += ","; 7857 } 7858 auto pair_it = _pterms->find(name); 7859 auto p = l.begin()->first; 7860 shared_ptr<param_> pnew; 7861 if (_ftype <= quad_ && p->is_var()) { 7862 _ftype = pol_; 7863 } 7864 if (pair_it == _pterms->end()) { 7865 auto newl = make_shared<list<pair<shared_ptr<param_>, int>>>(); 7866 // i = 1; 7867 for (auto &pair:l) { 7868 p = pair.first; 7869 s = p->get_name(false,false); 7870 if (p->is_var()) { 7871 pnew = get_var(s); 7872 if (!pnew) { 7873 pnew = p->pcopy(); 7874 add_var(pnew,pair.second); 7875 } 7876 else { 7877 incr_occ_var(s); 7878 } 7879 } 7880 else { 7881 pnew = get_param(s); 7882 if (!pnew) { 7883 pnew = p->pcopy(); 7884 add_param(pnew); 7885 } 7886 else { 7887 incr_occ_param(s); 7888 } 7889 } 7890 newv = true; 7891 for (auto& p_it:*newl) { 7892 if (p_it.first->get_name(false,false)==s) { 7893 p_it.second++; 7894 newv = false; 7895 break; 7896 } 7897 } 7898 if (newv) { 7899 newl->push_back(make_pair<>(pnew, pair.second)); 7900 } 7901 } 7902 auto c_new = coef.copy(); 7903 if (c_new->is_function()) { 7904 embed(*static_pointer_cast<func>(c_new)); 7905 } 7906 else if(c_new->is_param()) { 7907 auto cp = static_pointer_cast<param_>(c_new); 7908 auto pname = cp->get_name(false,false); 7909 auto p_exist = get_param(pname); 7910 if (!p_exist) { 7911 add_param(cp); 7912 } 7913 else { 7914 incr_occ_param(pname); 7915 } 7916 } 7917 pterm p(sign, c_new, newl); 7918 _pterms->insert(make_pair<>(name, move(p))); 7919 if(pnew->is_var()){ 7920 _evaluated = false; 7921 } 7922 return true; 7923 } 7924 else { 7925 if (pair_it->second._sign == sign) { 7926 if (coef.is_function()) { 7927 auto coef2 = *(func<type>*)(&coef); 7928 pair_it->second._coef = add(pair_it->second._coef,coef2); 7929 } 7930 else if(coef.is_param()) { 7931 auto coef2 = *(param<type>*)(&coef); 7932 pair_it->second._coef = add(pair_it->second._coef,coef2); 7933 } 7934 else if(coef.is_number()) { 7935 auto coef2 = *(constant<type>*)(&coef); 7936 pair_it->second._coef = add(pair_it->second._coef,coef2); 7937 } 7938 } 7939 else{ 7940 if (coef.is_function()) { 7941 auto coef2 = *(func<type>*)(&coef); 7942 pair_it->second._coef = subtract(pair_it->second._coef,coef2); 7943 } 7944 else if(coef.is_param()) { 7945 auto coef2 = *(param<type>*)(&coef); 7946 pair_it->second._coef = subtract(pair_it->second._coef,coef2); 7947 } 7948 else if(coef.is_number()) { 7949 auto coef2 = *(constant<type>*)(&coef); 7950 pair_it->second._coef = subtract(pair_it->second._coef,coef2); 7951 } 7952 } 7953 if (pair_it->second._coef->is_function()) { 7954 embed(*static_pointer_cast<func>(pair_it->second._coef)); 7955 } 7956 if (pair_it->second._coef->is_zero()) { 7957 for (auto& it:*pair_it->second._l) { 7958 p = it.first; 7959 s = p->get_name(false,false); 7960 if (p->is_var()) { 7961 decr_occ_var(s,it.second); 7962 } 7963 else { 7964 decr_occ_param(s,it.second); 7965 } 7966 } 7967 _pterms->erase(pair_it); 7968 if(_pterms->empty()){ 7969 _ftype = quad_; 7970 } 7971 if(_qterms->empty()){ 7972 _ftype = lin_; 7973 } 7974 if(is_constant()){ 7975 _ftype = const_; 7976 _val->resize(1); 7977 } 7978 // update_sign(); 7979 // update_convexity(); 7980 } 7981 // else { 7982 // update_sign(pair_it->second); 7983 // } 7984 return false; 7985 } 7986 7987 };/**< Adds polynomial term to the function. Returns true if added new term, false if only updated corresponding coef */ 7988 insert(const constant_ & coef,const param_ & p,int exp)7989 bool insert(const constant_& coef, const param_& p, int exp){ 7990 return insert(true, coef, p, exp); 7991 }; 7992 insert(bool sign,const constant_ & coef,const param_ & p,int exp)7993 bool insert(bool sign, const constant_& coef, const param_& p, int exp){ 7994 list<pair<shared_ptr<param_>, int>> l; 7995 l.push_back(make_pair<>(p.pcopy(), exp)); 7996 return insert(sign, coef, l); 7997 };/**< Adds polynomial term to the function. Returns true if added new term, false if only updated corresponding coef */ 7998 7999 insert(const qterm & term)8000 bool insert(const qterm& term){ 8001 return insert(term._sign, *term._coef, *term._p->first, *term._p->second, term._coef_p1_tr); 8002 }; 8003 insert(const pterm & term)8004 bool insert(const pterm& term){return insert(term._sign, *term._coef, *term._l);}; 8005 8006 func<type> get_OA_symbolic(const vector<param<double>>& c, const param<double>& c0, const indices& Pert); 8007 8008 }; 8009 8010 8011 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 8012 func<T1> operator+(const func<T1>& f1, const func<T2>& f2){ 8013 return func<T1>(f1)+= f2; 8014 } 8015 8016 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 8017 func<T2> operator+(const func<T1>& f1, const func<T2>& f2){ 8018 return func<T2>(f1)+= f2; 8019 } 8020 8021 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 8022 func<T1> operator-(const func<T1>& f1, const func<T2>& f2){ 8023 return func<T1>(f1)-= f2; 8024 } 8025 8026 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 8027 func<T2> operator-(const func<T1>& f1, const func<T2>& f2){ 8028 return func<T2>(f1)-= f2; 8029 } 8030 // 8031 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 8032 func<T1> operator*(const func<T1>& f1, const func<T2>& f2){ 8033 if((f1._is_transposed || f1.is_matrix()) && !f2._is_vector){ 8034 return func<T1>(f1)*= f2.vec(); 8035 } 8036 return func<T1>(f1)*= f2; 8037 } 8038 8039 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 8040 func<T2> operator*(const func<T1>& f1, const func<T2>& f2){ 8041 if((f1._is_transposed || f1.is_matrix()) && !f2._is_vector){ 8042 return func<T2>(f1)*= f2.vec(); 8043 } 8044 return func<T2>(f1)*= f2; 8045 } 8046 8047 8048 8049 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 8050 func<T1> operator*(const param<T1>& p1, const param<T2>& p2){ 8051 func<T1> res; 8052 if(p1.is_zero() || p2.is_zero()){ 8053 return res; 8054 } 8055 if(p1.is_param() && p2.is_var()){ 8056 if(p1._is_transposed && ! p2._is_vector){ 8057 res.insert(true,p1,p2.vec()); 8058 } 8059 else { 8060 res.insert(true,p1,p2); 8061 } 8062 res.update_dot_dim(p1,p2); 8063 } 8064 else if(p2.is_param() && p1.is_var()){ 8065 if(p1._is_transposed && (p2.is_row_vector() || p2.is_matrix_indexed())){/* transform x^T*p to (p^T*x)^T */ 8066 auto new_p2 = p2.tr(); 8067 auto new_p1 = p1.tr(); 8068 res.insert(true,new_p2,new_p1); 8069 res.update_dot_dim(new_p2,new_p1); 8070 res.transpose(); 8071 } 8072 else { 8073 res.insert(true,p2,p1); 8074 res.update_dot_dim(p1,p2); 8075 } 8076 } 8077 else {//Both vars or both params 8078 if(p1._is_transposed && !p2._is_vector){ 8079 res.insert(true,unit<T1>(),p1,p2.vec()); 8080 } 8081 else { 8082 res.insert(true,unit<T1>(),p1,p2); 8083 } 8084 res.update_dot_dim(p1,p2); 8085 } 8086 8087 if(res.has_square()){ 8088 auto signp = p1.get_all_sign(); 8089 if(signp==neg_ || signp==pos_){ 8090 res._all_sign = pos_; 8091 } 8092 else { 8093 res._all_sign = non_neg_; 8094 } 8095 res._range->first=zero<T1>().eval(); 8096 if(p1.is_positive() || p1.is_negative()){ 8097 res._range->first=extended_mult(p1._range->first,p1._range->first); 8098 } 8099 res._range->second=extended_mult(std::max(std::abs(p1._range->second),std::abs(p1._range->first)),std::max(std::abs(p1._range->second),std::abs(p1._range->first))); 8100 } 8101 else { 8102 res._range = get_product_range(p1._range,p2._range); 8103 res._all_sign = sign_product(p1.get_all_sign(), p2.get_all_sign()); 8104 } 8105 if(res.is_quadratic()){res.update_quad_convexity();} 8106 if(p1._is_transposed){ 8107 res._range->first = extended_mult(res._range->first,(T1)p1._dim[0]); 8108 res._range->second = extended_mult(res._range->second,(T1)p1._dim[0]); 8109 } 8110 return res; 8111 } 8112 8113 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 8114 func<T2> operator*(const param<T1>& p1, const param<T2>& p2){ 8115 func<T2> res; 8116 if(p1.is_zero() || p2.is_zero()){ 8117 return res; 8118 } 8119 if(p1.is_param() && p2.is_var()){ 8120 if(p1._is_transposed && ! p2._is_vector){ 8121 res.insert(true,param<T2>(p1),p2.vec()); 8122 } 8123 else { 8124 res.insert(true,param<T2>(p1),p2); 8125 } 8126 res.update_dot_dim(p1,p2); 8127 } 8128 else if(p2.is_param() && p1.is_var()){ 8129 if(p1._is_transposed && (p2.is_row_vector() || p2.is_matrix_indexed())){/* transform x^T*p to (p^T*x)^T */ 8130 auto new_p2 = p2.tr(); 8131 auto new_p1 = p1.tr(); 8132 res.insert(true,new_p2,new_p1); 8133 res.update_dot_dim(new_p2,new_p1); 8134 res.transpose(); 8135 } 8136 else { 8137 res.insert(true,p2,p1); 8138 res.update_dot_dim(p1,p2); 8139 } 8140 } 8141 else {//Both vars or both params 8142 if(p1._is_transposed && !p2._is_vector){ 8143 res.insert(true,unit<T2>(),p1,p2.vec()); 8144 } 8145 else { 8146 res.insert(true,unit<T2>(),p1,p2); 8147 } 8148 res.update_dot_dim(p1,p2); 8149 } 8150 8151 if(res.has_square()){ 8152 auto signp = p1.get_all_sign(); 8153 if(signp==neg_ || signp==pos_){ 8154 res._all_sign = pos_; 8155 } 8156 else { 8157 res._all_sign = non_neg_; 8158 } 8159 res._range->first=zero<T2>().eval(); 8160 if(p1.is_positive() || p1.is_negative()){ 8161 res._range->first=extended_mult(p1._range->first,p1._range->first); 8162 } 8163 res._range->second=extended_mult(std::max(std::abs(p1._range->second),std::abs(p1._range->first)),std::max(std::abs(p1._range->second),std::abs(p1._range->first))); 8164 } 8165 else { 8166 res._range = get_product_range(p1._range,p2._range); 8167 res._all_sign = sign_product(p1.get_all_sign(), p2.get_all_sign()); 8168 } 8169 if(res.is_quadratic()){res.update_quad_convexity();} 8170 if(p1._is_transposed){ 8171 res._range->first = extended_mult(res._range->first,(T2)p1._dim[0]); 8172 res._range->second = extended_mult(res._range->second,(T2)p1._dim[0]); 8173 } 8174 return res; 8175 } 8176 8177 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8178 func<T1> operator+(const param<T1>& p1, const param<T2>& p2){ 8179 func<T1> res; 8180 res.set_max_dim(p1,p2); 8181 if(p1.is_param() && p2.is_var()){ 8182 res.insert(true,unit<T1>(),p2); 8183 res.add_cst(p1); 8184 } 8185 else if(p2.is_param() && p1.is_var()){ 8186 res.insert(true,unit<T1>(),p1); 8187 res.add_cst(param<T1>(p2)); 8188 } 8189 else {//Both vars or both params 8190 res.insert(true,unit<T1>(),p1); 8191 res.insert(true,unit<T1>(),p2); 8192 } 8193 res._all_sign = sign_add(p1.get_all_sign(), p2.get_all_sign()); 8194 if(res.is_quadratic()){res.update_quad_convexity();} 8195 res._range = get_plus_range(p1._range,p2._range); 8196 return res; 8197 } 8198 8199 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8200 func<T2> operator+(const param<T1>& p1, const param<T2>& p2){ 8201 func<T2> res; 8202 res.set_max_dim(p1,p2); 8203 if(p1.is_param() && p2.is_var()){ 8204 res.insert(true,unit<T2>(),p2); 8205 res.add_cst(param<T2>(p1)); 8206 } 8207 else if(p2.is_param() && p1.is_var()){ 8208 res.insert(true,unit<T2>(),p1); 8209 res.add_cst(p2); 8210 } 8211 else {//Both vars or both params 8212 res.insert(true,unit<T2>(),p1); 8213 res.insert(true,unit<T2>(),p2); 8214 } 8215 res._all_sign = sign_add(p1.get_all_sign(), p2.get_all_sign()); 8216 if(res.is_quadratic()){res.update_quad_convexity();} 8217 res._range = get_plus_range(p1._range,p2._range); 8218 return res; 8219 } 8220 8221 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 8222 func<T1> operator-(const param<T1>& p1, const param<T2>& p2){ 8223 func<T1> res; 8224 res.set_max_dim(p1,p2); 8225 if(p1.is_param() && p2.is_var()){ 8226 res.insert(false,unit<T1>(),p2); 8227 res.add_cst(p1); 8228 } 8229 else if(p2.is_param() && p1.is_var()){ 8230 res.insert(true,unit<T1>(),p1); 8231 func<T1> newp(p2); 8232 newp.reverse_sign(); 8233 res.add_cst(newp); 8234 } 8235 else {//Both vars or both params 8236 res.insert(true,unit<T1>(),p1); 8237 res.insert(false,unit<T1>(),p2); 8238 } 8239 res._all_sign = sign_add(p1.get_all_sign(), reverse(p2.get_all_sign())); 8240 if(res.is_quadratic()){res.update_quad_convexity();} 8241 res._range = get_minus_range(p1._range, p2._range); 8242 return res; 8243 } 8244 8245 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 8246 func<T2> operator-(const param<T1>& p1, const param<T2>& p2){ 8247 func<T2> res; 8248 res.set_max_dim(p1,p2); 8249 if(p1.is_param() && p2.is_var()){ 8250 res.insert(false,unit<T2>(),p2); 8251 res.add_cst(param<T2>(p1)); 8252 } 8253 else if(p2.is_param() && p1.is_var()){ 8254 res.insert(true,unit<T2>(),p1); 8255 func<T2> newp(p2); 8256 newp.reverse_sign(); 8257 res.add_cst(newp); 8258 } 8259 else {//Both vars or both params 8260 res.insert(true,unit<T2>(),p1); 8261 res.insert(false,unit<T2>(),p2); 8262 } 8263 res._all_sign = sign_add(p1.get_all_sign(), reverse(p2.get_all_sign())); 8264 if(res.is_quadratic()){res.update_quad_convexity();} 8265 res._range = get_minus_range(p1._range,p2._range); 8266 return res; 8267 } 8268 8269 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8270 constant<T1> operator+(const constant<T1>& p1, const constant<T2>& p2){ 8271 constant<T1> res(p1); 8272 res.set_val(res.eval()+p2.eval()); 8273 return res; 8274 } 8275 8276 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8277 constant<T2> operator+(const constant<T1>& p1, const constant<T2>& p2){ 8278 constant<T2> res(p2); 8279 res.set_val(res.eval()+(T2)p1.eval()); 8280 return res; 8281 } 8282 8283 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8284 constant<T1> operator-(const constant<T1>& p1, const constant<T2>& p2){ 8285 constant<T1> res(p1); 8286 res.set_val(res.eval()-p2.eval()); 8287 return res; 8288 } 8289 8290 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8291 constant<T2> operator-(const constant<T1>& p1, const constant<T2>& p2){ 8292 constant<T2> res(p2); 8293 res.set_val((T2)p1.eval() - res.eval()); 8294 return res; 8295 } 8296 8297 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8298 constant<T1> operator*(const constant<T1>& p1, const constant<T2>& p2){ 8299 constant<T1> res(p1); 8300 res.set_val(res.eval()*p2.eval()); 8301 return res; 8302 } 8303 8304 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8305 constant<T2> operator*(const constant<T1>& p1, const constant<T2>& p2){ 8306 constant<T2> res(p2); 8307 res.set_val((T2)p1.eval() * res.eval()); 8308 return res; 8309 } 8310 8311 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8312 func<T1> operator/(const func<T1>& f1, const func<T2>& f2){ 8313 return func<T1>(f1)/=f2; 8314 } 8315 8316 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8317 func<T2> operator/(const func<T1>& f1, const func<T2>& f2){ 8318 return func<T2>(f1)/=f2; 8319 } 8320 8321 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8322 func<T1> operator/(const param<T1>& f1, const param<T2>& f2){ 8323 return func<T1>(f1)/=f2; 8324 } 8325 8326 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8327 func<T2> operator/(const param<T1>& f1, const param<T2>& f2){ 8328 return func<T2>(f1)/=f2; 8329 } 8330 8331 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8332 func<T1> operator/(const func<T1>& f1, const param<T2>& p2){ 8333 return func<T1>(f1)/=p2; 8334 } 8335 8336 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8337 func<T2> operator/(const func<T1>& f1, const param<T2>& p2){ 8338 return func<T2>(f1)/=p2; 8339 } 8340 8341 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8342 func<T1> operator/(T1 f1, const param<T2>& p2){ 8343 return func<T1>(f1)/=p2; 8344 } 8345 8346 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8347 func<T2> operator/(T1 f1, const param<T2>& p2){ 8348 return func<T2>(f1)/=p2; 8349 } 8350 8351 8352 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8353 func<T1> operator/(const param<T1>& p1, const func<T2>& f2){ 8354 return func<T1>(p1)/=f2; 8355 } 8356 8357 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8358 func<T2> operator/(const param<T1>& p1, const func<T2>& f2){ 8359 return func<T2>(p1)/=f2; 8360 } 8361 8362 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8363 func<T1> operator/(const func<T1>& f1, const constant<T2>& p2){ 8364 return func<T1>(f1)/=p2; 8365 } 8366 8367 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8368 func<T2> operator/(const func<T1>& f1, const constant<T2>& p2){ 8369 return func<T2>(f1)/=p2; 8370 } 8371 8372 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8373 func<T1> operator/(const constant<T1>& p1, const func<T2>& f2){ 8374 return func<T1>(p1)/=f2; 8375 } 8376 8377 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8378 func<T2> operator/(const constant<T1>& p1, const func<T2>& f2){ 8379 return func<T2>(p1)/=f2; 8380 } 8381 8382 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8383 func<T1> operator/(const func<T1>& f1, const T2 p2){ 8384 return func<T1>(f1)/=p2; 8385 } 8386 8387 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8388 func<T2> operator/(const func<T1>& f1, T2 p2){ 8389 return func<T2>(f1)/=p2; 8390 } 8391 8392 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8393 func<T1> operator/(const T1 p1, const func<T2>& f2){ 8394 return func<T1>(p1)/=f2; 8395 } 8396 8397 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8398 func<T2> operator/(const T1 p1, const func<T2>& f2){ 8399 return func<T2>(p1)/=f2; 8400 } 8401 8402 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8403 func<T1> operator/(const param<T1>& f1, const constant<T2>& p2){ 8404 return func<T1>(f1)/=p2; 8405 } 8406 8407 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8408 func<T2> operator/(const param<T1>& f1, const constant<T2>& p2){ 8409 return func<T2>(f1)/=p2; 8410 } 8411 8412 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8413 func<T1> operator/(const constant<T1>& p1, const param<T2>& f2){ 8414 return func<T1>(p1)/=f2; 8415 } 8416 8417 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8418 func<T2> operator/(const constant<T1>& p1, const param<T2>& f2){ 8419 return func<T2>(p1)/=f2; 8420 } 8421 8422 8423 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) <= sizeof(T1)>::type* = nullptr> 8424 constant<T1> operator/(const constant<T1>& p1, const constant<T2>& p2){ 8425 constant<T1> res(p1); 8426 res.set_val(res.eval()/p2.eval()); 8427 return res; 8428 } 8429 8430 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T1) < sizeof(T2)>::type* = nullptr> 8431 constant<T2> operator/(const constant<T1>& p1, const constant<T2>& p2){ 8432 constant<T2> res(p2); 8433 res.set_val((T2)p1.eval()/res.eval()); 8434 return res; 8435 } 8436 8437 8438 template<class T1> min(const constant<T1> & p1,const constant<T1> & p2)8439 constant<T1> min(const constant<T1>& p1,const constant<T1>& p2){ 8440 constant<T1> res(p1); 8441 res.set_val(gravity::min(res.eval(),p2.eval())); 8442 return res; 8443 } 8444 8445 template<class T1> max(const constant<T1> & p1,const constant<T1> & p2)8446 constant<T1> max(const constant<T1>& p1,const constant<T1>& p2){ 8447 constant<T1> res(p1); 8448 res.set_val(gravity::max(res.eval(),p2.eval())); 8449 return res; 8450 } 8451 8452 template<class T1> log(const constant<T1> & p1)8453 constant<T1> log(const constant<T1>& p1){ 8454 constant<T1> res(p1); 8455 res.set_val(log(res.eval())); 8456 return res; 8457 } 8458 8459 template<class T1> exp(const constant<T1> & p1)8460 constant<T1> exp(const constant<T1>& p1){ 8461 constant<T1> res(p1); 8462 res.set_val(exp(res.eval())); 8463 return res; 8464 } 8465 8466 template<class T1> sqrt(const constant<T1> & p1)8467 constant<T1> sqrt(const constant<T1>& p1){ 8468 constant<T1> res(p1); 8469 res.set_val(sqrt(res.eval())); 8470 return res; 8471 } 8472 8473 template<class T1> unit_step(const constant<T1> & p1)8474 constant<T1> unit_step(const constant<T1>& p1){ 8475 if(p1.is_non_positive()){ 8476 return zero<T1>(); 8477 } 8478 return unit<T1>(); 8479 } 8480 8481 template<class T1> df_abs(const constant<T1> & p1)8482 constant<T1> df_abs(const constant<T1>& p1){ 8483 if(p1.is_zero()){ 8484 return zero<T1>(); 8485 } 8486 if(p1.is_non_positive()){ 8487 return -1*unit<T1>(); 8488 } 8489 return unit<T1>(); 8490 } 8491 8492 template<class T1> cos(const constant<T1> & p1)8493 constant<T1> cos(const constant<T1>& p1){ 8494 constant<T1> res(p1); 8495 res.set_val(cos(res.eval())); 8496 return res; 8497 } 8498 8499 template<class T1> sin(const constant<T1> & p1)8500 constant<T1> sin(const constant<T1>& p1){ 8501 constant<T1> res(p1); 8502 res.set_val(sin(res.eval())); 8503 return res; 8504 } 8505 8506 template<class T1> tan(const constant<T1> & p1)8507 constant<T1> tan(const constant<T1>& p1){ 8508 constant<T1> res(p1); 8509 res.set_val(tan(res.eval())); 8510 return res; 8511 } 8512 8513 template<class T1> pow(const constant<T1> & p1,int exp)8514 constant<T1> pow(const constant<T1>& p1, int exp){ 8515 constant<T1> res(p1); 8516 res.set_val(std::pow(res.eval(),exp)); 8517 return res; 8518 } 8519 8520 /** 8521 Return the sign and curvature of unitary operator op on given range. 8522 @param[in] op Mathematical unitary operator, e.g., cos, sin, log, etc... 8523 @param[in] range, range of values we're interested in. 8524 @return a pair<Convexity,Sign> charachterizing the sign and the curvature of the operator op in the given range 8525 */ 8526 template<class T, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> get_sign_curvature(OperatorType op,pair<T,T> range)8527 pair<Convexity,Sign> get_sign_curvature(OperatorType op, pair<T,T> range){ 8528 pair<Convexity,Sign> res = {undet_,unknown_}; 8529 switch(op){ 8530 case id_:{// f(x) = x 8531 res.first = linear_; 8532 if(range.first>=0){ 8533 res.second = non_neg_; 8534 } 8535 if(range.first>0){ 8536 res.second = pos_; 8537 } 8538 if(range.second<=0){ 8539 res.second = non_pos_; 8540 } 8541 if(range.first<0){ 8542 res.second = neg_; 8543 } 8544 return res; 8545 } 8546 case cos_: 8547 return cos_sign_curvature(range); 8548 case sin_:{ 8549 range.first += pi/2.; 8550 range.second += pi/2.; 8551 return cos_sign_curvature(range); 8552 } 8553 case log_:{ 8554 res.first = concave_; 8555 if(range->first>=1){ 8556 res.second = non_neg_; 8557 if(range->first>1){ 8558 res.second = pos_; 8559 } 8560 } 8561 if(range->second<=1){ 8562 res.second = non_pos_; 8563 if(range->second<1){ 8564 res.second = neg_; 8565 } 8566 } 8567 return res; 8568 } 8569 case exp_:{ 8570 return {convex_,pos_}; 8571 } 8572 case sqrt_:{ 8573 res.first = concave_; 8574 res.second = non_neg_; 8575 if(range.first > 0){ 8576 res.second = pos_; 8577 } 8578 return res; 8579 } 8580 case tan_:{ 8581 if(range.first>=0){ 8582 res.first = convex_; 8583 res.second = non_neg_; 8584 } 8585 if(range.first>0){ 8586 res.first = convex_; 8587 res.second = pos_; 8588 } 8589 if(range.second<=0){ 8590 res.first = concave_; 8591 res.second = non_pos_; 8592 } 8593 if(range.second<0){ 8594 res.first = concave_; 8595 res.second = neg_; 8596 } 8597 return res; 8598 } 8599 case abs_:{ 8600 return {convex_,pos_}; 8601 } 8602 case relu_:{ 8603 res.first = convex_; 8604 res.second = non_neg_; 8605 if(range.first > 0){ 8606 res.second = pos_; 8607 } 8608 return res; 8609 } 8610 default: 8611 break; 8612 } 8613 return res; 8614 } 8615 8616 // template<class T> 8617 // func<T> min(const param<T>& p1, const param<T>& p2){ 8618 // func<T> res(bexpr<T>(min_, p1.copy(), p2.copy())); 8619 // res._all_sign = std::min(p1.get_all_sign(),p2.get_all_sign()); 8620 // res._all_convexity = undet_; 8621 // res._range->first = gravity::min(p1._range->first,p2._range->first); 8622 // res._range->second = gravity::min(p1._range->second,p2._range->second); 8623 // res._expr->_range->first = res._range->first; 8624 // res._expr->_range->second = res._range->second; 8625 // res._expr->_all_convexity = res._all_convexity; 8626 // res._expr->_all_sign = res._all_sign; 8627 // return res; 8628 // } 8629 // 8630 // template<class T> 8631 // func<T> max(const param<T>& p1, const param<T>& p2){ 8632 // func<T> res(bexpr<T>(max_, p1.copy(), p2.copy())); 8633 // res._all_sign = std::max(p1.get_all_sign(),p2.get_all_sign()); 8634 // res._all_convexity = undet_; 8635 // res._range->first = gravity::max(p1._range->first,p2._range->first); 8636 // res._range->second = gravity::max(p1._range->second,p2._range->second); 8637 // res._expr->_range->first = res._range->first; 8638 // res._expr->_range->second = res._range->second; 8639 // res._expr->_all_convexity = res._all_convexity; 8640 // res._expr->_all_sign = res._all_sign; 8641 // return res; 8642 // } 8643 // 8644 // template<class T> 8645 // func<T> min(const param<T>& p1, const func<T>& p2){ 8646 // func<T> res(bexpr<T>(min_, p1.copy(), p2.copy())); 8647 // res._all_sign = std::min(p1.get_all_sign(),p2.get_all_sign()); 8648 // res._all_convexity = undet_; 8649 // res._range->first = gravity::min(p1._range->first,p2._range->first); 8650 // res._range->second = gravity::min(p1._range->second,p2._range->second); 8651 // res._expr->_range->first = res._range->first; 8652 // res._expr->_range->second = res._range->second; 8653 // res._expr->_all_convexity = res._all_convexity; 8654 // res._expr->_all_sign = res._all_sign; 8655 // return res; 8656 // } 8657 // 8658 // template<class T> 8659 // func<T> min(const func<T>& p1, const param<T>& p2){ 8660 // func<T> res(bexpr<T>(min_, p1.copy(), p2.copy())); 8661 // res._all_sign = std::min(p1.get_all_sign(),p2.get_all_sign()); 8662 // res._all_convexity = undet_; 8663 // res._range->first = gravity::min(p1._range->first,p2._range->first); 8664 // res._range->second = gravity::min(p1._range->second,p2._range->second); 8665 // res._expr->_range->first = res._range->first; 8666 // res._expr->_range->second = res._range->second; 8667 // res._expr->_all_convexity = res._all_convexity; 8668 // res._expr->_all_sign = res._all_sign; 8669 // return res; 8670 // } 8671 // 8672 // template<class T> 8673 // func<T> max(const param<T>& p1, const func<T>& p2){ 8674 // func<T> res(bexpr<T>(max_, p1.copy(), p2.copy())); 8675 // res._all_sign = std::max(p1.get_all_sign(),p2.get_all_sign()); 8676 // res._all_convexity = undet_; 8677 // res._range->first = gravity::max(p1._range->first,p2._range->first); 8678 // res._range->second = gravity::max(p1._range->second,p2._range->second); 8679 // res._expr->_range->first = res._range->first; 8680 // res._expr->_range->second = res._range->second; 8681 // res._expr->_all_convexity = res._all_convexity; 8682 // res._expr->_all_sign = res._all_sign; 8683 // return res; 8684 // } 8685 // 8686 // template<class T> 8687 // func<T> max(const func<T>& p1, const param<T>& p2){ 8688 // func<T> res(bexpr<T>(max_, p1.copy(), p2.copy())); 8689 // res._all_sign = std::max(p1.get_all_sign(),p2.get_all_sign()); 8690 // res._all_convexity = undet_; 8691 // res._range->first = gravity::max(p1._range->first,p2._range->first); 8692 // res._range->second = gravity::max(p1._range->second,p2._range->second); 8693 // res._expr->_range->first = res._range->first; 8694 // res._expr->_range->second = res._range->second; 8695 // res._expr->_all_convexity = res._all_convexity; 8696 // res._expr->_all_sign = res._all_sign; 8697 // return res; 8698 // } 8699 // 8700 // template<class T> 8701 // func<T> max(const func<T>& p1, const func<T>& p2){ 8702 // func<T> res(bexpr<T>(max_, p1.copy(), p2.copy())); 8703 // res._all_sign = std::max(p1.get_all_sign(),p2.get_all_sign()); 8704 // res._all_convexity = undet_; 8705 // res._range->first = gravity::max(p1._range->first,p2._range->first); 8706 // res._range->second = gravity::max(p1._range->second,p2._range->second); 8707 // res._expr->_range->first = res._range->first; 8708 // res._expr->_range->second = res._range->second; 8709 // res._expr->_all_convexity = res._all_convexity; 8710 // res._expr->_all_sign = res._all_sign; 8711 // return res; 8712 // } 8713 // 8714 // template<class T> 8715 // func<T> min(const func<T>& p1, const func<T>& p2){ 8716 // func<T> res(bexpr<T>(min_, p1.copy(), p2.copy())); 8717 // res._all_sign = std::min(p1.get_all_sign(),p2.get_all_sign()); 8718 // res._all_convexity = undet_; 8719 // res._range->first = gravity::min(p1._range->first,p2._range->first); 8720 // res._range->second = gravity::min(p1._range->second,p2._range->second); 8721 // res._expr->_range->first = res._range->first; 8722 // res._expr->_range->second = res._range->second; 8723 // res._expr->_all_convexity = res._all_convexity; 8724 // res._expr->_all_sign = res._all_sign; 8725 // return res; 8726 // } 8727 8728 template<class T, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> log(const param<T> & p1)8729 func<T> log(const param<T>& p1){ 8730 if(!p1.is_positive()){ 8731 throw invalid_argument("Calling log() with a non-positive argument"); 8732 } 8733 func<T> res(uexpr<T>(log_, p1.copy())); 8734 if(p1._range->first>=1){ 8735 res._all_sign = non_neg_; 8736 if(p1._range->first>1){ 8737 res._all_sign = pos_; 8738 } 8739 } 8740 if(p1._range->second<=1){ 8741 res._all_sign = non_pos_; 8742 if(p1._range->second<1){ 8743 res._all_sign = neg_; 8744 } 8745 } 8746 if (p1.is_var()) { 8747 res._all_convexity = concave_; 8748 } 8749 res._range->first = std::log(p1._range->first); 8750 res._range->second = std::log(p1._range->second); 8751 res._expr->_range->first = res._range->first; 8752 res._expr->_range->second = res._range->second; 8753 res._expr->_all_convexity = res._all_convexity; 8754 res._expr->_all_sign = res._all_sign; 8755 return res; 8756 } 8757 8758 template<class T1> exp(const param<T1> & p1)8759 func<T1> exp(const param<T1>& p1){ 8760 func<T1> res(uexpr<T1>(exp_, p1.copy())); 8761 res._all_sign = pos_; 8762 if (p1.is_var()) { 8763 res._all_convexity = convex_; 8764 } 8765 res._range->first = std::exp(p1._range->first); 8766 res._range->second = std::exp(p1._range->second); 8767 res._expr->_range->first = res._range->first; 8768 res._expr->_range->second = res._range->second; 8769 res._expr->_all_convexity = res._all_convexity; 8770 res._expr->_all_sign = res._all_sign; 8771 return res; 8772 } 8773 8774 template<class T1> sqrt(const param<T1> & p1)8775 func<T1> sqrt(const param<T1>& p1){ 8776 if(!p1.is_non_negative()){ 8777 throw invalid_argument("Calling sqrt() with a negative argument"); 8778 } 8779 func<T1> res(uexpr<T1>(sqrt_, p1.copy())); 8780 res._all_sign = non_neg_; 8781 if(p1.is_positive()){ 8782 res._all_sign = pos_; 8783 } 8784 if (p1.is_var()) { 8785 res._all_convexity = concave_; 8786 } 8787 res._range->first = std::sqrt(p1._range->first); 8788 res._range->second = std::sqrt(p1._range->second); 8789 res._expr->_range->first = res._range->first; 8790 res._expr->_range->second = res._range->second; 8791 res._expr->_all_convexity = res._all_convexity; 8792 res._expr->_all_sign = res._all_sign; 8793 return res; 8794 } 8795 8796 template<class T, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> cos_sign_curvature(const pair<T,T> & range)8797 pair<Convexity,Sign> cos_sign_curvature(const pair<T,T>& range){ 8798 if(range.first==numeric_limits<T>::lowest() || range.second==numeric_limits<T>::max()){ 8799 return {undet_,unknown_}; 8800 } 8801 pair<Convexity,Sign> res = {undet_,zero_}; 8802 auto lb = fmod(range.first,(2*pi)); 8803 auto ub = fmod(range.second,(2*pi)); 8804 if(ub<= -3*pi/2){ 8805 res.first = concave_; 8806 res.second = non_neg_; 8807 if(ub< -3*pi/2){ 8808 res.second = pos_; 8809 } 8810 } 8811 if(lb>=-3*pi/2 && ub<= -pi/2){ 8812 res.first = convex_; 8813 res.second = non_pos_; 8814 if(lb>-3*pi/2 && ub<-pi/2){ 8815 res.second = neg_; 8816 } 8817 } 8818 if(lb>=-pi/2 && ub<= pi/2){ 8819 res.first = concave_; 8820 res.second = non_neg_; 8821 if(lb>-pi/2 && ub<pi/2){ 8822 res.second = pos_; 8823 } 8824 } 8825 if(lb>=pi/2 && ub<= 3*pi/2){ 8826 res.first = convex_; 8827 res.second = non_pos_; 8828 if(lb>pi/2 && ub<3*pi/2){ 8829 res.second = neg_; 8830 } 8831 } 8832 if(lb >= 3*pi/2){ 8833 res.first = concave_; 8834 res.second = non_neg_; 8835 if(lb > 3*pi/2){ 8836 res.second = pos_; 8837 } 8838 } 8839 return res; 8840 } 8841 8842 template<class T1> unit_step(const param<T1> & p1)8843 func<T1> unit_step(const param<T1>& p1){ 8844 func<T1> res(uexpr<T1>(unit_step_, p1.copy())); 8845 if(p1.is_non_positive()){ 8846 res._range->first = zero<T1>().eval(); 8847 res._range->second = zero<T1>().eval(); 8848 } 8849 else if(p1.is_positive()){ 8850 res._range->first = unit<T1>().eval(); 8851 res._range->second = unit<T1>().eval(); 8852 } 8853 else { 8854 res._range->first = zero<T1>().eval(); 8855 res._range->second = unit<T1>().eval(); 8856 } 8857 res._all_convexity = undet_; 8858 res._expr->_range->first = res._range->first; 8859 res._expr->_range->second = res._range->second; 8860 res._expr->_all_convexity = res._all_convexity; 8861 res._expr->_all_sign = res._all_sign; 8862 return res; 8863 } 8864 8865 template<class T1> df_abs(const param<T1> & p1)8866 func<T1> df_abs(const param<T1>& p1){ 8867 func<T1> res(uexpr<T1>(df_abs_, p1.copy())); 8868 if(p1.is_zero()){ 8869 res._range->first = zero<T1>().eval(); 8870 res._range->second = zero<T1>().eval(); 8871 } 8872 else if(p1.is_negative()){ 8873 res._range->first = -1*unit<T1>().eval(); 8874 res._range->second = -1*unit<T1>().eval(); 8875 } 8876 else if(p1.is_positive()){ 8877 res._range->first = unit<T1>().eval(); 8878 res._range->second = unit<T1>().eval(); 8879 } 8880 else { 8881 res._range->first = -1*unit<T1>().eval(); 8882 res._range->second = unit<T1>().eval(); 8883 } 8884 res._all_convexity = undet_; 8885 res._expr->_range->first = res._range->first; 8886 res._expr->_range->second = res._range->second; 8887 res._expr->_all_convexity = res._all_convexity; 8888 res._expr->_all_sign = res._all_sign; 8889 return res; 8890 } 8891 8892 template<class T, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> cos(const param<T> & p1)8893 func<T> cos(const param<T>& p1){ 8894 func<T> res(uexpr<T>(cos_, p1.copy())); 8895 auto conv_sign = cos_sign_curvature(*p1._range); 8896 if (p1.is_var()) { 8897 res._all_convexity = conv_sign.first; 8898 } 8899 res._all_sign = conv_sign.second; 8900 res._range->first = gravity::min(std::cos(p1._range->first),std::cos(p1._range->second)); 8901 res._range->second = gravity::max(std::cos(p1._range->first),std::cos(p1._range->second)); 8902 if(p1._range->first <0 && p1._range->second >0){ 8903 res._range->second = 1; 8904 } 8905 if((p1._range->first <-pi && p1._range->second >-pi) || (p1._range->first <pi && p1._range->second >pi)){ 8906 res._range->first = -1; 8907 } 8908 res._expr->_range->first = res._range->first; 8909 res._expr->_range->second = res._range->second; 8910 res._expr->_all_convexity = res._all_convexity; 8911 res._expr->_all_sign = res._all_sign; 8912 res._indices = p1._indices; 8913 return res; 8914 } 8915 8916 template<class T, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> sin(const param<T> & p1)8917 func<T> sin(const param<T>& p1){ 8918 func<T> res(uexpr<T>(sin_, p1.copy())); 8919 auto shifted_range = *p1._range; 8920 shifted_range.first += pi/2.; 8921 shifted_range.second += pi/2.; 8922 auto conv_sign = cos_sign_curvature(shifted_range); 8923 if (p1.is_var()) { 8924 res._all_convexity = conv_sign.first; 8925 } 8926 res._all_sign = conv_sign.second; 8927 res._range->first = gravity::min(std::sin(p1._range->first),std::sin(p1._range->second)); 8928 res._range->second = gravity::max(std::sin(p1._range->first),std::sin(p1._range->second)); 8929 if(shifted_range.first <0 && shifted_range.second >0){ 8930 res._range->second = 1; 8931 } 8932 if((shifted_range.first <-pi && shifted_range.second >-pi) || (shifted_range.first <pi && shifted_range.second >pi)){ 8933 res._range->first = -1; 8934 } 8935 res._expr->_range->first = res._range->first; 8936 res._expr->_range->second = res._range->second; 8937 res._expr->_all_convexity = res._all_convexity; 8938 res._expr->_all_sign = res._all_sign; 8939 res._indices = p1._indices; 8940 return res; 8941 } 8942 8943 template<class T, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> tan(const param<T> & p1)8944 func<T> tan(const param<T>& p1){ 8945 auto centered_range = *p1._range; 8946 centered_range.first= fmod(centered_range.first, 2*pi); 8947 centered_range.second=fmod(centered_range.second, 2*pi); 8948 if(centered_range.first<=-pi/2 || centered_range.second>=pi/2){ 8949 throw invalid_argument("Calling tan() with discontinuous domain"); 8950 } 8951 func<T> res(uexpr<T>(tan_, p1.copy())); 8952 if(centered_range.first>=0){ 8953 if (p1.is_var()) { 8954 res._all_convexity = convex_; 8955 } 8956 res._all_sign = non_neg_; 8957 if(centered_range.first>0){ 8958 res._all_sign = pos_; 8959 } 8960 } 8961 if(centered_range.second<=0){ 8962 if (p1.is_var()) { 8963 res._all_convexity = concave_; 8964 } 8965 res._all_sign = non_pos_; 8966 if(centered_range.first>0){ 8967 res._all_sign = neg_; 8968 } 8969 } 8970 res._range->first = std::tan(p1._range->first); 8971 res._range->second = std::tan(p1._range->second); 8972 res._expr->_range->first = res._range->first; 8973 res._expr->_range->second = res._range->second; 8974 res._expr->_all_convexity = res._all_convexity; 8975 res._expr->_all_sign = res._all_sign; 8976 res._indices = p1._indices; 8977 return res; 8978 } 8979 8980 template<class T1> ReLU(const param<T1> & p1)8981 func<T1> ReLU(const param<T1>& p1){ 8982 func<T1> res(uexpr<T1>(relu_, p1.copy())); 8983 if (p1.is_var()) { 8984 res._all_convexity = convex_; 8985 } 8986 res._all_sign = non_neg_; 8987 res._range->first = zero<T1>().eval(); 8988 if(p1.is_positive()){ 8989 res._all_sign = pos_; 8990 res._range->first = p1._range->first; 8991 } 8992 res._range->second = gravity::max(zero<T1>().eval(),p1._range->second); 8993 res._expr->_range->first = res._range->first; 8994 res._expr->_range->second = res._range->second; 8995 res._expr->_all_convexity = res._all_convexity; 8996 res._expr->_all_sign = res._all_sign; 8997 res._indices = p1._indices; 8998 return res; 8999 } 9000 9001 template<class T1> abs(const param<T1> & p1)9002 func<T1> abs(const param<T1>& p1){ 9003 func<T1> res(uexpr<T1>(abs_, p1.copy())); 9004 if (p1.is_var()) { 9005 res._all_convexity = convex_; 9006 } 9007 res._all_sign = non_neg_; 9008 res._range->first = zero<T1>().eval(); 9009 if(p1.is_positive()){ 9010 res._all_sign = pos_; 9011 res._range->first = p1._range->first; 9012 } 9013 res._range->second = gravity::max(zero<T1>().eval(),p1._range->second); 9014 res._expr->_range->first = res._range->first; 9015 res._expr->_range->second = res._range->second; 9016 res._expr->_all_convexity = res._all_convexity; 9017 res._expr->_all_sign = res._all_sign; 9018 res._indices = p1._indices; 9019 return res; 9020 } 9021 9022 template<class T> pow(const param<T> & p1,int exp)9023 func<T> pow(const param<T>& p1, int exp){ 9024 if(exp<0){ 9025 func<T> res; 9026 if(!p1.is_negative() && !p1.is_positive()){ 9027 throw invalid_argument("Calling pow() with a negative exponent on an argument that can be zero"); 9028 } 9029 res.insert(p1,exp); 9030 return res; 9031 } 9032 if(exp==0){ 9033 return func<T>(); 9034 } 9035 if(exp==1){ 9036 return func<T>(p1); 9037 } 9038 if(exp==2){ 9039 return p1*p1; 9040 } 9041 else { 9042 func<T> res; 9043 res.insert(p1,exp); 9044 res.set_max_dim(p1); 9045 res._range->first = gravity::min(std::pow(p1._range->first,exp),std::pow(p1._range->second,exp)); 9046 res._range->second = gravity::max(std::pow(p1._range->first,exp),std::pow(p1._range->second,exp)); 9047 if(exp%2==0) { 9048 res._all_sign = non_neg_; 9049 if(p1.is_positive()){ 9050 res._all_sign = pos_; 9051 } 9052 if(p1._range->first <0 && p1._range->second >0){ 9053 res._range->first = 0; 9054 } 9055 } 9056 else { 9057 res._all_sign = p1.get_all_sign(); 9058 } 9059 if (p1.is_var()) { 9060 if(exp%2==0) { 9061 res._all_convexity = convex_; 9062 } 9063 else if(p1.is_non_negative()){ 9064 res._all_convexity = convex_; 9065 } 9066 else if(p1.is_non_positive()){ 9067 res._all_convexity = concave_; 9068 } 9069 else { 9070 res._all_convexity = undet_; 9071 } 9072 } 9073 res._indices = p1._indices; 9074 return res; 9075 } 9076 } 9077 9078 9079 template<class T1> log(const func<T1> & f)9080 func<T1> log(const func<T1>& f){ 9081 if(!f.is_positive()){ 9082 throw invalid_argument("Calling log() with a potentially negative/zero argument"); 9083 } 9084 func<T1> res(uexpr<T1>(log_, f.copy())); 9085 if(f._range->first>=1){ 9086 res._all_sign = non_neg_; 9087 if(f._range->first>1){ 9088 res._all_sign = pos_; 9089 } 9090 } 9091 if(f._range->second<=1){ 9092 res._all_sign = non_pos_; 9093 if(f._range->second<1){ 9094 res._all_sign = neg_; 9095 } 9096 } 9097 if (f.is_linear()) { 9098 res._all_convexity = concave_; 9099 } 9100 else if(!f.is_constant()){ 9101 res._all_convexity = undet_; 9102 } 9103 res._range->first = std::log(f._range->first); 9104 if(f._range->second==numeric_limits<T1>::max()){ 9105 res._range->second = numeric_limits<T1>::max(); 9106 } 9107 else { 9108 res._range->second = std::log(f._range->second); 9109 } 9110 res._expr->_range->first = res._range->first; 9111 res._expr->_range->second = res._range->second; 9112 res._expr->_all_convexity = res._all_convexity; 9113 res._expr->_all_sign = res._all_sign; 9114 res._indices = f._indices; 9115 return res; 9116 } 9117 9118 template<class T1> exp(const func<T1> & f)9119 func<T1> exp(const func<T1>& f){ 9120 func<T1> res(uexpr<T1>(exp_, f.copy())); 9121 res._all_sign = pos_; 9122 if (f.is_linear()) { 9123 res._all_convexity = convex_; 9124 } 9125 else if(!f.is_constant()){ 9126 res._all_convexity = undet_; 9127 } 9128 if(f._range->first==numeric_limits<T1>::lowest() || f._range->second==numeric_limits<T1>::max()){ 9129 res._range->first = numeric_limits<T1>::lowest(); 9130 res._range->second = numeric_limits<T1>::max(); 9131 } 9132 else { 9133 res._range->first = std::exp(f._range->first); 9134 res._range->second = std::exp(f._range->second); 9135 } 9136 res._expr->_range->first = res._range->first; 9137 res._expr->_range->second = res._range->second; 9138 res._expr->_all_convexity = res._all_convexity; 9139 res._expr->_all_sign = res._all_sign; 9140 res._indices = f._indices; 9141 return res; 9142 } 9143 9144 template<class T1> sqrt(const func<T1> & f)9145 func<T1> sqrt(const func<T1>& f){ 9146 if(!f.is_non_negative()){ 9147 throw invalid_argument("Calling sqrt() with a potentially negative argument"); 9148 } 9149 func<T1> res(uexpr<T1>(sqrt_, f.copy())); 9150 res._all_sign = non_neg_; 9151 if(f.is_positive()){ 9152 res._all_sign = pos_; 9153 } 9154 if (f.is_linear()) { 9155 res._all_convexity = convex_; 9156 } 9157 else if(!f.is_constant()){ 9158 res._all_convexity = undet_; 9159 } 9160 res._range->first = std::sqrt(f._range->first); 9161 if(f._range->second==numeric_limits<T1>::max()){ 9162 res._range->second = numeric_limits<T1>::max(); 9163 } 9164 else { 9165 res._range->second = std::sqrt(f._range->second); 9166 } 9167 res._expr->_range->first = res._range->first; 9168 res._expr->_range->second = res._range->second; 9169 res._expr->_all_convexity = res._all_convexity; 9170 res._expr->_all_sign = res._all_sign; 9171 res._indices = f._indices; 9172 return res; 9173 } 9174 9175 template<class T, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> asin(const func<T> & f)9176 func<T> asin(const func<T>& f){ 9177 if(f._range->first<-1 || f._range->second>1){ 9178 throw invalid_argument("Calling asin(const func<T1>& f) outside [-1,1]"); 9179 } 9180 func<T> res(uexpr<T>(asin_, f.copy())); 9181 if (f.is_linear()) { 9182 if(f.is_non_positive()){ 9183 res._all_convexity = concave_; 9184 } 9185 else if(f.is_non_negative()){ 9186 res._all_convexity = convex_; 9187 } 9188 } 9189 else if(!f.is_constant()){ 9190 res._all_convexity = undet_; 9191 } 9192 res._all_sign = f._all_sign; 9193 res._range->first = std::asin(f._range->first); 9194 res._range->second = std::asin(f._range->second); 9195 res._expr->_range->first = res._range->first; 9196 res._expr->_range->second = res._range->second; 9197 res._expr->_all_convexity = res._all_convexity; 9198 res._expr->_all_sign = res._all_sign; 9199 res._indices = f._indices; 9200 return res; 9201 } 9202 9203 template<class T> acos(const param<T> & p)9204 func<T> acos(const param<T>& p){ 9205 return acos(func<T>(p)); 9206 } 9207 9208 template<class T> acos(const var<T> & p)9209 func<T> acos(const var<T>& p){ 9210 return acos(func<T>(p)); 9211 } 9212 9213 template<class T> asin(const param<T> & p)9214 func<T> asin(const param<T>& p){ 9215 return acos(func<T>(p)); 9216 } 9217 9218 template<class T> asin(const var<T> & p)9219 func<T> asin(const var<T>& p){ 9220 return acos(func<T>(p)); 9221 } 9222 9223 template<class T, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> acos(const func<T> & f)9224 func<T> acos(const func<T>& f){ 9225 if(f._range->first<-1 || f._range->second>1){ 9226 throw invalid_argument("Calling acos(const func<T1>& f) outside [-1,1]"); 9227 } 9228 func<T> res(uexpr<T>(acos_, f.copy())); 9229 if (f.is_linear()) { 9230 if(f.is_non_positive()){ 9231 res._all_convexity = convex_; 9232 } 9233 else if(f.is_non_negative()){ 9234 res._all_convexity = concave_; 9235 } 9236 } 9237 else if(!f.is_constant()){ 9238 res._all_convexity = undet_; 9239 } 9240 res._all_sign = non_neg_; 9241 res._range->first = std::acos(f._range->second); 9242 res._range->second = std::acos(f._range->first); 9243 res._expr->_range->first = res._range->first; 9244 res._expr->_range->second = res._range->second; 9245 res._expr->_all_convexity = res._all_convexity; 9246 res._expr->_all_sign = res._all_sign; 9247 res._indices = f._indices; 9248 return res; 9249 } 9250 9251 template<class T, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> cos(const func<T> & f)9252 func<T> cos(const func<T>& f){ 9253 func<T> res(uexpr<T>(cos_, f.copy())); 9254 auto conv_sign = cos_sign_curvature(*f._range); 9255 if (f.is_linear()) { 9256 res._all_convexity = conv_sign.first; 9257 } 9258 else if(!f.is_constant()){ 9259 res._all_convexity = undet_; 9260 } 9261 res._all_sign = conv_sign.second; 9262 if(f._range->first==numeric_limits<T>::lowest() || f._range->second==numeric_limits<T>::max()){ 9263 res._range->first = -1; 9264 res._range->second = 1; 9265 } 9266 else { 9267 res._range->first = gravity::min(std::cos(f._range->first),std::cos(f._range->second)); 9268 res._range->second = gravity::max(std::cos(f._range->first),std::cos(f._range->second)); 9269 } 9270 if(f._range->first <0 && f._range->second >0){ 9271 res._range->second = 1; 9272 } 9273 if((f._range->first <-pi && f._range->second >-pi) || (f._range->first <pi && f._range->second >pi)){ 9274 res._range->first = -1; 9275 } 9276 res._expr->_range->first = res._range->first; 9277 res._expr->_range->second = res._range->second; 9278 res._expr->_all_convexity = res._all_convexity; 9279 res._expr->_all_sign = res._all_sign; 9280 res._indices = f._indices; 9281 return res; 9282 } 9283 9284 template<class T, typename enable_if<is_same<T,Cpx>::value>::type* = nullptr> acos(const func<T> & f)9285 func<T> acos(const func<T>& f){ 9286 func<T> res(uexpr<T>(acos_, f.copy())); 9287 res._all_convexity = undet_; 9288 res._expr->_range->first = res._range->first; 9289 res._expr->_range->second = res._range->second; 9290 res._expr->_all_convexity = res._all_convexity; 9291 res._expr->_all_sign = res._all_sign; 9292 res._indices = f._indices; 9293 return res; 9294 } 9295 9296 template<class T, typename enable_if<is_same<T,Cpx>::value>::type* = nullptr> asin(const func<T> & f)9297 func<T> asin(const func<T>& f){ 9298 func<T> res(uexpr<T>(asin_, f.copy())); 9299 res._all_convexity = undet_; 9300 res._expr->_range->first = res._range->first; 9301 res._expr->_range->second = res._range->second; 9302 res._expr->_all_convexity = res._all_convexity; 9303 res._expr->_all_sign = res._all_sign; 9304 res._indices = f._indices; 9305 return res; 9306 } 9307 9308 template<class T, typename enable_if<is_same<T,Cpx>::value>::type* = nullptr> cos(const func<T> & f)9309 func<T> cos(const func<T>& f){ 9310 func<T> res(uexpr<T>(cos_, f.copy())); 9311 res._all_convexity = undet_; 9312 res._expr->_range->first = res._range->first; 9313 res._expr->_range->second = res._range->second; 9314 res._expr->_all_convexity = res._all_convexity; 9315 res._expr->_all_sign = res._all_sign; 9316 res._indices = f._indices; 9317 return res; 9318 } 9319 9320 template<class T, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> sin(const func<T> & f)9321 func<T> sin(const func<T>& f){ 9322 func<T> res(uexpr<T>(sin_, f.copy())); 9323 auto shifted_range = *f._range; 9324 auto conv_sign = cos_sign_curvature(shifted_range); 9325 if (f.is_linear()) { 9326 res._all_convexity = conv_sign.first; 9327 } 9328 else if(!f.is_constant()){ 9329 res._all_convexity = undet_; 9330 } 9331 res._all_sign = conv_sign.second; 9332 if(f._range->first==numeric_limits<T>::lowest() || f._range->second==numeric_limits<T>::max()){ 9333 res._range->first = -1; 9334 res._range->second = 1; 9335 } 9336 else { 9337 res._range->first = gravity::min(std::sin(f._range->first),std::sin(f._range->second)); 9338 res._range->second = gravity::max(std::sin(f._range->first),std::sin(f._range->second)); 9339 shifted_range.first += pi/2.; 9340 shifted_range.second += pi/2.; 9341 if(shifted_range.first <0 && shifted_range.second >0){ 9342 res._range->second = 1; 9343 } 9344 if((shifted_range.first <-pi && shifted_range.second >-pi) || (shifted_range.first <pi && shifted_range.second >pi)){ 9345 res._range->first = -1; 9346 } 9347 } 9348 res._expr->_range->first = res._range->first; 9349 res._expr->_range->second = res._range->second; 9350 res._expr->_all_convexity = res._all_convexity; 9351 res._expr->_all_sign = res._all_sign; 9352 res._indices = f._indices; 9353 return res; 9354 } 9355 9356 template<class T, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> atan2(const param<T> & f1,const param<T> & f2)9357 func<T> atan2(const param<T>& f1, const param<T>& f2){ 9358 return atan2(func<T>(f1), func<T>(f2)); 9359 } 9360 9361 template<class T, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> atan2(const func<T> & f1,const func<T> & f2)9362 func<T> atan2(const func<T>& f1, const func<T>& f2){ 9363 func<T> res(bexpr<T>(atan2_, f1.copy(), f2.copy())); 9364 res._all_convexity = undet_; 9365 res._all_sign = unknown_; 9366 res._expr->_range->first = res._range->first; 9367 res._expr->_range->second = res._range->second; 9368 res._expr->_all_convexity = res._all_convexity; 9369 res._expr->_all_sign = res._all_sign; 9370 return res; 9371 } 9372 9373 template<class T, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> atan(const func<T> & f)9374 func<T> atan(const func<T>& f){ 9375 // if(f._range->first<=-pi/2 || f._range->second>=pi/2){ 9376 // throw invalid_argument("Calling atan(const func<T1>& f) outside ]-pi/2,pi/2["); 9377 // } 9378 func<T> res(uexpr<T>(atan_, f.copy())); 9379 if (f.is_linear()) { 9380 if(f.is_non_positive()){ 9381 res._all_convexity = convex_; 9382 } 9383 else if(f.is_non_negative()){ 9384 res._all_convexity = concave_; 9385 } 9386 } 9387 else if(!f.is_constant()){ 9388 res._all_convexity = undet_; 9389 } 9390 res._all_sign = f._all_sign; 9391 res._range->first = std::atan(f._range->second); 9392 res._range->second = std::atan(f._range->first); 9393 res._expr->_range->first = res._range->first; 9394 res._expr->_range->second = res._range->second; 9395 res._expr->_all_convexity = res._all_convexity; 9396 res._expr->_all_sign = res._all_sign; 9397 res._indices = f._indices; 9398 return res; 9399 } 9400 9401 template<class T, typename enable_if<is_same<T,Cpx>::value>::type* = nullptr> atan(const func<T> & f)9402 func<T> atan(const func<T>& f){ 9403 // if(f._range->first<=-pi/2 || f._range->second>=pi/2){ 9404 // throw invalid_argument("Calling atan(const func<T1>& f) outside ]-pi/2,pi/2["); 9405 // } 9406 func<T> res(uexpr<T>(atan_, f.copy())); 9407 if (f.is_linear()) { 9408 if(f.is_non_positive()){ 9409 res._all_convexity = convex_; 9410 } 9411 else if(f.is_non_negative()){ 9412 res._all_convexity = concave_; 9413 } 9414 } 9415 else if(!f.is_constant()){ 9416 res._all_convexity = undet_; 9417 } 9418 res._all_sign = f._all_sign; 9419 res._range->first = std::atan(f._range->second); 9420 res._range->second = std::atan(f._range->first); 9421 res._expr->_range->first = res._range->first; 9422 res._expr->_range->second = res._range->second; 9423 res._expr->_all_convexity = res._all_convexity; 9424 res._expr->_all_sign = res._all_sign; 9425 res._indices = f._indices; 9426 return res; 9427 } 9428 9429 template<class T, typename enable_if<is_same<T,Cpx>::value>::type* = nullptr> sin(const func<T> & f)9430 func<T> sin(const func<T>& f){ 9431 func<T> res(uexpr<T>(sin_, f.copy())); 9432 res._all_convexity = undet_; 9433 res._expr->_range->first = res._range->first; 9434 res._expr->_range->second = res._range->second; 9435 res._expr->_all_convexity = res._all_convexity; 9436 res._expr->_all_sign = res._all_sign; 9437 res._indices = f._indices; 9438 return res; 9439 } 9440 9441 template<class T, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> tan(const func<T> & f)9442 func<T> tan(const func<T>& f){ 9443 func<T> res(uexpr<T>(tan_, f.copy())); 9444 auto centered_range = *f._range; 9445 if(f._range->first==numeric_limits<T>::lowest() || f._range->second==numeric_limits<T>::max()){ 9446 throw invalid_argument("Calling tan(const func<T1>& f) with discontinuous domain"); 9447 } 9448 centered_range.first %= 2*pi; 9449 centered_range.second %= 2*pi; 9450 if(centered_range.first<=-pi/2 || centered_range.second>=pi/2){ 9451 throw invalid_argument("Calling tan(const func<T1>& f) with discontinuous domain"); 9452 } 9453 if(centered_range.first>=0){ 9454 if (f.is_linear()) { 9455 res._all_convexity = convex_; 9456 } 9457 else if(!f.is_constant()){ 9458 res._all_convexity = undet_; 9459 } 9460 res._all_sign = non_neg_; 9461 if(centered_range.first>0){ 9462 res._all_sign = pos_; 9463 } 9464 } 9465 if(centered_range.second<=0){ 9466 if (f.is_linear()) { 9467 res._all_convexity = concave_; 9468 } 9469 else if(!f.is_constant()){ 9470 res._all_convexity = undet_; 9471 } 9472 res._all_sign = non_pos_; 9473 if(centered_range.first>0){ 9474 res._all_sign = neg_; 9475 } 9476 } 9477 res._range->first = std::tan(f._range->first); 9478 res._range->second = std::tan(f._range->second); 9479 res._expr->_range->first = res._range->first; 9480 res._expr->_range->second = res._range->second; 9481 res._expr->_all_convexity = res._all_convexity; 9482 res._expr->_all_sign = res._all_sign; 9483 res._indices = f._indices; 9484 return res; 9485 } 9486 template<class T1> unit_step(const func<T1> & f)9487 func<T1> unit_step(const func<T1>& f){ 9488 func<T1> res(uexpr<T1>(unit_step_, f.copy())); 9489 if(f.is_non_positive()){ 9490 res._range->first = zero<T1>().eval(); 9491 res._range->second = zero<T1>().eval(); 9492 } 9493 else if(f.is_positive()){ 9494 res._range->first = unit<T1>().eval(); 9495 res._range->second = unit<T1>().eval(); 9496 } 9497 else { 9498 res._range->first = zero<T1>().eval(); 9499 res._range->second = unit<T1>().eval(); 9500 } 9501 res._all_convexity = undet_; 9502 res._expr->_range->first = res._range->first; 9503 res._expr->_range->second = res._range->second; 9504 res._expr->_all_convexity = res._all_convexity; 9505 res._expr->_all_sign = res._all_sign; 9506 res._indices = f._indices; 9507 return res; 9508 } 9509 9510 template<class T1> df_abs(const func<T1> & f)9511 func<T1> df_abs(const func<T1>& f){ 9512 func<T1> res(uexpr<T1>(df_abs_, f.copy())); 9513 if(f.is_zero()){ 9514 res._range->first = zero<T1>().eval(); 9515 res._range->second = zero<T1>().eval(); 9516 } 9517 else if(f.is_negative()){ 9518 res._range->first = -1*unit<T1>().eval(); 9519 res._range->second = -1*unit<T1>().eval(); 9520 } 9521 else if(f.is_positive()){ 9522 res._range->first = unit<T1>().eval(); 9523 res._range->second = unit<T1>().eval(); 9524 } 9525 else { 9526 res._range->first = -1*unit<T1>().eval(); 9527 res._range->second = unit<T1>().eval(); 9528 } 9529 res._all_convexity = undet_; 9530 res._expr->_range->first = res._range->first; 9531 res._expr->_range->second = res._range->second; 9532 res._expr->_all_convexity = res._all_convexity; 9533 res._expr->_all_sign = res._all_sign; 9534 res._indices = f._indices; 9535 return res; 9536 } 9537 9538 template<class T1> ReLU(const func<T1> & f)9539 func<T1> ReLU(const func<T1>& f){ 9540 func<T1> res(uexpr<T1>(relu_, f.copy())); 9541 if (f.is_linear()) { 9542 res._all_convexity = convex_; 9543 } 9544 else if(!f.is_constant()){ 9545 res._all_convexity = undet_; 9546 } 9547 res._all_sign = non_neg_; 9548 res._range->first = zero<T1>().eval(); 9549 if(f.is_positive()){ 9550 res._all_sign = pos_; 9551 res._range->first = f._range->first; 9552 } 9553 res._range->second = gravity::max(zero<T1>().eval(),f._range->second); 9554 res._expr->_range->first = res._range->first; 9555 res._expr->_range->second = res._range->second; 9556 res._expr->_all_convexity = res._all_convexity; 9557 res._expr->_all_sign = res._all_sign; 9558 res._indices = f._indices; 9559 return res; 9560 } 9561 9562 template<class T1> abs(const func<T1> & f)9563 func<T1> abs(const func<T1>& f){ 9564 func<T1> res(uexpr<T1>(abs_, f.copy())); 9565 if (f.is_linear()) { 9566 res._all_convexity = convex_; 9567 } 9568 else if(!f.is_constant()){ 9569 res._all_convexity = undet_; 9570 } 9571 res._all_sign = non_neg_; 9572 res._range->first = zero<T1>().eval(); 9573 if(f.is_positive()){ 9574 res._all_sign = pos_; 9575 res._range->first = f._range->first; 9576 } 9577 res._range->second = gravity::max(zero<T1>().eval(),f._range->second); 9578 res._expr->_range->first = res._range->first; 9579 res._expr->_range->second = res._range->second; 9580 res._expr->_all_convexity = res._all_convexity; 9581 res._expr->_all_sign = res._all_sign; 9582 res._indices = f._indices; 9583 return res; 9584 } 9585 9586 template<class T, typename enable_if<is_arithmetic<T>::value>::type* = nullptr> pow(const func<T> & f,int exp)9587 func<T> pow(const func<T>& f, int exp){ 9588 if(exp<0){ 9589 return func<T>(bexpr<T>(power_, f.copy(), make_shared<constant<int>>(exp))); 9590 } 9591 if(exp==0){ 9592 return func<T>(); 9593 } 9594 if(exp==1){ 9595 return f; 9596 } 9597 // if(exp==2){ 9598 // return f*f; 9599 // } 9600 else { 9601 func<T> res(f); 9602 for (int i = 1; i < exp; i++) { 9603 res *= f; 9604 } 9605 if(f._range->first==numeric_limits<T>::lowest() || f._range->second==numeric_limits<T>::max()){ 9606 res._range->first = numeric_limits<T>::lowest(); 9607 res._range->second = numeric_limits<T>::max(); 9608 } 9609 else { 9610 res._range->first = gravity::min(std::pow(f._range->first,exp),std::pow(f._range->second,exp)); 9611 res._range->second = gravity::max(std::pow(f._range->first,exp),std::pow(f._range->second,exp)); 9612 } 9613 if(exp%2==0) { 9614 res._all_sign = non_neg_; 9615 if(f.is_positive()){ 9616 res._all_sign = pos_; 9617 } 9618 if(f._range->first <0 && f._range->second >0){ 9619 res._range->first = 0; 9620 } 9621 } 9622 else { 9623 res._all_sign = f.get_all_sign(); 9624 } 9625 if (f.is_linear()) { 9626 if(exp%2==0) { 9627 res._all_convexity = convex_; 9628 } 9629 else if(f.is_non_negative()){ 9630 res._all_convexity = convex_; 9631 } 9632 else if(f.is_non_positive()){ 9633 res._all_convexity = concave_; 9634 } 9635 else { 9636 res._all_convexity = undet_; 9637 } 9638 } 9639 else if(!f.is_constant()){ 9640 res._all_convexity = undet_; 9641 } 9642 res._indices = f._indices; 9643 return res; 9644 } 9645 } 9646 9647 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9648 func<T1> operator-(T1 p, const param<T2>& v){ 9649 return constant<T1>(p) - v; 9650 } 9651 9652 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9653 func<T2> operator-(T1 p, const param<T2>& v){ 9654 return constant<T2>(p) - v; 9655 } 9656 9657 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9658 func<T1> operator-(const param<T1>& v, T2 p){ 9659 return v - constant<T1>(p); 9660 } 9661 9662 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9663 func<T2> operator-(const param<T1>& v, T2 p){ 9664 return v - constant<T2>(p); 9665 } 9666 9667 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9668 func<T1> operator+(T1 p, const param<T2>& v){ 9669 return constant<T1>(p) + v; 9670 } 9671 9672 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9673 func<T2> operator+(T1 p, const param<T2>& v){ 9674 return constant<T2>(p) + v; 9675 } 9676 9677 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9678 func<T1> operator+(const param<T1>& v, T2 p){ 9679 return v + constant<T1>(p); 9680 } 9681 9682 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9683 func<T2> operator+(const param<T1>& v, T2 p){ 9684 return v + constant<T2>(p); 9685 } 9686 9687 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9688 func<T1> operator-(const constant<T1>& p, const param<T2>& v){ 9689 func<T1> res(v); 9690 res.reverse_sign(); 9691 res.add_cst(p); 9692 res._range = get_minus_range(p.range(),v._range); 9693 res.update_all_sign(); 9694 return res; 9695 } 9696 9697 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9698 func<T2> operator-(const constant<T1>& p, const param<T2>& v){ 9699 func<T2> res(v); 9700 res.reverse_sign(); 9701 res.add_cst(p); 9702 res._range = get_minus_range(p.range(),v._range); 9703 res.update_all_sign(); 9704 return res; 9705 } 9706 9707 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9708 func<T1> operator-(const param<T1>& v, const constant<T2>& p){ 9709 func<T1> res(v); 9710 func<T1> newp = constant<T1>(p); 9711 newp.reverse_sign(); 9712 res.add_cst(newp); 9713 res._range = get_minus_range(v._range,p.range()); 9714 res.update_all_sign(); 9715 return res; 9716 } 9717 9718 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9719 func<T2> operator-(const param<T1>& v, const constant<T2>& p){ 9720 func<T2> res(v); 9721 func<T2> newp(p); 9722 newp.reverse_sign(); 9723 res.add_cst(newp); 9724 res._range = get_minus_range(v._range,p.range()); 9725 res.update_all_sign(); 9726 return res; 9727 } 9728 9729 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9730 func<T1> operator+(const constant<T1>& p, const param<T2>& v){ 9731 func<T1> res(v); 9732 res.add_cst(p); 9733 res._range = get_plus_range(v._range,p.range()); 9734 res.update_all_sign(); 9735 return res; 9736 } 9737 9738 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9739 func<T2> operator+(const constant<T1>& p, const param<T2>& v){ 9740 func<T2> res(v); 9741 res.add_cst(p); 9742 res._range = get_plus_range(v._range,p.range()); 9743 res.update_all_sign(); 9744 return res; 9745 } 9746 9747 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9748 func<T1> operator+(const param<T1>& v, const constant<T2>& p){ 9749 func<T1> res(v); 9750 res.add_cst(p); 9751 res._range = get_plus_range(v._range,p.range()); 9752 res.update_all_sign(); 9753 return res; 9754 } 9755 9756 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9757 func<T2> operator+(const param<T1>& v, const constant<T2>& p){ 9758 func<T2> res(v); 9759 res.add_cst(p); 9760 res._range = get_plus_range(v._range,p.range()); 9761 res.update_all_sign(); 9762 return res; 9763 } 9764 9765 9766 9767 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9768 func<T1> operator+(const param<T1>& v, const func<T2>& f){ 9769 func<T1> res(v); 9770 res += f; 9771 return res; 9772 } 9773 9774 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9775 func<T2> operator+(const param<T1>& v, const func<T2>& f){ 9776 func<T2> res(v); 9777 res += f; 9778 return res; 9779 } 9780 9781 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9782 func<T1> operator+(const func<T1>& f, const param<T2>& v){ 9783 func<T1> res(v); 9784 res += f; 9785 return res; 9786 } 9787 9788 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9789 func<T2> operator+(const func<T1>& f, const param<T2>& v){ 9790 func<T2> res(v); 9791 res += f; 9792 return res; 9793 } 9794 9795 9796 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9797 func<T1> operator+(const constant<T1>& v, const func<T2>& f){ 9798 func<T1> res(v); 9799 res += f; 9800 return res; 9801 } 9802 9803 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9804 func<T2> operator+(const constant<T1>& v, const func<T2>& f){ 9805 func<T2> res(v); 9806 res += f; 9807 return res; 9808 } 9809 9810 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9811 func<T1> operator+(const func<T1>& f, const constant<T2>& v){ 9812 func<T1> res(v); 9813 res += f; 9814 return res; 9815 } 9816 9817 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9818 func<T2> operator+(const func<T1>& f, const constant<T2>& v){ 9819 func<T2> res(v); 9820 res += f; 9821 return res; 9822 } 9823 9824 9825 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9826 func<T1> operator+(T1 v, const func<T2>& f){ 9827 func<T1> res(v); 9828 res += f; 9829 return res; 9830 } 9831 9832 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9833 func<T2> operator+(T1 v, const func<T2>& f){ 9834 func<T2> res(v); 9835 res += f; 9836 return res; 9837 } 9838 9839 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9840 func<T1> operator+(const func<T1>& f, T2 v){ 9841 func<T1> res(v); 9842 res += f; 9843 return res; 9844 } 9845 9846 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9847 func<T2> operator+(const func<T1>& f, T2 v){ 9848 func<T2> res(v); 9849 res += f; 9850 return res; 9851 } 9852 9853 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9854 func<T1> operator-(const constant<T1>& v, const func<T2>& f){ 9855 func<T1> res(v); 9856 res -= f; 9857 return res; 9858 } 9859 9860 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9861 func<T2> operator-(const constant<T1>& v, const func<T2>& f){ 9862 func<T2> res(v); 9863 res -= f; 9864 return res; 9865 } 9866 9867 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9868 func<T1> operator-(const func<T1>& f, const constant<T2>& v){ 9869 func<T1> res(f); 9870 res -= v; 9871 return res; 9872 } 9873 9874 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9875 func<T2> operator-(const func<T1>& f, const constant<T2>& v){ 9876 func<T2> res(f); 9877 res -= f; 9878 return res; 9879 } 9880 9881 9882 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9883 func<T1> operator-(T1 v, const func<T2>& f){ 9884 func<T1> res(v); 9885 res -= f; 9886 return res; 9887 } 9888 9889 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9890 func<T2> operator-(T1 v, const func<T2>& f){ 9891 func<T2> res(v); 9892 res -= f; 9893 return res; 9894 } 9895 9896 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9897 func<T1> operator-(const func<T1>& f, T2 v){ 9898 func<T1> res(f); 9899 res -= v; 9900 return res; 9901 } 9902 9903 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9904 func<T2> operator-(const func<T1>& f, T2 v){ 9905 func<T2> res(f); 9906 res -= v;//TODO check when v = f 9907 return res; 9908 } 9909 9910 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9911 func<T1> operator-(const func<T1>& f, const param<T2>& v){ 9912 func<T1> res(v); 9913 res.reverse_sign(); 9914 res += f; 9915 return res; 9916 } 9917 9918 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9919 func<T2> operator-(const func<T1>& f, const param<T2>& v){ 9920 func<T2> res(v); 9921 res.reverse_sign(); 9922 res += f; 9923 return res; 9924 } 9925 9926 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9927 func<T1> operator-(const param<T1>& p, const func<T2>& f){ 9928 func<T1> res(f); 9929 res.reverse_sign(); 9930 res += p; 9931 return res; 9932 } 9933 9934 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9935 func<T2> operator-(const param<T1>& p, const func<T2>& f){ 9936 func<T2> res(f); 9937 res.reverse_sign(); 9938 res += p; 9939 return res; 9940 } 9941 9942 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9943 func<T1> operator*(const param<T1>& v, const func<T2>& f){ 9944 func<T1> res(v); 9945 func<T1> f2(f); 9946 if((v._is_transposed || v.is_matrix()) && (!f2.func_is_number() && !f2._is_vector)){ 9947 return res *= f2.vec(); 9948 } 9949 res *= f2; 9950 return res; 9951 } 9952 9953 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9954 func<T2> operator*(const param<T1>& v, const func<T2>& f){ 9955 func<T2> res(v); 9956 func<T2> f2(f); 9957 if((v._is_transposed || v.is_matrix()) && (!f2.func_is_number() && !f2._is_vector)){ 9958 return res *= f2.vec(); 9959 } 9960 res *= f2; 9961 return res; 9962 } 9963 9964 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9965 func<T1> operator*(const func<T1>& f, const param<T2>& v){ 9966 func<T1> res(f); 9967 func<T1> f2(v); 9968 if((f._is_transposed || f.is_matrix()) && (!f2.func_is_number() && !f2._is_vector)){ 9969 return res *= f2.vec(); 9970 } 9971 return res *= f2; 9972 } 9973 9974 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 9975 func<T2> operator*(const func<T1>& f, const param<T2>& v){ 9976 func<T2> res(f); 9977 func<T2> f2(v); 9978 if((f._is_transposed || f.is_matrix()) && (!f2.func_is_number() && !f2._is_vector)){ 9979 return res *= f2.vec(); 9980 } 9981 res *= f2; 9982 return res; 9983 } 9984 9985 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 9986 func<T1> operator*(const constant<T1>& c, const param<T2>& p){ 9987 func<T1> res; 9988 auto new_c(c); 9989 if(c._is_transposed){/* If this is a dot product resize the constant to match p's number of rows */ 9990 new_c._dim[1] = p._dim[0]; 9991 } 9992 res.update_dot_dim(new_c,p); 9993 res.insert(true,new_c,p); 9994 res._range = get_product_range(new_c.range(), p._range); 9995 res.update_all_sign(); 9996 if(c._is_transposed){ 9997 res._range->first = extended_mult(res._range->first,(T1)p._dim[0]); 9998 res._range->second = extended_mult(res._range->second,(T1)p._dim[0]); 9999 } 10000 return res; 10001 } 10002 10003 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 10004 func<T2> operator*(const constant<T1>& c, const param<T2>& p){ 10005 func<T2> res; 10006 constant<T2> new_c(c); 10007 if(c._is_transposed){/* If this is a dot product resize the constant to match p's number of rows */ 10008 new_c._dim[1] = p._dim[0]; 10009 } 10010 res.update_dot_dim(new_c,p); 10011 res.insert(true,new_c,p); 10012 res._range = get_product_range(new_c.range(), p._range); 10013 if(c._is_transposed){ 10014 res._range->first = extended_mult(res._range->first,(T2)p._dim[0]); 10015 res._range->second = extended_mult(res._range->second,(T2)p._dim[0]); 10016 } 10017 return res; 10018 } 10019 10020 10021 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10022 func<T1> operator*(const param<T1>& p, const constant<T2>& c){ 10023 func<T1> res; 10024 res._range = get_product_range(p._range,c.range()); 10025 res.update_all_sign(); 10026 res.update_dot_dim(p,c); 10027 if(p._is_transposed){ 10028 constant<T1> new_c(c.tr()); 10029 new_c._dim[1] = p._dim[0]; 10030 res.insert(true,new_c,p.tr()); 10031 res._range->first = extended_mult(res._range->first,(T1)p._dim[0]); 10032 res._range->second = extended_mult(res._range->second,(T1)p._dim[0]); 10033 res.transpose(); 10034 } 10035 else { 10036 res.insert(true,constant<T1>(c),p); 10037 } 10038 return res; 10039 } 10040 10041 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 10042 func<T2> operator*(const param<T1>& p, const constant<T2>& c){ 10043 func<T2> res; 10044 res._range = get_product_range(p._range,c.range()); 10045 res.update_all_sign(); 10046 res.update_dot_dim(p,c); 10047 if(p._is_transposed){ 10048 constant<T2> new_c(c.tr()); 10049 new_c._dim[1] = p._dim[0]; 10050 res.insert(true,new_c,p.tr()); 10051 res._range->first = extended_mult(res._range->first,(T2)p._dim[0]); 10052 res._range->second = extended_mult(res._range->second,(T2)p._dim[0]); 10053 res.transpose(); 10054 } 10055 else { 10056 res.insert(true,c,p); 10057 } 10058 return res; 10059 } 10060 10061 10062 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10063 func<T1> operator*(const constant<T1>& c, const func<T2>& p){ 10064 return func<T1>(c) *= p; 10065 } 10066 10067 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 10068 func<T2> operator*(const constant<T1>& c, const func<T2>& p){ 10069 return func<T2>(c) *= p; 10070 } 10071 10072 10073 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10074 func<T1> operator*(const func<T1>& p, const constant<T2>& c){ 10075 return func<T1>(p) *= func<T1>(c); 10076 } 10077 10078 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 10079 func<T2> operator*(const func<T1>& p, const constant<T2>& c){ 10080 return func<T2>(p) *= func<T2>(c); 10081 } 10082 10083 10084 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10085 func<T1> operator*(T1 p, const param<T2>& v){ 10086 return constant<T1>(p)*v; 10087 } 10088 10089 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 10090 func<T2> operator*(T1 p, const param<T2>& v){ 10091 return constant<T2>(p)*v; 10092 } 10093 10094 10095 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10096 func<T1> operator*(const param<T1>& v, T2 p){ 10097 return v*constant<T1>(p); 10098 } 10099 10100 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 10101 func<T2> operator*(const param<T1>& v, T2 p){ 10102 return v*constant<T2>(p); 10103 } 10104 10105 10106 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10107 func<T1> operator*(T1 p, const func<T2>& f){ 10108 return constant<T1>(p) * f; 10109 } 10110 10111 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 10112 func<T2> operator*(T1 p, const func<T2>& f){ 10113 return constant<T2>(p)*f; 10114 } 10115 10116 10117 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10118 func<T1> operator*(const func<T1>& f, T2 p){ 10119 return f * constant<T1>(p); 10120 } 10121 10122 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> 10123 func<T2> operator*(const func<T1>& f, T2 p){ 10124 return f * constant<T2>(p); 10125 } 10126 10127 10128 10129 template<typename type> sum(const param<type> & p)10130 func<type> sum(const param<type>& p){ 10131 func<type> res; 10132 if (p.get_dim()==0) { 10133 return res; 10134 } 10135 if(p.is_matrix_indexed()){ 10136 return (unit<type>().tr()*p.vec()).in(range(0,p._indices->size()-1)); 10137 } 10138 return unit<type>().tr()*p.vec(); 10139 } 10140 10141 /** Create a matrix sum where each row will be indexed based on the entries starting at start_pos and spaning nb_entries. 10142 Example: 10143 dv = { 10144 [1,8] = 0 10145 [1,9] = 0 10146 [1,10] = 0 10147 [1,11] = 0 10148 [1,12] = 0 10149 [2,8] = 0 10150 [2,9] = 0 10151 [2,10] = 0 10152 [2,11] = 0 10153 [2,12] = 0 10154 [3,8] = 0 10155 [3,9] = 0 10156 [3,10] = 0 10157 [3,11] = 0 10158 [3,12] = 0 10159 }; 10160 sum(dv.in_matrix(0,1)) <= 0 gives: dv[1,8] + dv[2,8] + dv[3,8] <= 0; 10161 sum(dv.in_matrix(1,1)) <= 0 gives: dv[1,8] + dv[1,9] + dv[1,10] + dv[1,11] + dv[1,12] <= 0; 10162 */ 10163 template<typename type> sum_ith(const param<type> & p,unsigned start_pos,unsigned nb_entries)10164 func<type> sum_ith(const param<type>& p, unsigned start_pos, unsigned nb_entries){ 10165 auto matrix_p = p.in_matrix(start_pos,nb_entries); 10166 auto res = sum(matrix_p); 10167 return res.in(range(0,matrix_p._indices->get_nb_rows()-1)); 10168 } 10169 10170 /** Create a matrix sum where each row will be indexed based on the entries starting at start_pos and spaning nb_entries. 10171 Example: 10172 dv = { 10173 [1,8] = 0 10174 [1,9] = 0 10175 [1,10] = 0 10176 [1,11] = 0 10177 [1,12] = 0 10178 [2,8] = 0 10179 [2,9] = 0 10180 [2,10] = 0 10181 [2,11] = 0 10182 [2,12] = 0 10183 [3,8] = 0 10184 [3,9] = 0 10185 [3,10] = 0 10186 [3,11] = 0 10187 [3,12] = 0 10188 }; 10189 sum(dv.in_matrix(0,1)) <= 0 gives: dv[1,8] + dv[2,8] + dv[3,8] <= 0; 10190 sum(dv.in_matrix(1,1)) <= 0 gives: dv[1,8] + dv[1,9] + dv[1,10] + dv[1,11] + dv[1,12] <= 0; 10191 */ 10192 template<typename type> sum_ith(const var<type> & p,unsigned start_pos,unsigned nb_entries)10193 func<type> sum_ith(const var<type>& p, unsigned start_pos, unsigned nb_entries){ 10194 auto matrix_p = p.in_matrix(start_pos,nb_entries); 10195 auto res = sum(matrix_p); 10196 return res.in(range(0,matrix_p._indices->get_nb_rows()-1)); 10197 } 10198 10199 template<typename type> sum(const var<type> & p)10200 func<type> sum(const var<type>& p){ 10201 func<type> res; 10202 if (p.get_dim()==0) { 10203 return res; 10204 } 10205 if(p.is_matrix_indexed()){ 10206 return (unit<type>().tr()*p.vec()).in(range(0,p._indices->size()-1)); 10207 } 10208 return unit<type>().tr()*p.vec(); 10209 } 10210 10211 template<typename type> norm2(const func<type> & f)10212 func<type> norm2(const func<type>& f){ 10213 return sum(pow(f,2)); 10214 } 10215 10216 template<typename type> norm2(const var<type> & v)10217 func<type> norm2(const var<type>& v){ 10218 return sum(pow(v,2)); 10219 } 10220 10221 template<typename type> norm2(const param<type> & p)10222 func<type> norm2(const param<type>& p){ 10223 return sum(pow(p,2)); 10224 } 10225 10226 template<typename type> sum(const func<type> & f)10227 func<type> sum(const func<type>& f){ 10228 func<type> res; 10229 if (f.get_dim()==0) { 10230 return res; 10231 } 10232 if(f.is_matrix_indexed()){ 10233 return (unit<type>().tr()*f.vec()).in(range(0,f._indices->size()-1)); 10234 } 10235 if(f.is_nonlinear()){ 10236 return unit<type>().tr()*f.vec(); 10237 } 10238 res = f; 10239 for (auto &pair: *res._lterms) { 10240 pair.second._coef->transpose(); 10241 pair.second._p->vectorize(); 10242 } 10243 for (auto &pair: *res._qterms) { 10244 pair.second._coef->transpose(); 10245 pair.second._p->first->vectorize(); 10246 pair.second._p->second->vectorize(); 10247 } 10248 for (auto &pair: *res._pterms) { 10249 pair.second._coef->transpose(); 10250 } 10251 if (res._cst->is_function()) { 10252 auto f_cst = *static_pointer_cast<func<type>>(res._cst); 10253 res._cst = make_shared<func<type>>(sum(f_cst)); 10254 } 10255 else if(res._cst->is_param()) { 10256 auto p_cst = *static_pointer_cast<param<type>>(res._cst); 10257 res._cst = make_shared<func<type>>(sum(p_cst)); 10258 } 10259 else if(res._cst->is_number()) { 10260 auto p_cst = *static_pointer_cast<constant<type>>(res._cst); 10261 res._cst = make_shared<func<type>>(func<type>(f.get_nb_inst()*p_cst)); 10262 } 10263 res._dim[0] = 1; 10264 res._dim[1] = 1; 10265 return res; 10266 } 10267 10268 template<typename type> sum(const param<type> & p,const indices & ids)10269 func<type> sum(const param<type>& p, const indices& ids){ 10270 func<type> res; 10271 if (p.get_dim()==0) { 10272 return res; 10273 } 10274 if(p.is_matrix_indexed()){ 10275 return (unit<type>().tr()*(p.vec()).in(ids)).in(ids); 10276 } 10277 return unit<type>().tr()*(p.vec()).in(ids); 10278 } 10279 10280 template<typename type> sum(const var<type> & p,const indices & ids)10281 func<type> sum(const var<type>& p, const indices& ids){ 10282 func<type> res; 10283 if (p.get_dim()==0) { 10284 return res; 10285 } 10286 if(p.is_matrix_indexed()){ 10287 return (unit<type>().tr()*(p.vec()).in(ids)).in(ids); 10288 } 10289 return unit<type>().tr()*(p.vec()).in(ids); 10290 } 10291 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const func<T1> & f1,const func<T2> & f2)10292 func<T2> product(const func<T1>& f1, const func<T2>& f2){ 10293 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10294 return f1.tr()*f2.vec(); 10295 } 10296 if(f1.is_matrix() && f2.is_column_vector()) 10297 return f1*f2.vec(); 10298 return f1*f2; 10299 } 10300 10301 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10302 func<T1> product(const func<T1>& f1, const func<T2>& f2){ 10303 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10304 return f1.tr()*f2.vec(); 10305 } 10306 if(f1.is_matrix() && f2.is_column_vector()) 10307 return f1*f2.vec(); 10308 return f1*f2; 10309 } 10310 10311 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10312 func<T1> product(const param<T1>& f1, const param<T2>& f2){ 10313 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10314 return f1.tr()*f2.vec(); 10315 } 10316 if(f1.is_matrix() && f2.is_column_vector()) 10317 return f1*f2.vec(); 10318 return f1*f2; 10319 } 10320 10321 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10322 func<T1> product(const var<T1>& f1, const var<T2>& f2){ 10323 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10324 return f1.tr()*f2.vec(); 10325 } 10326 if(f1.is_matrix() && f2.is_column_vector()) 10327 return f1*f2.vec(); 10328 return f1*f2; 10329 } 10330 10331 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const var<T1> & f1,const var<T2> & f2)10332 func<T2> product(const var<T1>& f1, const var<T2>& f2){ 10333 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10334 return f1.tr()*f2.vec(); 10335 } 10336 if(f1.is_matrix() && f2.is_column_vector()) 10337 return f1*f2.vec(); 10338 return f1*f2; 10339 } 10340 10341 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10342 func<T1> product(const var<T1>& f1, const param<T2>& f2){ 10343 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10344 return f1.tr()*f2.vec(); 10345 } 10346 if(f1.is_matrix() && f2.is_column_vector()) 10347 return f1*f2.vec(); 10348 return f1*f2; 10349 } 10350 10351 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const var<T1> & f1,const param<T2> & f2)10352 func<T2> product(const var<T1>& f1, const param<T2>& f2){ 10353 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10354 return f1.tr()*f2.vec(); 10355 } 10356 if(f1.is_matrix() && f2.is_column_vector()) 10357 return f1*f2.vec(); 10358 return f1*f2; 10359 } 10360 10361 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10362 func<T1> product(const param<T1>& f1, const var<T2>& f2){ 10363 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10364 return f1.tr()*f2.vec(); 10365 } 10366 if(f1.is_matrix() && f2.is_column_vector()) 10367 return f1*f2.vec(); 10368 return f1*f2; 10369 } 10370 10371 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const param<T1> & f1,const var<T2> & f2)10372 func<T2> product(const param<T1>& f1, const var<T2>& f2){ 10373 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10374 return f1.tr()*f2.vec(); 10375 } 10376 if(f1.is_matrix() && f2.is_column_vector()) 10377 return f1*f2.vec(); 10378 return f1*f2; 10379 } 10380 10381 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const param<T1> & f1,const param<T2> & f2)10382 func<T2> product(const param<T1>& f1, const param<T2>& f2){ 10383 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10384 return f1.tr()*f2.vec(); 10385 } 10386 if(f1.is_matrix() && f2.is_column_vector()) 10387 return f1*f2.vec(); 10388 return f1*f2; 10389 } 10390 10391 10392 10393 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const param<T1> & f1,const func<T2> & f2)10394 func<T2> product(const param<T1>& f1, const func<T2>& f2){ 10395 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10396 return f1.tr()*f2.vec(); 10397 } 10398 if(f1.is_matrix() && f2.is_column_vector()) 10399 return f1*f2.vec(); 10400 return f1*f2; 10401 } 10402 10403 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10404 func<T1> product(const param<T1>& f1, const func<T2>& f2){ 10405 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10406 return f1.tr()*f2.vec(); 10407 } 10408 if(f1.is_matrix() && f2.is_column_vector()) 10409 return f1*f2.vec(); 10410 return f1*f2; 10411 } 10412 10413 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const func<T1> & f1,const param<T2> & f2)10414 func<T2> product(const func<T1>& f1, const param<T2>& f2){ 10415 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10416 return f1.tr()*f2.vec(); 10417 } 10418 if(f1.is_matrix() && f2.is_column_vector()) 10419 return f1*f2.vec(); 10420 return f1*f2; 10421 } 10422 10423 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10424 func<T1> product(const func<T1>& f1, const param<T2>& f2){ 10425 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10426 return f1.tr()*f2.vec(); 10427 } 10428 if(f1.is_matrix() && f2.is_column_vector()) 10429 return f1*f2.vec(); 10430 return f1*f2; 10431 } 10432 10433 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const var<T1> & f1,const func<T2> & f2)10434 func<T2> product(const var<T1>& f1, const func<T2>& f2){ 10435 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10436 return f1.tr()*f2.vec(); 10437 } 10438 if(f1.is_matrix() && f2.is_column_vector()) 10439 return f1*f2.vec(); 10440 return f1*f2; 10441 } 10442 10443 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10444 func<T1> product(const var<T1>& f1, const func<T2>& f2){ 10445 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10446 return f1.tr()*f2.vec(); 10447 } 10448 if(f1.is_matrix() && f2.is_column_vector()) 10449 return f1*f2.vec(); 10450 return f1*f2; 10451 } 10452 10453 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const func<T1> & f1,const var<T2> & f2)10454 func<T2> product(const func<T1>& f1, const var<T2>& f2){ 10455 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10456 return f1.tr()*f2.vec(); 10457 } 10458 if(f1.is_matrix() && f2.is_column_vector()) 10459 return f1*f2.vec(); 10460 return f1*f2; 10461 } 10462 10463 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10464 func<T1> product(const func<T1>& f1, const var<T2>& f2){ 10465 if(f1.is_column_vector() && f2.is_column_vector()){/* This is a dot product */ 10466 return f1.tr()*f2.vec(); 10467 } 10468 if(f1.is_matrix() && f2.is_column_vector()) 10469 return f1*f2.vec(); 10470 return f1*f2; 10471 } 10472 10473 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10474 func<T1> product(const param<T1>& f1, const constant<T2>& f2){ 10475 constant<T1> new_f2(f2); 10476 if(f1.is_column_vector()){/* This is a dot product */ 10477 new_f2._dim[0] = f1._dim[1]; 10478 return f1.tr()*new_f2.vec(); 10479 } 10480 return f1*new_f2; 10481 } 10482 10483 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const param<T1> & f1,const constant<T2> & f2)10484 func<T2> product(const param<T1>& f1, const constant<T2>& f2){ 10485 constant<T2> new_f2(f2); 10486 if(f1.is_column_vector()){/* This is a dot product */ 10487 new_f2._dim[0] = f1._dim[1]; 10488 return f1.tr()*new_f2.vec(); 10489 } 10490 return f1*new_f2; 10491 } 10492 10493 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10494 func<T1> product(const constant<T1>& f1, const param<T2>& f2){ 10495 constant<T1> new_f1(f1); 10496 if(f2.is_column_vector()){/* Dot product with a constant */ 10497 new_f1.transpose(); 10498 new_f1._dim[1] = f2._dim[0]; 10499 return new_f1*f2.vec(); 10500 } 10501 return new_f1*f2; 10502 } 10503 10504 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const constant<T1> & f1,const param<T2> & f2)10505 func<T2> product(const constant<T1>& f1, const param<T2>& f2){ 10506 constant<T2> new_f1(f1); 10507 if(f2.is_column_vector()){/* Dot product with a constant */ 10508 new_f1.transpose(); 10509 new_f1._dim[1] = f2._dim[0]; 10510 return new_f1*f2.vec(); 10511 } 10512 return new_f1*f2; 10513 } 10514 10515 10516 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10517 func<T1> product(const var<T1>& f1, const constant<T2>& f2){ 10518 constant<T1> new_f2(f2); 10519 if(f1.is_column_vector()){/* This is a dot product */ 10520 new_f2._dim[0] = f1._dim[1]; 10521 return f1.tr()*new_f2.vec(); 10522 } 10523 return f1*new_f2; 10524 } 10525 10526 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const var<T1> & f1,const constant<T2> & f2)10527 func<T2> product(const var<T1>& f1, const constant<T2>& f2){ 10528 constant<T2> new_f2(f2); 10529 if(f1.is_column_vector()){/* This is a dot product */ 10530 new_f2._dim[0] = f1._dim[1]; 10531 return f1.tr()*new_f2.vec(); 10532 } 10533 return f1*new_f2; 10534 } 10535 10536 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10537 func<T1> product(const constant<T1>& f1, const var<T2>& f2){ 10538 constant<T1> new_f1(f1); 10539 if(f2.is_column_vector()){/* Dot product with a constant */ 10540 new_f1.transpose(); 10541 new_f1._dim[1] = f2._dim[0]; 10542 return new_f1*f2.vec(); 10543 } 10544 return new_f1*f2; 10545 } 10546 10547 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const constant<T1> & f1,const var<T2> & f2)10548 func<T2> product(const constant<T1>& f1, const var<T2>& f2){ 10549 constant<T2> new_f1(f1); 10550 if(f2.is_column_vector()){/* Dot product with a constant */ 10551 new_f1.transpose(); 10552 new_f1._dim[1] = f2._dim[0]; 10553 return new_f1*f2.vec(); 10554 } 10555 return new_f1*f2; 10556 } 10557 10558 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10559 func<T1> product(const constant<T1>& f1, const func<T2>& f2){ 10560 constant<T1> new_f1(f1); 10561 if(f2.is_column_vector()){/* Dot product with a constant */ 10562 new_f1.transpose(); 10563 new_f1._dim[1] = f2._dim[0]; 10564 return new_f1*f2.vec(); 10565 } 10566 return new_f1*f2; 10567 } 10568 10569 10570 template<class T1=double,class T2=double, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const constant<T1> & f1,const func<T2> & f2)10571 func<T2> product(const constant<T1>& f1, const func<T2>& f2){ 10572 constant<T2> new_f1(f1); 10573 if(f2.is_column_vector()){/* Dot product with a constant */ 10574 new_f1.transpose(); 10575 new_f1._dim[1] = f2._dim[0]; 10576 return new_f1*f2.vec(); 10577 } 10578 return new_f1*f2; 10579 } 10580 10581 10582 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10583 func<T1> product(const func<T1>& f1, const constant<T2>& f2){ 10584 constant<T1> new_f2(f2); 10585 if(f1.is_column_vector()){/* This is a dot product */ 10586 new_f2._dim[0] = f1._dim[1]; 10587 return f1.tr()*new_f2.vec(); 10588 } 10589 return f1*new_f2; 10590 } 10591 10592 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const func<T1> & f1,const constant<T2> & f2)10593 func<T2> product(const func<T1>& f1, const constant<T2>& f2){ 10594 constant<T2> new_f2(f2); 10595 if(f1.is_column_vector()){/* This is a dot product */ 10596 new_f2._dim[0] = f1._dim[1]; 10597 return f1.tr()*new_f2.vec(); 10598 } 10599 return f1*new_f2; 10600 } 10601 10602 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10603 func<T1> product(T1 f1, const param<T2>& f2){ 10604 return product(constant<T1>(f1), f2); 10605 } 10606 10607 template<class T1=double,class T2=double, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(T1 f1,const param<T2> & f2)10608 func<T2> product(T1 f1, const param<T2>& f2){ 10609 return product(constant<T2>(f1), f2); 10610 } 10611 10612 10613 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10614 func<T1> product(const param<T1>& f1, T2 f2){ 10615 return product(f1, constant<T1>(f2)); 10616 } 10617 10618 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const param<T1> & f1,T2 f2)10619 func<T2> product(const param<T1>& f1, T2 f2){ 10620 return product(f1, constant<T2>(f2)); 10621 } 10622 10623 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10624 func<T1> product(T1 f1, const var<T2>& f2){ 10625 return product(constant<T1>(f1), f2); 10626 } 10627 10628 template<class T1=double,class T2=double, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(T1 f1,const var<T2> & f2)10629 func<T2> product(T1 f1, const var<T2>& f2){ 10630 return product(constant<T2>(f1), f2); 10631 } 10632 10633 10634 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10635 func<T1> product(const var<T1>& f1, T2 f2){ 10636 return product(f1, constant<T1>(f2)); 10637 } 10638 10639 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const var<T1> & f1,T2 f2)10640 func<T2> product(const var<T1>& f1, T2 f2){ 10641 return product(f1, constant<T2>(f2)); 10642 } 10643 10644 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10645 func<T1> product(T1 f1, const func<T2>& f2){ 10646 return product(constant<T1>(f1), f2); 10647 } 10648 10649 template<class T1=double,class T2=double, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(T1 f1,const func<T2> & f2)10650 func<T2> product(T1 f1, const func<T2>& f2){ 10651 return product(constant<T2>(f1), f2); 10652 } 10653 10654 10655 template<class T1,class T2, typename enable_if<is_convertible<T2, T1>::value && sizeof(T2) < sizeof(T1)>::type* = nullptr> 10656 func<T1> product(const func<T1>& f1, T2 f2){ 10657 return product(f1, constant<T1>(f2)); 10658 } 10659 10660 template<class T1,class T2, typename enable_if<is_convertible<T1, T2>::value && sizeof(T2) >= sizeof(T1)>::type* = nullptr> product(const func<T1> & f1,T2 f2)10661 func<T2> product(const func<T1>& f1, T2 f2){ 10662 return product(f1, constant<T2>(f2)); 10663 } 10664 10665 10666 // template<typename type> 10667 // func<type> sum(const func<type>& f, const indices& ids){ 10668 // auto ff = f; 10669 // ff.index_in(ids); 10670 // func<type> res; 10671 // return unit<type>().tr()*(ff.vec()).in(ids); 10672 // } 10673 10674 /** WARNING, only call if the variables appearing in the function are complex or double */ 10675 pair<func<double>,func<double>> get_real_imag(const func<Cpx>& f); 10676 pair<func<double>,func<double>> get_mag_ang(const func<Cpx>& f); 10677 func<double> get_real(constant_* c); 10678 func<double> get_imag(constant_* c); 10679 func<double> get_mag(constant_* c); 10680 func<double> get_ang(constant_* c); 10681 10682 vector<vector<int>> build_compositions(int k, int n); 10683 10684 } 10685 10686 10687 10688 #endif /* func_h */ 10689 10690 10691