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