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 &lt: *_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 &lt: *_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 &lt: *_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 &lt:get_lterms()) {
7633                 lt.second._p = new_vars->at(lt.second._p->get_name(false,false)).first;
7634             }
7635             for (auto &lt: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 &lt: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