sx_instantiator.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 #define CASADI_SX_INSTANTIATOR_CPP
26 #include "matrix_impl.hpp"
27 
28 #include "sx_function.hpp"
29 #include <array>
30 
31 namespace casadi {
32 
33  template<>
34  bool CASADI_EXPORT SX::__nonzero__() const {
35  casadi_assert(numel()==1,
36  "Only scalar Matrix could have a truth value, but you "
37  "provided a shape" + dim());
38  return nonzeros().at(0).__nonzero__();
39  }
40 
41  template<>
42  void CASADI_EXPORT SX::set_max_depth(casadi_int eq_depth) {
43  SXNode::eq_depth_ = eq_depth;
44  }
45 
46  template<>
47  casadi_int CASADI_EXPORT SX::get_max_depth() {
48  return SXNode::eq_depth_;
49  }
50 
51  template<>
52  SX CASADI_EXPORT SX::_sym(const std::string& name, const Sparsity& sp) {
53  // Create a dense n-by-m matrix
54  std::vector<SXElem> retv;
55 
56  // Check if individial names have been provided
57  if (name[0]=='[') {
58 
59  // Make a copy of the string and modify it as to remove the special characters
60  std::string modname = name;
61  for (std::string::iterator it=modname.begin(); it!=modname.end(); ++it) {
62  switch (*it) {
63  case '(': case ')': case '[': case ']': case '{': case '}': case ',': case ';': *it = ' ';
64  }
65  }
66 
67  std::istringstream iss(modname);
68  std::string varname;
69 
70  // Loop over elements
71  while (!iss.fail()) {
72  // Read the name
73  iss >> varname;
74 
75  // Append to the return vector
76  if (!iss.fail())
77  retv.push_back(SXElem::sym(varname));
78  }
79  } else if (sp.is_scalar(true)) {
80  retv.push_back(SXElem::sym(name));
81  } else {
82  // Scalar
83  std::stringstream ss;
84  for (casadi_int k=0; k<sp.nnz(); ++k) {
85  ss.str("");
86  ss << name << "_" << k;
87  retv.push_back(SXElem::sym(ss.str()));
88  }
89  }
90 
91  // Determine dimensions automatically if empty
92  if (sp.is_scalar(true)) {
93  return SX(retv);
94  } else {
95  return SX(sp, retv, false);
96  }
97  }
98 
99  template<>
100  bool CASADI_EXPORT SX::is_regular() const {
101  // First pass: ignore symbolics
102  for (casadi_int i=0; i<nnz(); ++i) {
103  const SXElem& x = nonzeros().at(i);
104  if (x.is_constant()) {
105  if (x.is_nan() || x.is_inf() || x.is_minus_inf()) return false;
106  }
107  }
108  // Second pass: don't ignore symbolics
109  for (casadi_int i=0; i<nnz(); ++i) {
110  if (!nonzeros().at(i).is_regular()) return false;
111  }
112  return true;
113  }
114 
115  template<>
116  bool CASADI_EXPORT SX::is_smooth() const {
117  // Make a function
118  Function temp("tmp_is_smooth", {SX()}, {*this}, Dict{{"max_io", 0}, {"allow_free", true}});
119 
120  // Run the function on the temporary variable
121  SXFunction* t = temp.get<SXFunction>();
122  return t->is_smooth();
123  }
124 
125  template<>
126  casadi_int CASADI_EXPORT SX::element_hash() const {
127  return scalar().__hash__();
128  }
129 
130  template<>
131  bool CASADI_EXPORT SX::is_leaf() const {
132  return scalar().is_leaf();
133  }
134 
135  template<>
136  bool CASADI_EXPORT SX::is_commutative() const {
137  return scalar().is_commutative();
138  }
139 
140  template<>
141  bool CASADI_EXPORT SX::is_valid_input() const {
142  for (casadi_int k=0; k<nnz(); ++k) // loop over non-zero elements
143  if (!nonzeros().at(k)->is_symbolic()) // if an element is not symbolic
144  return false;
145 
146  return true;
147  }
148 
149  template<>
150  bool CASADI_EXPORT SX::is_call() const {
151  return scalar().is_call();
152  }
153 
154  template<>
155  bool CASADI_EXPORT SX::is_output() const {
156  return scalar().is_output();
157  }
158 
159  template<>
160  bool CASADI_EXPORT SX::has_output() const {
161  return scalar().has_output();
162  }
163 
164  template<>
165  SX CASADI_EXPORT SX::get_output(casadi_int oind) const {
166  return scalar().get_output(oind);
167  }
168 
169  template<>
170  Function CASADI_EXPORT SX::which_function() const {
171  return scalar().which_function();
172  }
173 
174  template<>
175  casadi_int CASADI_EXPORT SX::which_output() const {
176  return scalar().which_output();
177  }
178 
179  template<>
180  bool CASADI_EXPORT SX::is_symbolic() const {
181  if (is_dense()) {
182  return is_valid_input();
183  } else {
184  return false;
185  }
186  }
187 
188  template<>
189  casadi_int CASADI_EXPORT SX::op() const {
190  return scalar().op();
191  }
192 
193  template<>
194  bool CASADI_EXPORT SX::is_op(casadi_int op) const {
195  return scalar().is_op(op);
196  }
197 
198  template<> bool CASADI_EXPORT SX::has_duplicates() const {
199  bool has_duplicates = false;
200  for (auto&& i : nonzeros_) {
201  bool is_duplicate = i.get_temp()!=0;
202  if (is_duplicate) {
203  casadi_warning("Duplicate expression: " + str(i));
204  }
205  has_duplicates = has_duplicates || is_duplicate;
206  i.set_temp(1);
207  }
208  return has_duplicates;
209  }
210 
211  template<> void CASADI_EXPORT SX::reset_input() const {
212  for (auto&& i : nonzeros_) {
213  i.set_temp(0);
214  }
215  }
216 
217  template<>
218  std::string CASADI_EXPORT SX::name() const {
219  return scalar().name();
220  }
221 
222  template<>
223  SX CASADI_EXPORT SX::dep(casadi_int ch) const {
224  return scalar().dep(ch);
225  }
226 
227  template<>
228  casadi_int CASADI_EXPORT SX::n_dep() const {
229  return scalar().n_dep();
230  }
231 
232  template<>
233  void CASADI_EXPORT SX::expand(const SX& ex2, SX& ww, SX& tt) {
234  casadi_assert_dev(ex2.is_scalar());
235  SXElem ex = ex2.scalar();
236 
237  // Terms, weights and indices of the nodes that are already expanded
238  std::vector<std::vector<SXNode*> > terms;
239  std::vector<std::vector<double> > weights;
240  std::map<SXNode*, casadi_int> indices;
241 
242  // Stack of nodes that are not yet expanded
243  std::stack<SXNode*> to_be_expanded;
244  to_be_expanded.push(ex.get());
245 
246  while (!to_be_expanded.empty()) { // as long as there are nodes to be expanded
247 
248  // Check if the last element on the stack is already expanded
249  if (indices.find(to_be_expanded.top()) != indices.end()) {
250  // Remove from stack
251  to_be_expanded.pop();
252  continue;
253  }
254 
255  // Weights and terms
256  std::vector<double> w; // weights
257  std::vector<SXNode*> f; // terms
258 
259  if (to_be_expanded.top()->is_constant()) { // constant nodes are seen as multiples of one
260  w.push_back(to_be_expanded.top()->to_double());
261  f.push_back(casadi_limits<SXElem>::one.get());
262  } else if (to_be_expanded.top()->is_symbolic()) {
263  // symbolic nodes have weight one and itself as factor
264  w.push_back(1);
265  f.push_back(to_be_expanded.top());
266  } else { // unary or binary node
267 
268  casadi_assert_dev(to_be_expanded.top()->n_dep()); // make sure that the node is binary
269 
270  // Check if addition, subtracton or multiplication
271  SXNode* node = to_be_expanded.top();
272  // If we have a binary node that we can factorize
273  if (node->op() == OP_ADD || node->op() == OP_SUB ||
274  (node->op() == OP_MUL && (node->dep(0)->is_constant() ||
275  node->dep(1)->is_constant()))) {
276  // Make sure that both children are factorized, if not - add to stack
277  if (indices.find(node->dep(0).get()) == indices.end()) {
278  to_be_expanded.push(node->dep(0).get());
279  continue;
280  }
281  if (indices.find(node->dep(1).get()) == indices.end()) {
282  to_be_expanded.push(node->dep(1).get());
283  continue;
284  }
285 
286  // Get indices of children
287  casadi_int ind1 = indices[node->dep(0).get()];
288  casadi_int ind2 = indices[node->dep(1).get()];
289 
290  // If multiplication
291  if (node->op() == OP_MUL) {
292  double fac;
293  // Multiplication where the first factor is a constant
294  if (node->dep(0)->is_constant()) {
295  fac = node->dep(0)->to_double();
296  f = terms[ind2];
297  w = weights[ind2];
298  } else { // Multiplication where the second factor is a constant
299  fac = node->dep(1)->to_double();
300  f = terms[ind1];
301  w = weights[ind1];
302  }
303  for (casadi_int i=0; i<w.size(); ++i) w[i] *= fac;
304 
305  } else { // if addition or subtraction
306  if (node->op() == OP_ADD) { // Addition: join both sums
307  f = terms[ind1]; f.insert(f.end(), terms[ind2].begin(), terms[ind2].end());
308  w = weights[ind1]; w.insert(w.end(), weights[ind2].begin(), weights[ind2].end());
309  } else { // Subtraction: join both sums with negative weights for second term
310  f = terms[ind1]; f.insert(f.end(), terms[ind2].begin(), terms[ind2].end());
311  w = weights[ind1];
312  w.reserve(f.size());
313  for (casadi_int i=0; i<weights[ind2].size(); ++i) w.push_back(-weights[ind2][i]);
314  }
315  // Eliminate multiple elements
316  std::vector<double> w_new; w_new.reserve(w.size()); // weights
317  std::vector<SXNode*> f_new; f_new.reserve(f.size()); // terms
318  std::map<SXNode*, casadi_int> f_ind; // index in f_new
319 
320  for (casadi_int i=0; i<w.size(); i++) {
321  // Try to locate the node
322  auto it = f_ind.find(f[i]);
323  if (it == f_ind.end()) { // if the term wasn't found
324  w_new.push_back(w[i]);
325  f_new.push_back(f[i]);
326  f_ind[f[i]] = f_new.size()-1;
327  } else { // if the term already exists
328  w_new[it->second] += w[i]; // just add the weight
329  }
330  }
331  w = w_new;
332  f = f_new;
333  }
334  } else { // if we have a binary node that we cannot factorize
335  // By default,
336  w.push_back(1);
337  f.push_back(node);
338 
339  }
340  }
341 
342  // Save factorization of the node
343  weights.push_back(w);
344  terms.push_back(f);
345  indices[to_be_expanded.top()] = terms.size()-1;
346 
347  // Remove node from stack
348  to_be_expanded.pop();
349  }
350 
351  // Save expansion to output
352  casadi_int thisind = indices[ex.get()];
353  ww = SX(weights[thisind]);
354 
355  std::vector<SXElem> termsv(terms[thisind].size());
356  for (casadi_int i=0; i<termsv.size(); ++i)
357  termsv[i] = SXElem::create(terms[thisind][i]);
358  tt = SX(termsv);
359  }
360 
361  template<>
362  SX CASADI_EXPORT SX::pw_const(const SX& t, const SX& tval, const SX& val) {
363  // number of intervals
364  casadi_int n = val.numel();
365 
366  casadi_assert(t.is_scalar(), "t must be a scalar");
367  casadi_assert(tval.numel() == n-1, "dimensions do not match");
368 
369  SX ret = val->at(0);
370  for (casadi_int i=0; i<n-1; ++i) {
371  ret += (val(i+1)-val(i)) * (t>=tval(i));
372  }
373 
374  return ret;
375  }
376 
377  template<>
378  SX CASADI_EXPORT SX::pw_lin(const SX& t, const SX& tval, const SX& val) {
379  // Number of points
380  casadi_int N = tval.numel();
381  casadi_assert(N>=2, "pw_lin: N>=2");
382  casadi_assert(val.numel() == N, "dimensions do not match");
383 
384  // Gradient for each line segment
385  SX g = SX(1, N-1);
386  for (casadi_int i=0; i<N-1; ++i)
387  g(i) = (val(i+1)- val(i))/(tval(i+1)-tval(i));
388 
389  // Line segments
390  SX lseg = SX(1, N-1);
391  for (casadi_int i=0; i<N-1; ++i)
392  lseg(i) = val(i) + g(i)*(t-tval(i));
393 
394  // Return piecewise linear function
395  return pw_const(t, tval(range(1, N-1)), lseg);
396  }
397 
398  template<>
399  SX CASADI_EXPORT SX::gauss_quadrature(const SX& f, const SX& x, const SX& a,
400  const SX& b, casadi_int order, const SX& w) {
401  casadi_assert(order == 5, "gauss_quadrature: order must be 5");
402  casadi_assert(w.is_empty(), "gauss_quadrature: empty weights");
403 
404  // Change variables to [-1, 1]
405  if (!is_equal(a.scalar(), -1) || !is_equal(b.scalar(), 1)) {
406  SX q1 = (b-a)/2;
407  SX q2 = (b+a)/2;
408 
409  Function fcn("gauss_quadrature", {x}, {f});
410 
411  return q1*gauss_quadrature(fcn(q1*x+q2).at(0), x, -1, 1);
412  }
413 
414  // Gauss points
415  std::vector<double> xi;
416  xi.push_back(-std::sqrt(5 + 2*std::sqrt(10.0/7))/3);
417  xi.push_back(-std::sqrt(5 - 2*std::sqrt(10.0/7))/3);
418  xi.push_back(0);
419  xi.push_back(std::sqrt(5 - 2*std::sqrt(10.0/7))/3);
420  xi.push_back(std::sqrt(5 + 2*std::sqrt(10.0/7))/3);
421 
422  // Gauss weights
423  std::vector<double> wi;
424  wi.push_back((322-13*std::sqrt(70.0))/900.0);
425  wi.push_back((322+13*std::sqrt(70.0))/900.0);
426  wi.push_back(128/225.0);
427  wi.push_back((322+13*std::sqrt(70.0))/900.0);
428  wi.push_back((322-13*std::sqrt(70.0))/900.0);
429 
430  // Evaluate at the Gauss points
431  Function fcn("gauss_quadrature", {x}, {f});
432  std::vector<SXElem> f_val(5);
433  for (casadi_int i=0; i<5; ++i)
434  f_val[i] = fcn(SX(xi[i])).at(0).scalar();
435 
436  // Weighted sum
437  SXElem sum;
438  for (casadi_int i=0; i<5; ++i)
439  sum += wi[i]*f_val[i];
440 
441  return sum;
442  }
443 
444  template<>
445  SX CASADI_EXPORT SX::simplify(const SX& x) {
446  SX r = x;
447  for (casadi_int el=0; el<r.nnz(); ++el) {
448  // Start by expanding the node to a weighted sum
449  SX terms, weights;
450  expand(r.nz(el), weights, terms);
451 
452  // Make a scalar product to get the simplified expression
453  r.nz(el) = mtimes(terms.T(), weights);
454  }
455  return r;
456  }
457 
458  template<>
459  std::vector<SX> CASADI_EXPORT
460  SX::substitute(const std::vector<SX>& ex, const std::vector<SX>& v, const std::vector<SX>& vdef) {
461 
462  // Assert consistent dimensions
463  if (v.size()!=vdef.size()) {
464  casadi_warning("subtitute: number of symbols to replace ( " + str(v.size()) + ") "
465  "must match number of expressions (" + str(vdef.size()) + ") "
466  "to replace them with.");
467  }
468 
469  // Quick return if all equal
470  bool all_equal = true;
471  for (casadi_int k=0; k<v.size(); ++k) {
472  if (v[k].size()!=vdef[k].size() || !is_equal(v[k], vdef[k])) {
473  all_equal = false;
474  break;
475  }
476  }
477  if (all_equal) return ex;
478 
479  // Check sparsities
480  for (casadi_int k=0; k<v.size(); ++k) {
481  if (v[k].sparsity()!=vdef[k].sparsity()) {
482  // Expand vdef to sparsity of v if vdef is scalar
483  if (vdef[k].is_scalar() && vdef[k].nnz()==1) {
484  std::vector<SX> vdef_mod = vdef;
485  vdef_mod[k] = SX(v[k].sparsity(), vdef[k]->at(0), false);
486  return substitute(ex, v, vdef_mod);
487  } else {
488  casadi_error("Sparsities of v and vdef must match. Got v: "
489  + v[k].dim() + " and vdef: " + vdef[k].dim() + ".");
490  }
491  }
492  }
493 
494 
495  // Otherwise, evaluate symbolically
496  Function F("tmp_substitute", v, ex, Dict{{"max_io", 0}, {"allow_free", true}});
497  return F(vdef);
498  }
499 
500  template<>
501  SX CASADI_EXPORT SX::substitute(const SX& ex, const SX& v, const SX& vdef) {
502  return substitute(std::vector<SX>{ex}, std::vector<SX>{v}, std::vector<SX>{vdef}).front();
503  }
504 
505  template<>
506  void CASADI_EXPORT SX::substitute_inplace(const std::vector<SX >& v, std::vector<SX >& vdef,
507  std::vector<SX >& ex, bool reverse) {
508  // Assert correctness
509  casadi_assert_dev(v.size()==vdef.size());
510  for (casadi_int i=0; i<v.size(); ++i) {
511  casadi_assert(v[i].is_symbolic(), "the variable is not symbolic");
512  casadi_assert(v[i].sparsity() == vdef[i].sparsity(), "the sparsity patterns of the "
513  "expression and its defining bexpression do not match");
514  }
515 
516  // Quick return if empty or single expression
517  if (v.empty()) return;
518 
519  // Function inputs
520  std::vector<SX> f_in;
521  if (!reverse) f_in.insert(f_in.end(), v.begin(), v.end());
522 
523  // Function outputs
524  std::vector<SX> f_out = vdef;
525  f_out.insert(f_out.end(), ex.begin(), ex.end());
526 
527  // Write the mapping function
528  Function f("tmp_substitute_inplace", f_in, f_out, Dict{{"max_io", 0}, {"allow_free", true}});
529 
530  // Get references to the internal data structures
531  SXFunction *ff = f.get<SXFunction>();
532  const std::vector<ScalarAtomic>& algorithm = ff->algorithm_;
533  std::vector<SXElem> work(f.sz_w());
534 
535  // Iterator to the binary operations
536  std::vector<SXElem>::const_iterator b_it=ff->operations_.begin();
537 
538  // Iterator to stack of constants
539  std::vector<SXElem>::const_iterator c_it = ff->constants_.begin();
540 
541  // Iterator to free variables
542  std::vector<SXElem>::const_iterator p_it = ff->free_vars_.begin();
543 
544  // Evaluate the algorithm
545  for (std::vector<ScalarAtomic>::const_iterator it=algorithm.begin(); it<algorithm.end(); ++it) {
546  switch (it->op) {
547  case OP_INPUT:
548  // reverse is false, substitute out
549  work[it->i0] = vdef.at(it->i1)->at(it->i2);
550  break;
551  case OP_OUTPUT:
552  if (it->i0 < v.size()) {
553  vdef.at(it->i0)->at(it->i2) = work[it->i1];
554  if (reverse) {
555  // Use the new variable henceforth, substitute in
556  work[it->i1] = v.at(it->i0)->at(it->i2);
557  }
558  } else {
559  // Auxiliary output
560  ex.at(it->i0 - v.size())->at(it->i2) = work[it->i1];
561  }
562  break;
563  case OP_CONST: work[it->i0] = *c_it++; break;
564  case OP_PARAMETER: work[it->i0] = *p_it++; break;
565  default:
566  {
567  switch (it->op) {
568  CASADI_MATH_FUN_BUILTIN(work[it->i1], work[it->i2], work[it->i0])
569  }
570 
571  // Avoid creating duplicates
572  const casadi_int depth = 2; // NOTE: a higher depth could possibly give more savings
573  work[it->i0].assignIfDuplicate(*b_it++, depth);
574  }
575  }
576  }
577  }
578 
579  SXElem register_symbol(const SXElem& node, std::map<SXNode*, SXElem>& symbol_map,
580  std::vector<SXElem>& symbol_v, std::vector<SXElem>& parametric_v,
581  bool extract_trivial, casadi_int v_offset,
582  const std::string& v_prefix, const std::string& v_suffix) {
583 
584  // Check if a symbol is already registered
585  auto it = symbol_map.find(node.get());
586 
587  // Ignore trivial expressions if applicable
588  bool is_trivial = node.is_symbolic();
589  if (is_trivial && !extract_trivial) {
590  return node;
591  }
592 
593  if (it==symbol_map.end()) {
594  // Create a symbol and register
595  SXElem sym = SXElem::sym(v_prefix + str(symbol_map.size()+v_offset) + v_suffix);
596  symbol_map[node.get()] = sym;
597 
598  // Make the (symbol,parametric expression) pair available
599  symbol_v.push_back(sym);
600  parametric_v.push_back(node);
601 
602  // Overwrite the argument
603  return sym;
604  } else {
605  // Just use the registered symbol
606  return it->second;
607  }
608  }
609 
610  template<>
611  void CASADI_EXPORT SX::extract_parametric(const SX &expr, const SX& par,
612  SX& expr_ret, std::vector<SX>& symbols, std::vector<SX>& parametric, const Dict& opts) {
613  std::string v_prefix = "e_";
614  std::string v_suffix = "";
615  bool extract_trivial = false;
616  casadi_int v_offset = 0;
617  for (auto&& op : opts) {
618  if (op.first == "prefix") {
619  v_prefix = std::string(op.second);
620  } else if (op.first == "suffix") {
621  v_suffix = std::string(op.second);
622  } else if (op.first == "offset") {
623  v_offset = op.second;
624  } else if (op.first == "extract_trivial") {
625  extract_trivial = op.second;
626  } else {
627  casadi_error("No such option: " + std::string(op.first));
628  }
629  }
630  Function f("f", std::vector<SX>{par},
631  std::vector<SX>{expr}, {{"live_variables", false},
632  {"max_io", 0}, {"allow_free", true}});
633  SXFunction *ff = f.get<SXFunction>();
634 
635  // Each work vector element has (const, lin, nonlin) part
636  std::vector< SXElem > w(ff->worksize_);
637 
638  // Status of the expression:
639  // 0: dependant on constants only
640  // 1: dependant on parameters/constants only
641  // 2: dependant on non-parameters
642  std::vector< char > expr_status(ff->worksize_, 0);
643 
644  // Iterator to the binary operations
645  std::vector<SXElem>::const_iterator b_it=ff->operations_.begin();
646 
647  // Iterator to stack of constants
648  std::vector<SXElem>::const_iterator c_it = ff->constants_.begin();
649 
650  // Iterator to free variables
651  std::vector<SXElem>::const_iterator p_it = ff->free_vars_.begin();
652 
653  // Get argument nonzeros
654  const SXElem* arg = get_ptr(par.nonzeros());
655 
656  // Allocate space to write results to
657  expr_ret = SX::zeros(expr.sparsity());
658  std::vector<SXElem>& ret = expr_ret.nonzeros();
659 
660  // Map of registered symbols
661  std::map<SXNode*, SXElem> symbol_map;
662 
663  // Flat list of registerd symbols and parametric expressions
664  std::vector<SXElem> symbol_v, parametric_v;
665 
666  // Evaluate algorithm
667  for (auto&& a : ff->algorithm_) {
668  switch (a.op) {
669  case OP_INPUT:
670  w[a.i0] = arg[a.i2];
671  expr_status[a.i0] = 1;
672  break;
673  case OP_OUTPUT:
674  casadi_assert_dev(a.i0==0);
675  {
676  SXElem arg = w[a.i1];
677  if (expr_status[a.i1]==1) {
678  arg = register_symbol(arg, symbol_map, symbol_v, parametric_v,
679  extract_trivial, v_offset, v_prefix, v_suffix);
680  }
681  ret[a.i2] = arg;
682  }
683  break;
684  case OP_CONST:
685  w[a.i0] = *c_it++;
686  expr_status[a.i0] = 0;
687  break;
688  case OP_PARAMETER:
689  w[a.i0] = *p_it++;
690  expr_status[a.i0] = 2;
691  break;
692  case OP_CALL:
693  {
694  const auto& m = ff->call_.el.at(a.i1);
695  const SXElem& orig = *b_it++;
696  std::vector<SXElem> deps(m.n_dep);
697 
698  bool identical = true;
699  for (casadi_int i=0;i<m.n_dep;++i) {
700  identical &= SXElem::is_equal(w[m.dep.at(i)], orig->dep(i), 2);
701  }
702 
703  // Check worst case status of inputs
704  char max_status = 0;
705  for (casadi_int i=0;i<m.n_dep;++i) {
706  max_status = std::max(max_status, expr_status[m.dep[i]]);
707  }
708 
709  bool any_tainted = max_status==2;
710 
711  if (any_tainted) {
712  // Loop over inputs
713  for (casadi_int i=0;i<m.n_dep;++i) {
714  // Skip if already tainted
715  if (expr_status[m.dep[i]]==2) continue;
716  // Skip if it is a constant
717  if (expr_status[m.dep[i]]==0) continue;
718 
719  w[m.dep[i]] = register_symbol(w[m.dep[i]], symbol_map, symbol_v, parametric_v,
720  extract_trivial, v_offset, v_prefix, v_suffix);
721 
722  identical = false;
723  }
724  }
725 
726  std::vector<SXElem> ret;
727 
728  if (identical) {
729  for (casadi_int i=0;i<m.n_res;++i) {
730  ret.push_back(orig.get_output(i));
731  }
732  } else {
733  for (casadi_int i=0;i<m.n_dep;++i) deps[i] = w[m.dep[i]];
734  ret = SXElem::call(m.f, deps);
735  }
736 
737  // Update expression status
738  for (casadi_int i=0;i<m.n_res;++i) {
739  if (m.res[i]>=0) expr_status[m.res[i]] = max_status;
740  }
741 
742  for (casadi_int i=0;i<m.n_res;++i) {
743  if (m.res[i]>=0) w[m.res[i]] = ret[i];
744  }
745  }
746  break;
747  default:
748  {
749  bool is_binary = casadi_math<SXElem>::is_binary(a.op);
750 
751  SXElem w1 = w[a.i1];
752  SXElem w2 = is_binary ? w[a.i2] : 0;
753  // Check worst case status of inputs
754  char max_status = expr_status[a.i1];
755  if (casadi_math<SXElem>::is_binary(a.op)) {
756  max_status = std::max(max_status, expr_status[a.i2]);
757  }
758  bool any_tainted = max_status==2;
759 
760  if (any_tainted) {
761  // Loop over inputs
762  for (int k=0;k<1+is_binary;++k) {
763  // Skip if already tainted
764  casadi_int el = k==0 ? a.i1 : a.i2;
765  if (expr_status[el]==2) continue;
766  // Skip if it is a constant
767  if (expr_status[el]==0) continue;
768 
769  SXElem& arg = k==0 ? w1 : w2;
770 
771  arg = register_symbol(arg, symbol_map, symbol_v, parametric_v,
772  extract_trivial, v_offset, v_prefix, v_suffix);
773  }
774  }
775 
776  // Evaluate the function to a temporary value
777  // (as it might overwrite the children in the work vector)
778  SXElem f;
779  switch (a.op) {
780  CASADI_MATH_FUN_BUILTIN(w1, w2, f)
781  }
782 
783  w[a.i0] = f;
784 
785  // Avoid creating duplicates
786  const casadi_int depth = 2; // NOTE: a higher depth could possibly give more savings
787  w[a.i0].assignIfDuplicate(*b_it++, depth);
788 
789  // Update expression status
790  expr_status[a.i0] = max_status;
791  }
792  }
793  }
794 
795  symbols.resize(symbol_v.size());
796  parametric.resize(parametric_v.size());
797 
798  for (casadi_int i=0;i<symbol_v.size();++i) {
799  symbols[i] = symbol_v[i];
800  parametric[i] = parametric_v[i];
801  }
802  }
803 
804  template<>
805  void CASADI_EXPORT SX::separate_linear(const SX &expr,
806  const SX &sym_lin, const SX &sym_const,
807  SX& expr_const, SX& expr_lin, SX& expr_nonlin) {
808 
809  Function f("f", std::vector<SX>{sym_const, sym_lin},
810  std::vector<SX>{expr}, {{"live_variables", false},
811  {"max_io", 0}});
812  SXFunction *ff = f.get<SXFunction>();
813  //f.disp(uout(), true);
814 
815  expr_const = SX::zeros(expr.sparsity());
816  expr_lin = SX::zeros(expr.sparsity());
817  expr_nonlin = SX::zeros(expr.sparsity());
818 
819  std::vector<SXElem*> ret = {
820  get_ptr(expr_const.nonzeros()),
821  get_ptr(expr_lin.nonzeros()),
822  get_ptr(expr_nonlin.nonzeros())};
823 
824  // Each work vector element has (const, lin, nonlin) part
825  std::vector< std::array<SXElem, 3> > w(ff->worksize_,
826  std::array<SXElem, 3>{{0, 0, 0}});
827 
828  std::vector<const SXElem*> arg(f.sz_arg());
829  arg[0] = get_ptr(sym_const.nonzeros());
830  arg[1] = get_ptr(sym_lin.nonzeros());
831 
832  // Iterator to stack of constants
833  std::vector<SXElem>::const_iterator c_it = ff->constants_.begin();
834 
835  // Iterator to free variables
836  std::vector<SXElem>::const_iterator p_it = ff->free_vars_.begin();
837 
838  // Evaluate algorithm
839  for (auto&& a : ff->algorithm_) {
840  switch (a.op) {
841  case OP_INPUT:
842  w[a.i0][a.i1] = arg[a.i1]==nullptr ? 0 : arg[a.i1][a.i2];
843  break;
844  case OP_OUTPUT:
845  casadi_assert_dev(a.i0==0);
846  ret[0][a.i2] = w[a.i1][0];
847  ret[1][a.i2] = w[a.i1][1];
848  ret[2][a.i2] = w[a.i1][2];
849  break;
850  case OP_CONST:
851  w[a.i0][0] = *c_it++;
852  break;
853  case OP_PARAMETER:
854  w[a.i0][2] = *p_it++;
855  break;
856  case OP_CALL:
857  casadi_error("Not implemented");
858  default:
859  casadi_math<SXElem>::fun_linear(a.op, w[a.i1].data(), w[a.i2].data(), w[a.i0].data());
860  }
861  }
862  }
863 
864 
865  template<>
866  bool CASADI_EXPORT SX::depends_on(const SX &x, const SX &arg) {
867  if (x.nnz()==0) return false;
868 
869  // Construct a temporary algorithm
870  Function temp("tmp_depends_on", {arg}, {x}, Dict{{"max_io", 0}, {"allow_free", true}});
871 
872  // Perform a single dependency sweep
873  std::vector<bvec_t> t_in(arg.nnz(), 1), t_out(x.nnz());
874  temp({get_ptr(t_in)}, {get_ptr(t_out)});
875 
876  // Loop over results
877  for (casadi_int i=0; i<t_out.size(); ++i) {
878  if (t_out[i]) return true;
879  }
880 
881  return false;
882  }
883 
884  template<>
885  bool CASADI_EXPORT SX::contains_all(const std::vector<SX>& v, const std::vector<SX> &n) {
886  if (n.empty()) return true;
887 
888  // Set to contain all nodes
889  std::set<SXNode*> l;
890  for (const SX& e : v) l.insert(e.scalar().get());
891 
892  size_t l_unique = l.size();
893 
894  for (const SX& e : n) l.insert(e.scalar().get());
895 
896  return l.size()==l_unique;
897  }
898 
899  template<>
900  bool CASADI_EXPORT SX::contains_any(const std::vector<SX>& v, const std::vector<SX> &n) {
901  if (n.empty()) return true;
902 
903  // Set to contain all nodes
904  std::set<SXNode*> l;
905  for (const SX& e : v) l.insert(e.scalar().get());
906 
907  size_t l_unique = l.size();
908 
909  std::set<SXNode*> r;
910  for (const SX& e : n) r.insert(e.scalar().get());
911 
912  size_t r_unique = r.size();
913  for (const SX& e : n) l.insert(e.scalar().get());
914 
915  return l.size()<l_unique+r_unique;
916  }
917 
918  class IncrementalSerializer {
924  public:
925 
926  IncrementalSerializer() : serializer(ss) {
927  }
928 
929  std::string pack(const SXElem& a) {
930  // Serialization goes wrong if serialized SXNodes get destroyed
931  ref.push_back(a);
932  a.serialize(serializer);
933  ss.str("");
934  ss.clear();
935  a.serialize(serializer);
936  std::string ret = ss.str();
937  ss.str("");
938  ss.clear();
939  return ret;
940  }
941 
942  private:
943  std::stringstream ss;
944  // List of references to keep alive
945  std::vector<SXElem> ref;
946  SerializingStream serializer;
947  };
948 
949 
950  template<>
951  std::vector<SX> CASADI_EXPORT SX::cse(const std::vector<SX>& e) {
952 
953  SX c = veccat(e);
954  //std::vector<SX> args = symvar(c);
955  Function f("f", std::vector<SX>{}, e, {{"live_variables", false},
956  {"max_io", 0}, {"cse", false}, {"allow_free", true}});
957  SXFunction *ff = f.get<SXFunction>();
958 
959  std::vector<SX> ret;
960  for (casadi_int i=0;i<e.size();++i) {
961  ret.push_back(SX::zeros(e.at(i).sparsity()));
962  }
963 
964  // Symbolic work, non-differentiated
965  std::vector<SXElem> w(ff->worksize_);
966 
967  std::vector<const SXElem*> arg(f.sz_arg());
968  /*for (casadi_int i=0;i<args.size();++i) {
969  arg[i] = get_ptr(args.at(i).nonzeros());
970  }*/
971 
972  std::vector<SXElem*> res(f.sz_res());
973  for (casadi_int i=0;i<e.size();++i) {
974  res[i] = get_ptr(ret.at(i).nonzeros());
975  }
976 
977  std::unordered_map<std::string, SXElem > cache;
978  IncrementalSerializer s;
979 
980  // Iterator to stack of constants
981  std::vector<SXElem>::const_iterator c_it = ff->constants_.begin();
982 
983  // Iterator to free variables
984  std::vector<SXElem>::const_iterator p_it = ff->free_vars_.begin();
985 
986  std::unordered_map<std::string, Function> function_cache;
987 
988  // Evaluate algorithm
989  for (auto&& a : ff->algorithm_) {
990  switch (a.op) {
991  case OP_INPUT:
992  w[a.i0] = arg[a.i1]==nullptr ? 0 : arg[a.i1][a.i2];
993  if (arg[a.i1]!=nullptr) cache[s.pack(w[a.i0])] = w[a.i0];
994  break;
995  case OP_OUTPUT:
996  if (res[a.i0]!=nullptr) res[a.i0][a.i2] = w[a.i1];
997  break;
998  case OP_CONST:
999  w[a.i0] = *c_it++;
1000  cache[s.pack(w[a.i0])] = w[a.i0];
1001  break;
1002  case OP_PARAMETER:
1003  w[a.i0] = *p_it++;
1004  cache[s.pack(w[a.i0])] = w[a.i0];
1005  break;
1006  case OP_CALL:
1007  {
1008  const auto& m = ff->call_.el.at(a.i1);
1009 
1010  // Retrieve dependencies from w
1011  std::vector<SXElem> deps(m.n_dep);
1012  for (casadi_int i=0;i<m.n_dep;++i) deps[i] = w[m.dep[i]];
1013 
1014  // Cache Function
1015  std::string key = m.f.serialize();
1016  auto itk = function_cache.find(key);
1017  if (itk==function_cache.end()) {
1018  function_cache[key] = m.f;
1019  }
1020 
1021  // Make the call
1022  std::vector<SXElem> ret = SXElem::call(function_cache[key], deps);
1023 
1024  SXElem call_node = ret[0].dep(0);
1025 
1026  // Is the call node in cache?
1027  key = s.pack(call_node);
1028  auto it = cache.find(key);
1029  if (it==cache.end()) {
1030  // No, add it
1031  cache[key] = call_node;
1032  } else {
1033  // Yes, use it
1034  call_node = it->second;
1035  // Loop over all results
1036  for (casadi_int i=0; i<ret.size(); ++i) {
1037  // Create new output nodes
1038  ret[i] = call_node.get_output(ret[i].which_output());
1039  }
1040  }
1041 
1042  // Store results into w
1043  for (casadi_int i=0;i<m.n_res;++i) {
1044  if (m.res[i]>=0) w[m.res[i]] = ret[i];
1045  }
1046  }
1047  break;
1048  default:
1049  {
1050 
1051  // Evaluate the function to a temporary value
1052  // (as it might overwrite the children in the work vector)
1053  SXElem f;
1054  // Missing simplifications like [x+y]->[twice]
1055  switch (a.op) {
1056  CASADI_MATH_FUN_BUILTIN(w[a.i1], w[a.i2], f)
1057  default:
1058  casadi_error("Not implemented");
1059  }
1060 
1061  std::string key = s.pack(f);
1062 
1063  auto itk = cache.find(key);
1064  if (itk==cache.end()) {
1065  cache[key] = f;
1066  } else {
1067  f = itk->second;
1068  }
1069 
1070  // Finally save the function value
1071  w[a.i0] = f;
1072  }
1073  }
1074  }
1075  return ret;
1076  }
1077 
1078  template<>
1079  SX CASADI_EXPORT SX::jacobian(const SX &f, const SX &x, const Dict& opts) {
1080  // Propagate verbose option to helper function
1081  Dict h_opts;
1082  Dict opts_remainder = extract_from_dict(opts, "helper_options", h_opts);
1083  h_opts["allow_free"] = true;
1084  Function h("jac_helper", {x}, {f}, h_opts);
1085  return h.get<SXFunction>()->jac(opts_remainder).at(0);
1086  }
1088  template<>
1089  SX CASADI_EXPORT SX::hessian(const SX &ex, const SX &arg, SX &g, const Dict& opts) {
1090  Dict all_opts = opts;
1091  if (!opts.count("symmetric")) all_opts["symmetric"] = true;
1092  g = gradient(ex, arg);
1093  return jacobian(g, arg, all_opts);
1094  }
1095 
1096  template<>
1097  SX CASADI_EXPORT SX::hessian(const SX &ex, const SX &arg, const Dict& opts) {
1098  SX g;
1099  return hessian(ex, arg, g, opts);
1100  }
1101 
1102  template<>
1103  std::vector<std::vector<SX> > CASADI_EXPORT
1104  SX::forward(const std::vector<SX> &ex, const std::vector<SX> &arg,
1105  const std::vector<std::vector<SX> > &v, const Dict& opts) {
1106 
1107  Dict h_opts;
1108  Dict opts_remainder = extract_from_dict(opts, "helper_options", h_opts);
1109  h_opts["allow_free"] = true;
1110  // Read options
1111  bool always_inline = false;
1112  bool never_inline = false;
1113  for (auto&& op : opts_remainder) {
1114  if (op.first=="always_inline") {
1115  always_inline = op.second;
1116  } else if (op.first=="never_inline") {
1117  never_inline = op.second;
1118  } else {
1119  casadi_error("No such option: " + std::string(op.first));
1120  }
1121  }
1122  // Call internal function on a temporary object
1123  Function temp("forward_temp", arg, ex, h_opts);
1124  std::vector<std::vector<SX> > ret;
1125  temp->call_forward(arg, ex, v, ret, always_inline, never_inline);
1126  return ret;
1127  }
1128 
1129  template<>
1130  std::vector<std::vector<SX> > CASADI_EXPORT
1131  SX::reverse(const std::vector<SX> &ex, const std::vector<SX> &arg,
1132  const std::vector<std::vector<SX> > &v, const Dict& opts) {
1133 
1134  Dict h_opts;
1135  Dict opts_remainder = extract_from_dict(opts, "helper_options", h_opts);
1136  h_opts["allow_free"] = true;
1137  // Read options
1138  bool always_inline = false;
1139  bool never_inline = false;
1140  for (auto&& op : opts_remainder) {
1141  if (op.first=="always_inline") {
1142  always_inline = op.second;
1143  } else if (op.first=="never_inline") {
1144  never_inline = op.second;
1145  } else {
1146  casadi_error("No such option: " + std::string(op.first));
1147  }
1148  }
1149  // Call internal function on a temporary object
1150  Function temp("reverse_temp", arg, ex, h_opts);
1151  std::vector<std::vector<SX> > ret;
1152  temp->call_reverse(arg, ex, v, ret, always_inline, never_inline);
1153  return ret;
1154  }
1155 
1156  template<>
1157  std::vector<bool> CASADI_EXPORT SX::which_depends(const SX &expr,
1158  const SX &var, casadi_int order, bool tr) {
1159  return _which_depends(expr, var, order, tr);
1160  }
1161 
1162  template<>
1163  Sparsity CASADI_EXPORT SX::jacobian_sparsity(const SX &f, const SX &x) {
1164  return _jacobian_sparsity(f, x);
1165  }
1166 
1167  template<>
1168  SX CASADI_EXPORT SX::taylor(const SX& f, const SX& x,
1169  const SX& a, casadi_int order) {
1170  casadi_assert_dev(x.is_scalar() && a.is_scalar());
1171  if (f.nnz()!=f.numel())
1172  throw CasadiException("taylor: not implemented for sparse matrices");
1173  SX ff = vec(f.T());
1174 
1175  SX result = substitute(ff, x, a);
1176  double nf=1;
1177  SX dx = (x-a);
1178  SX dxa = (x-a);
1179  for (casadi_int i=1; i<=order; i++) {
1180  ff = jacobian(ff, x);
1181  nf*=static_cast<double>(i);
1182  result+=1/nf * substitute(ff, x, a) * dxa;
1183  dxa*=dx;
1184  }
1185  return reshape(result, f.size2(), f.size1()).T();
1186  }
1187 
1188  SX mtaylor_recursive(const SX& ex, const SX& x, const SX& a, casadi_int order,
1189  const std::vector<casadi_int>&order_contributions,
1190  const SXElem & current_dx=casadi_limits<SXElem>::one,
1191  double current_denom=1, casadi_int current_order=1) {
1192  SX result = substitute(ex, x, a)*current_dx/current_denom;
1193  for (casadi_int i=0;i<x.nnz();i++) {
1194  if (order_contributions[i]<=order) {
1195  result += mtaylor_recursive(SX::jacobian(ex, x->at(i)),
1196  x, a,
1197  order-order_contributions[i],
1198  order_contributions,
1199  current_dx*(x->at(i)-a->at(i)),
1200  current_denom*static_cast<double>(current_order),
1201  current_order+1);
1202  }
1203  }
1204  return result;
1205  }
1206 
1207  template<>
1208  SX CASADI_EXPORT SX::mtaylor(const SX& f, const SX& x, const SX& a, casadi_int order,
1209  const std::vector<casadi_int>& order_contributions) {
1210  casadi_assert(f.nnz()==f.numel() && x.nnz()==x.numel(),
1211  "mtaylor: not implemented for sparse matrices");
1212 
1213  casadi_assert(x.nnz()==order_contributions.size(),
1214  "mtaylor: number of non-zero elements in x (" + str(x.nnz())
1215  + ") must match size of order_contributions ("
1216  + str(order_contributions.size()) + ")");
1217 
1218  return reshape(mtaylor_recursive(vec(f), x, a, order,
1219  order_contributions),
1220  f.size2(), f.size1()).T();
1221  }
1222 
1223  template<>
1224  SX CASADI_EXPORT SX::mtaylor(const SX& f, const SX& x, const SX& a, casadi_int order) {
1225  return mtaylor(f, x, a, order, std::vector<casadi_int>(x.nnz(), 1));
1226  }
1227 
1228  template<>
1229  casadi_int CASADI_EXPORT SX::n_nodes(const SX& x) {
1230  Dict opts{{"max_io", 0}, {"cse", false}, {"allow_free", true}};
1231  Function f("tmp_n_nodes", {SX()}, {x}, opts);
1232  return f.n_nodes();
1233  }
1234 
1235  template<>
1236  std::string CASADI_EXPORT
1237  SX::print_operator(const SX& X, const std::vector<std::string>& args) {
1238  SXElem x = X.scalar();
1239  casadi_int ndeps = casadi_math<double>::ndeps(x.op());
1240  casadi_assert(ndeps==1 || ndeps==2, "Not a unary or binary operator");
1241  casadi_assert(args.size()==ndeps, "Wrong number of arguments");
1242  if (ndeps==1) {
1243  return casadi_math<double>::print(x.op(), args.at(0));
1244  } else {
1245  return casadi_math<double>::print(x.op(), args.at(0), args.at(1));
1246  }
1247  }
1248 
1249  template<>
1250  std::vector<SX> CASADI_EXPORT SX::symvar(const SX& x) {
1251  Dict opts{{"max_io", 0}, {"cse", false}, {"allow_free", true}};
1252  Function f("tmp_symvar", std::vector<SX>{}, {x}, opts);
1253  return f.free_sx();
1254  }
1255 
1256  template<>
1257  void CASADI_EXPORT SX::extract(std::vector<SX>& ex, std::vector<SX>& v_sx,
1258  std::vector<SX>& vdef_sx, const Dict& opts) {
1259  // Read options
1260  std::string v_prefix = "v_", v_suffix = "";
1261  bool lift_shared = true, lift_calls = false;
1262  casadi_int v_ind = 0;
1263  for (auto&& op : opts) {
1264  if (op.first == "prefix") {
1265  v_prefix = std::string(op.second);
1266  } else if (op.first == "suffix") {
1267  v_suffix = std::string(op.second);
1268  } else if (op.first == "lift_shared") {
1269  lift_shared = op.second;
1270  } else if (op.first == "lift_calls") {
1271  lift_calls = op.second;
1272  } else if (op.first == "offset") {
1273  v_ind = op.second;
1274  } else {
1275  casadi_error("No such option: " + std::string(op.first));
1276  }
1277  }
1278  // Partially implemented
1279  casadi_assert(lift_shared, "Not implemented");
1280  casadi_assert(!lift_calls, "Not implemented");
1281  // Sort the expression
1282  Function f("tmp_extract", std::vector<SX>(), ex, Dict{{"max_io", 0}, {"allow_free", true}});
1283  SXFunction *ff = f.get<SXFunction>();
1284  // Get references to the internal data structures
1285  const std::vector<ScalarAtomic>& algorithm = ff->algorithm_;
1286  std::vector<SXElem> work(f.sz_w());
1287  std::vector<SXElem> work2 = work;
1288  // Iterator to the binary operations
1289  std::vector<SXElem>::const_iterator b_it=ff->operations_.begin();
1290  // Iterator to stack of constants
1291  std::vector<SXElem>::const_iterator c_it = ff->constants_.begin();
1292  // Iterator to free variables
1293  std::vector<SXElem>::const_iterator p_it = ff->free_vars_.begin();
1294  // Count how many times an expression has been used
1295  std::vector<casadi_int> usecount(work.size(), 0);
1296  // Evaluate the algorithm
1297  std::vector<SXElem> v, vdef;
1298  for (std::vector<ScalarAtomic>::const_iterator it=algorithm.begin(); it<algorithm.end(); ++it) {
1299  // Increase usage counters
1300  switch (it->op) {
1301  case OP_CONST:
1302  case OP_PARAMETER:
1303  break;
1304  CASADI_MATH_BINARY_BUILTIN // Binary operation
1305  case OP_IF_ELSE_ZERO:
1306  if (usecount[it->i2]==0) {
1307  usecount[it->i2]=1;
1308  } else if (usecount[it->i2]==1) {
1309  // Get a suitable name
1310  vdef.push_back(work[it->i2]);
1311  usecount[it->i2]=-1; // Extracted, do not extract again
1312  }
1313  // fall-through
1314  case OP_OUTPUT:
1315  default: // Unary operation, binary operation or output
1316  if (usecount[it->i1]==0) {
1317  usecount[it->i1]=1;
1318  } else if (usecount[it->i1]==1) {
1319  vdef.push_back(work[it->i1]);
1320  usecount[it->i1]=-1; // Extracted, do not extract again
1321  }
1322  }
1323  // Perform the operation
1324  switch (it->op) {
1325  case OP_OUTPUT:
1326  break;
1327  case OP_CONST:
1328  case OP_PARAMETER:
1329  usecount[it->i0] = -1; // Never extract since it is a primitive type
1330  break;
1331  default:
1332  work[it->i0] = *b_it++;
1333  usecount[it->i0] = 0; // Not (yet) extracted
1334  break;
1335  }
1336  }
1337  // Create intermediate variables
1338  std::stringstream v_name;
1339  for (casadi_int i=0; i<vdef.size(); ++i) {
1340  v_name.str(std::string());
1341  v_name << v_prefix << (v_ind++) << v_suffix;
1342  v.push_back(SXElem::sym(v_name.str()));
1343  }
1344  // Consistency check
1345  casadi_assert(vdef.size() < std::numeric_limits<int>::max(), "Integer overflow");
1346  // Mark the above expressions
1347  for (casadi_int i=0; i<vdef.size(); ++i) {
1348  vdef[i].set_temp(static_cast<int>(i)+1);
1349  }
1350  // Save the marked nodes for later cleanup
1351  std::vector<SXElem> marked = vdef;
1352  // Reset iterator
1353  b_it=ff->operations_.begin();
1354  // Evaluate the algorithm
1355  for (std::vector<ScalarAtomic>::const_iterator it=algorithm.begin(); it<algorithm.end(); ++it) {
1356  switch (it->op) {
1357  case OP_OUTPUT: ex.at(it->i0)->at(it->i2) = work[it->i1]; break;
1358  case OP_CONST: work2[it->i0] = work[it->i0] = *c_it++; break;
1359  case OP_PARAMETER: work2[it->i0] = work[it->i0] = *p_it++; break;
1360  default:
1361  {
1362  switch (it->op) {
1363  CASADI_MATH_FUN_BUILTIN(work[it->i1], work[it->i2], work[it->i0])
1364  }
1365  work2[it->i0] = *b_it++;
1366  // Replace with intermediate variables
1367  casadi_int ind = work2[it->i0].get_temp()-1;
1368  if (ind>=0) {
1369  vdef.at(ind) = work[it->i0];
1370  work[it->i0] = v.at(ind);
1371  }
1372  }
1373  }
1374  }
1375  // Unmark the expressions
1376  for (std::vector<SXElem>::iterator it=marked.begin(); it!=marked.end(); ++it) {
1377  it->set_temp(0);
1378  }
1379  // Save v, vdef
1380  v_sx.resize(v.size());
1381  std::copy(v.begin(), v.end(), v_sx.begin());
1382  vdef_sx.resize(vdef.size());
1383  std::copy(vdef.begin(), vdef.end(), vdef_sx.begin());
1384  }
1385 
1386  template<>
1387  void CASADI_EXPORT SX::shared(std::vector<SX >& ex,
1388  std::vector<SX >& v,
1389  std::vector<SX >& vdef,
1390  const std::string& v_prefix,
1391  const std::string& v_suffix) {
1392  // Call new, more generic function
1393  return extract(ex, v, vdef, Dict{{"lift_shared", true}, {"lift_calls", false},
1394  {"prefix", v_prefix}, {"suffix", v_suffix}});
1395  }
1396 
1397  template<>
1398  SX CASADI_EXPORT SX::poly_coeff(const SX& ex, const SX& x) {
1399  casadi_assert_dev(ex.is_scalar());
1400  casadi_assert_dev(x.is_scalar());
1401  casadi_assert_dev(x.is_symbolic());
1402 
1403  std::vector<SXElem> r;
1404 
1405  SX j = ex;
1406  casadi_int mult = 1;
1407  bool success = false;
1408  for (casadi_int i=0; i<1000; ++i) {
1409  r.push_back((substitute(j, x, 0)/static_cast<double>(mult)).scalar());
1410  j = jacobian(j, x);
1411  if (j.nnz()==0) {
1412  success = true;
1413  break;
1414  }
1415  mult*=i+1;
1416  }
1417 
1418  if (!success) casadi_error("poly: supplied expression does not appear to be polynomial.");
1419 
1420  std::reverse(r.begin(), r.end());
1421 
1422  return r;
1423  }
1424 
1425  template<>
1426  SX CASADI_EXPORT SX::poly_roots(const SX& p) {
1427  casadi_assert(p.size2()==1,
1428  "poly_root(): supplied parameter must be column vector but got "
1429  + p.dim() + ".");
1430  casadi_assert_dev(p.is_dense());
1431  if (p.size1()==2) { // a*x + b
1432  SX a = p(0);
1433  SX b = p(1);
1434  return -b/a;
1435  } else if (p.size1()==3) { // a*x^2 + b*x + c
1436  SX a = p(0);
1437  SX b = p(1);
1438  SX c = p(2);
1439  SX ds = sqrt(b*b-4*a*c);
1440  SX bm = -b;
1441  SX a2 = 2*a;
1442  SX ret = SX::vertcat({(bm-ds)/a2, (bm+ds)/a2});
1443  return ret;
1444  } else if (p.size1()==4) {
1445  // www.cs.iastate.edu/~cs577/handouts/polyroots.pdf
1446  SX ai = 1/p(0);
1447 
1448  SX p_ = p(1)*ai;
1449  SX q = p(2)*ai;
1450  SX r = p(3)*ai;
1451 
1452  SX pp = p_*p_;
1453 
1454  SX a = q - pp/3;
1455  SX b = r + 2.0/27*pp*p_-p_*q/3;
1456 
1457  SX a3 = a/3;
1458 
1459  SX phi = acos(-b/2/sqrt(-a3*a3*a3));
1460 
1461  SX ret = SX::vertcat({cos(phi/3), cos((phi+2*pi)/3), cos((phi+4*pi)/3)});
1462  ret*= 2*sqrt(-a3);
1463 
1464  ret-= p_/3;
1465  return ret;
1466  } else if (p.size1()==5) {
1467  SX ai = 1/p(0);
1468  SX b = p(1)*ai;
1469  SX c = p(2)*ai;
1470  SX d = p(3)*ai;
1471  SX e = p(4)*ai;
1472 
1473  SX bb= b*b;
1474  SX f = c - (3*bb/8);
1475  SX g = d + (bb*b / 8) - b*c/2;
1476  SX h = e - (3*bb*bb/256) + (bb * c/16) - (b*d/4);
1477  SX poly = SX::vertcat({1, f/2, ((f*f -4*h)/16), -g*g/64});
1478  SX y = poly_roots(poly);
1479 
1480  SX r0 = y(0); // NOLINT(cppcoreguidelines-slicing)
1481  SX r1 = y(2); // NOLINT(cppcoreguidelines-slicing)
1482 
1483  SX p = sqrt(r0); // two non-zero-roots
1484  SX q = sqrt(r1);
1485 
1486  SX r = -g/(8*p*q);
1487 
1488  SX s = b/4;
1489 
1490  SX ret = SX::vertcat({
1491  p + q + r -s,
1492  p - q - r -s,
1493  -p + q - r -s,
1494  -p - q + r -s});
1495  return ret;
1496  } else if (is_equal(p(p.nnz()-1)->at(0), 0)) {
1497  SX ret = SX::vertcat({poly_roots(p(range(p.nnz()-1))), 0});
1498  return ret;
1499  } else {
1500  casadi_error("poly_root(): can only solve cases for first or second order polynomial. "
1501  "Got order " + str(p.size1()-1) + ".");
1502  }
1503 
1504  }
1505 
1506  template<>
1507  SX CASADI_EXPORT SX::eig_symbolic(const SX& m) {
1508  casadi_assert(m.size1()==m.size2(), "eig(): supplied matrix must be square");
1509 
1510  std::vector<SX> ret;
1511 
1513  std::vector<casadi_int> offset;
1514  std::vector<casadi_int> index;
1515  casadi_int nb = m.sparsity().scc(offset, index);
1516 
1517  SX m_perm = m(offset, offset);
1518 
1519  SX l = SX::sym("l");
1520 
1521  for (casadi_int k=0; k<nb; ++k) {
1522  std::vector<casadi_int> r = range(index.at(k), index.at(k+1));
1523  // det(lambda*I-m) = 0
1524  ret.push_back(poly_roots(poly_coeff(det(SX::eye(r.size())*l-m_perm(r, r)), l)));
1525  }
1526 
1527  return vertcat(ret);
1528  }
1529 
1530  template<>
1531  std::vector<SXElem> CASADI_EXPORT SX::call(const Function& f, const std::vector<SXElem>& dep) {
1532  return SXElem::call(f, dep);
1533  }
1534 
1535  template<>
1536  void CASADI_EXPORT SX::print_split(casadi_int nnz, const SXElem* nonzeros,
1537  std::vector<std::string>& nz,
1538  std::vector<std::string>& inter) {
1539  // Find out which noded can be inlined
1540  std::map<const SXNode*, casadi_int> nodeind;
1541  for (casadi_int i=0; i<nnz; ++i) nonzeros[i]->can_inline(nodeind);
1542 
1543  // Print expression
1544  nz.resize(0);
1545  nz.reserve(nnz);
1546  inter.resize(0);
1547  for (casadi_int i=0; i<nnz; ++i) nz.push_back(nonzeros[i]->print_compact(nodeind, inter));
1548  }
1549 
1550  template<> std::vector<SX> CASADI_EXPORT SX::get_input(const Function& f) {
1551  return f.sx_in();
1552  }
1553 
1554  template<> std::vector<SX> CASADI_EXPORT SX::get_free(const Function& f) {
1555  return f.free_sx();
1556  }
1557 
1558  template<>
1559  Dict CASADI_EXPORT SX::info() const {
1560  return {{"function", Function("f", std::vector<SX>{}, std::vector<SX>{*this})}};
1561  }
1562 
1563  template<>
1564  void CASADI_EXPORT SX::to_file(const std::string& filename,
1565  const Sparsity& sp, const SXElem* nonzeros,
1566  const std::string& format_hint) {
1567  casadi_error("Not implemented");
1568  }
1569 
1570 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
1571  template<>
1572  CASADI_EXPORT std::mutex& SX::get_mutex_temp() {
1573  return SXElem::mutex_temp;
1574  }
1575 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
1576 
1577 #if __GNUC__
1578 #pragma GCC diagnostic push
1579 #pragma GCC diagnostic ignored "-Wattributes"
1580 #endif
1581 template class CASADI_EXPORT Matrix< SXElem >;
1582 #if __GNUC__
1583 #pragma GCC diagnostic pop
1584 #endif
1585 
1586 } // namespace casadi
Casadi exception class.
Definition: exception.hpp:77
Function object.
Definition: function.hpp:60
FunctionInternal * get() const
Definition: function.cpp:353
const SX sx_in(casadi_int iind) const
Get symbolic primitives equivalent to the input expressions.
Definition: function.cpp:1552
std::vector< SX > free_sx() const
Get all the free variables of the function.
Definition: function.cpp:1673
casadi_int numel() const
Get the number of elements.
bool is_dense() const
Check if the matrix expression is dense.
std::pair< casadi_int, casadi_int > size() const
Get the shape.
casadi_int nnz() const
Get the number of (structural) non-zero elements.
casadi_int size2() const
Get the second dimension (i.e. number of columns)
casadi_int size1() const
Get the first dimension (i.e. number of rows)
std::string dim(bool with_nz=false) const
Get string representation of dimensions.
static Matrix< Scalar > zeros(casadi_int nrow=1, casadi_int ncol=1)
Create a dense matrix or a matrix with specified sparsity with all entries zero.
bool is_scalar(bool scalar_and_dense=false) const
Check if the matrix expression is scalar.
static MX find(const MX &x)
Definition: mx.cpp:2108
Sparse matrix class. SX and DM are specializations.
Definition: matrix_decl.hpp:99
casadi_int which_output() const
Get the index of evaluation output - only valid when is_output() is true.
std::vector< Scalar > & nonzeros()
static std::vector< std::vector< Matrix< Scalar > > > reverse(const std::vector< Matrix< Scalar > > &ex, const std::vector< Matrix< Scalar > > &arg, const std::vector< std::vector< Matrix< Scalar > > > &v, const Dict &opts=Dict())
static Matrix< Scalar > simplify(const Matrix< Scalar > &x)
static void extract_parametric(const Matrix< Scalar > &expr, const Matrix< Scalar > &par, Matrix< Scalar > &expr_ret, std::vector< Matrix< Scalar > > &symbols, std::vector< Matrix< Scalar >> &parametric, const Dict &opts)
static Matrix< Scalar > mtimes(const Matrix< Scalar > &x, const Matrix< Scalar > &y)
Matrix< Scalar > T() const
Transpose the matrix.
static void separate_linear(const Matrix< Scalar > &expr, const Matrix< Scalar > &sym_lin, const Matrix< Scalar > &sym_const, Matrix< Scalar > &expr_const, Matrix< Scalar > &expr_lin, Matrix< Scalar > &expr_nonlin)
bool is_smooth() const
Check if smooth.
static void set_max_depth(casadi_int eq_depth=1)
Set or reset the depth to which equalities are being checked for simplifications.
casadi_int n_dep() const
Get the number of dependencies of a binary SXElem.
void get(Matrix< Scalar > &m, bool ind1, const Slice &rr) const
friend Scalar * get_ptr(Matrix< Scalar > &v)
bool __nonzero__() const
Returns the truth value of a Matrix.
Definition: matrix_impl.hpp:70
const Sparsity & sparsity() const
Const access the sparsity - reference to data member.
bool has_duplicates() const
Detect duplicate symbolic expressions.
bool has_output() const
Check if a multiple output node.
casadi_int element_hash() const
Returns a number that is unique for a given symbolic scalar.
bool is_leaf() const
Check if SX is a leaf of the SX graph.
static Matrix< Scalar > gauss_quadrature(const Matrix< Scalar > &f, const Matrix< Scalar > &x, const Matrix< Scalar > &a, const Matrix< Scalar > &b, casadi_int order=5)
static Matrix< Scalar > pw_lin(const Matrix< Scalar > &t, const Matrix< Scalar > &tval, const Matrix< Scalar > &val)
bool is_regular() const
Checks if expression does not contain NaN or Inf.
Matrix< Scalar > get_output(casadi_int oind) const
Get an output.
static void expand(const Matrix< Scalar > &x, Matrix< Scalar > &weights, Matrix< Scalar > &terms)
Matrix< Scalar > dep(casadi_int ch=0) const
Get expressions of the children of the expression.
void reset_input() const
Reset the marker for an input expression.
static Matrix< Scalar > _sym(const std::string &name, const Sparsity &sp)
bool is_symbolic() const
Check if symbolic (Dense)
static void substitute_inplace(const std::vector< Matrix< Scalar > > &v, std::vector< Matrix< Scalar > > &vdef, std::vector< Matrix< Scalar > > &ex, bool revers)
Function which_function() const
Get function - only valid when is_call() is true.
bool is_commutative() const
Check whether a binary SX is commutative.
static Matrix< Scalar > substitute(const Matrix< Scalar > &ex, const Matrix< Scalar > &v, const Matrix< Scalar > &vdef)
casadi_int op() const
Get operation type.
bool is_output() const
Check if evaluation output.
bool is_valid_input() const
Check if matrix can be used to define function inputs.
bool is_call() const
Check if function call.
std::string name() const
Get name (only if symbolic scalar)
static bool is_equal(const Matrix< Scalar > &x, const Matrix< Scalar > &y, casadi_int depth=0)
static casadi_int get_max_depth()
Get the depth to which equalities are being checked for simplifications.
static Matrix< Scalar > pw_const(const Matrix< Scalar > &t, const Matrix< Scalar > &tval, const Matrix< Scalar > &val)
const Scalar scalar() const
Convert to scalar type.
bool is_op(casadi_int op) const
Is it a certain operation.
The basic scalar symbolic class of CasADi.
Definition: sx_elem.hpp:75
bool is_nan() const
Definition: sx_elem.cpp:505
SXElem dep(casadi_int ch=0) const
Definition: sx_elem.cpp:558
static std::vector< SXElem > call(const Function &f, const std::vector< SXElem > &deps)
Definition: sx_elem.cpp:403
bool is_minus_inf() const
Definition: sx_elem.cpp:513
bool is_symbolic() const
Definition: sx_elem.cpp:485
static SXElem create(SXNode *node)
Definition: sx_elem.cpp:62
SXElem get_output(casadi_int oind) const
Get an output.
Definition: sx_elem.cpp:567
bool is_constant() const
Definition: sx_elem.cpp:457
SXNode * get() const
Get a pointer to the node.
Definition: sx_elem.cpp:174
static bool is_equal(const SXElem &x, const SXElem &y, casadi_int depth=0)
Check equality up to a given depth.
Definition: sx_elem.cpp:529
static SXElem sym(const std::string &name)
Create a symbolic primitive.
Definition: sx_elem.cpp:90
bool is_inf() const
Definition: sx_elem.cpp:509
Internal node class for SXFunction.
Definition: sx_function.hpp:54
bool is_smooth() const
Check if smooth.
static casadi_int eq_depth_
Definition: sx_node.hpp:179
General sparsity class.
Definition: sparsity.hpp:106
bool is_scalar(bool scalar_and_dense=false) const
Is scalar?
Definition: sparsity.cpp:269
casadi_int nnz() const
Get the number of (structural) non-zeros.
Definition: sparsity.cpp:148
static const SXElem one
Definition: sx_elem.hpp:320
casadi_limits class
friend MatType sum(const MatType &x)
Returns summation of all elements.
The casadi namespace.
Definition: archiver.cpp:28
bool is_equal(double x, double y, casadi_int depth=0)
Definition: calculus.hpp:281
std::vector< casadi_int > range(casadi_int start, casadi_int stop, casadi_int step, casadi_int len)
Range function.
std::vector< bool > _which_depends(const MatType &expr, const MatType &var, casadi_int order, bool tr)
Sparsity _jacobian_sparsity(const MatType &expr, const MatType &var)
SX mtaylor_recursive(const SX &ex, const SX &x, const SX &a, casadi_int order, const std::vector< casadi_int > &order_contributions, const SXElem &current_dx=casadi_limits< SXElem >::one, double current_denom=1, casadi_int current_order=1)
Matrix< SXElem > SX
Definition: sx_fwd.hpp:32
MX register_symbol(const MX &node, std::map< MXNode *, MX > &symbol_map, std::vector< MX > &symbol_v, std::vector< MX > &parametric_v, bool extract_trivial, casadi_int v_offset, const std::string &v_prefix, const std::string &v_suffix)
Definition: mx.cpp:2294
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.
std::vector< T > reverse(const std::vector< T > &v)
Reverse a list.
Dict extract_from_dict(const Dict &d, const std::string &key, T &value)
@ OP_IF_ELSE_ZERO
Definition: calculus.hpp:71
@ OP_OUTPUT
Definition: calculus.hpp:82
@ OP_CONST
Definition: calculus.hpp:79
@ OP_INPUT
Definition: calculus.hpp:82
@ OP_SUB
Definition: calculus.hpp:65
@ OP_PARAMETER
Definition: calculus.hpp:85
@ OP_CALL
Definition: calculus.hpp:88
@ OP_ADD
Definition: calculus.hpp:65
@ OP_MUL
Definition: calculus.hpp:65
Easy access to all the functions for a particular type.
Definition: calculus.hpp:1125
static bool is_binary(unsigned char op)
Is binary operation?
Definition: calculus.hpp:1602
static void fun_linear(unsigned char op, const T *x, const T *y, T *f)
Evaluate function on a const/linear/nonlinear partition.
Definition: calculus.hpp:1552