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