sx_function.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 
26 #include "sx_function.hpp"
27 #include <limits>
28 #include <stack>
29 #include <deque>
30 #include <sstream>
31 #include <iomanip>
32 #include <bitset>
33 #include "sx_node.hpp"
34 #include "output_sx.hpp"
35 #include "call_sx.hpp"
36 #include "casadi_common.hpp"
37 #include "sparsity_internal.hpp"
38 #include "casadi_interrupt.hpp"
39 #include "serializing_stream.hpp"
40 #include "global_options.hpp"
41 
42 namespace casadi {
43 
45  n_dep = f.nnz_in(); n_res = f.nnz_out();
46  dep.resize(n_dep); res.resize(n_res, -1);
47  f_n_in = f.n_in(); f_n_out = f.n_out();
48  f_nnz_in.resize(f_n_in); f_nnz_out.resize(f_n_out);
49  for (casadi_int i=0;i<f_n_in;++i) f_nnz_in[i] = f.nnz_in(i);
50  for (casadi_int i=0;i<f_n_out;++i) f_nnz_out[i] = f.nnz_out(i);
51  copy_elision_arg.resize(f_n_in, -1);
52  copy_elision_offset.resize(f_n_in, -1);
53  }
54 
55  SXFunction::SXFunction(const std::string& name,
56  const std::vector<SX >& inputv,
57  const std::vector<SX >& outputv,
58  const std::vector<std::string>& name_in,
59  const std::vector<std::string>& name_out)
60  : XFunction<SXFunction, SX, SXNode>(name, inputv, outputv, name_in, name_out) {
61 
62  // Default (persistent) options
63  just_in_time_opencl_ = false;
64  just_in_time_sparsity_ = false;
65  print_instructions_ = false;
66  }
67 
69  clear_mem();
70  }
71 
72  int SXFunction::eval(const double** arg, double** res,
73  casadi_int* iw, double* w, void* mem) const {
74  auto trace = dump_trace_ ? open_trace(arg, static_cast<FunctionMemory*>(mem)->dump_id)
75  : nullptr;
76  try {
77  if (verbose_) casadi_message(name_ + "::eval");
78  setup(mem, arg, res, iw, w);
79 
80  // Make sure no free parameters
81  if (!free_vars_.empty()) {
82  std::stringstream ss;
83  disp(ss, false);
84  casadi_error("Cannot evaluate \"" + ss.str() + "\" since variables "
85  + str(free_vars_) + " are free.");
86  }
87 
88  // NOTE: The implementation of this function is very delicate. Small changes in the
89  // class structure can cause large performance losses. For this reason,
90  // the preprocessor macros are used below
91 
92  if (print_instructions_ || trace) {
93  int k = 0;
94  // Evaluate the algorithm
95  for (auto&& e : algorithm_) {
96  if (trace) trace_instruction(*trace, k, w, false);
97  if (print_instructions_) print_arg(uout(), k, e, w);
98  switch (e.op) {
99  CASADI_MATH_FUN_BUILTIN(w[e.i1], w[e.i2], w[e.i0])
100 
101  case OP_CONST: w[e.i0] = e.d; break;
102  case OP_INPUT: w[e.i0] = arg[e.i1]==nullptr ? 0 : arg[e.i1][e.i2]; break;
103  case OP_OUTPUT: if (res[e.i0]!=nullptr) res[e.i0][e.i2] = w[e.i1]; break;
104  case OP_CALL:
105  call_fwd(e, arg, res, iw, w);
106  break;
107  default:
108  casadi_error("Unknown operation" + str(e.op));
109  }
110  if (print_instructions_) print_res(uout(), k, e, w);
111  if (trace) trace_instruction(*trace, k, w, true);
112  k++;
113  }
114  } else {
115  // Evaluate the algorithm
116  for (auto&& e : algorithm_) {
117  switch (e.op) {
118  CASADI_MATH_FUN_BUILTIN(w[e.i1], w[e.i2], w[e.i0])
119 
120  case OP_CONST: w[e.i0] = e.d; break;
121  case OP_INPUT: w[e.i0] = arg[e.i1]==nullptr ? 0 : arg[e.i1][e.i2]; break;
122  case OP_OUTPUT: if (res[e.i0]!=nullptr) res[e.i0][e.i2] = w[e.i1]; break;
123  case OP_CALL:
124  call_fwd(e, arg, res, iw, w);
125  break;
126  default:
127  casadi_error("Unknown operation" + str(e.op));
128  }
129  }
130  }
131  } catch (...) {
132  if (trace) *trace << "{\"event\":\"error\"}\n";
133  throw;
134  }
135  if (trace) finish_trace(*trace, res, 0);
136  return 0;
137  }
138 
139  void SXFunction::trace_instruction(std::ostream& trace, casadi_int k,
140  const double* w, bool output) const {
141  const auto& e = algorithm_.at(k);
142  const int* slots = output ? &e.i0 : &e.i1;
143  casadi_int n = output ? 1 : casadi_math<double>::ndeps(e.op);
144  if (e.op == OP_CALL) {
145  const auto& call = call_.el.at(e.i1);
146  slots = get_ptr(output ? call.res : call.dep);
147  n = output ? call.n_res : call.n_dep;
148  } else if (e.op == OP_INPUT || e.op == OP_CONST) {
149  n = output ? 1 : 0;
150  } else if (e.op == OP_OUTPUT) {
151  n = output ? 0 : 1;
152  }
153  trace << "{\"instruction\":" << k << ",\"op\":" << e.op
154  << ",\"phase\":\"" << (output ? "outputs" : "inputs") << "\",\"values\":[";
155  for (casadi_int i = 0; i < n; ++i) {
156  if (i) trace << ",";
157  trace_values(trace, slots[i] < 0 ? nullptr : w + slots[i], 1);
158  }
159  trace << "]}\n";
160  }
161 
162  bool SXFunction::is_smooth() const {
163  // Go through all nodes and check if any node is non-smooth
164  for (auto&& a : algorithm_) {
165  if (!operation_checker<SmoothChecker>(a.op)) {
166  return false;
167  }
168  }
169  return true;
170  }
171  std::string SXFunction::print(const ScalarAtomic& a) const {
172  std::stringstream stream;
173  if (a.op==OP_OUTPUT) {
174  stream << "output[" << a.i0 << "][" << a.i2 << "] = @" << a.i1;
175  } else if (a.op==OP_CALL) {
176  const ExtendedAlgEl& m = call_.el.at(a.i1);
177  stream << "[";
178  casadi_int k = 0;
179  for (casadi_int i=0; i<m.f.n_out(); ++i) {
180  if (m.f.nnz_out(i)>1) stream << "[";
181  for (casadi_int j=0; j<m.f.nnz_out(i); ++j) {
182  int el = m.res[k++];
183  if (el>=0) {
184  stream << "@" << el;
185  } else {
186  stream << "NULL";
187  }
188  if (j<m.f.nnz_out(i)-1) stream << ",";
189  }
190  if (m.f.nnz_out(i)>1) stream << "]";
191  if (i<m.f.n_out()-1) stream << ",";
192  }
193  stream << "] = ";
194  stream << m.f.name() << "(";
195  k = 0;
196  for (casadi_int i=0; i<m.f.n_in(); ++i) {
197  if (m.f.nnz_in(i)==0) stream << "0x0";
198  if (m.f.nnz_in(i)>1) stream << "[";
199  for (casadi_int j=0; j<m.f.nnz_in(i); ++j) {
200  stream << "@" << m.dep[k++];
201  if (j<m.f.nnz_in(i)-1) stream << ",";
202  }
203  if (m.f.nnz_in(i)>1) stream << "]";
204  if (i<m.f.n_in()-1) stream << ",";
205  }
206  stream << ")";
207  } else {
208  stream << "@" << a.i0 << " = ";
209  if (a.op==OP_INPUT) {
210  stream << "input[" << a.i1 << "][" << a.i2 << "]";
211  } else {
212  if (a.op==OP_CONST) {
213  stream << a.d;
214  } else if (a.op==OP_PARAMETER) {
215  stream << free_vars_[a.i1];
216  } else {
217  casadi_int ndep = casadi_math<double>::ndeps(a.op);
218  stream << casadi_math<double>::pre(a.op);
219  for (casadi_int c=0; c<ndep; ++c) {
220  if (c==0) {
221  stream << "@" << a.i1;
222  } else {
223  stream << casadi_math<double>::sep(a.op);
224  stream << "@" << a.i2;
225  }
226 
227  }
228  stream << casadi_math<double>::post(a.op);
229  }
230  }
231  }
232  return stream.str();
233  }
234 
235  void SXFunction::disp_more(std::ostream &stream) const {
236  stream << "Algorithm:";
237 
238  // Normal, interpreted output
239  for (auto&& a : algorithm_) {
241  stream << std::endl;
242  stream << print(a);
243  stream << ";";
244  }
245  }
246 
247  size_t SXFunction::codegen_sz_w(const CodeGenerator& g) const {
248  if (!g.avoid_stack()) return call_.sz_w+call_.sz_w_arg+call_.sz_w_res;
249  return sz_w();
250  }
251 
253 
254  // Make sure that there are no free variables
255  if (!free_vars_.empty()) {
256  casadi_error("Code generation of '" + name_ + "' is not possible since variables "
257  + str(free_vars_) + " are free.");
258  }
259 
260  // Generate code for the call nodes
261  for (auto&& m : call_.el) {
262  g.add_dependency(m.f);
263  }
264  }
265 
266 
267  void SXFunction::print_arg(std::ostream &stream, casadi_int k, const ScalarAtomic& el,
268  const double* w) const {
269  if (el.op==OP_INPUT || el.op==OP_OUTPUT || el.op==OP_CONST) return;
270  stream << name_ << ":" << k << ": " << print(el) << " inputs:" << std::endl;
271 
272  // Default dependencies
273  const int* dep = &el.i1;
274  casadi_int ndeps = casadi_math<double>::ndeps(el.op);
275 
276  // Call node overrides these defaults
277  if (el.op==OP_CALL) {
278  const ExtendedAlgEl& e = call_.el.at(el.i1);
279  ndeps = e.n_dep;
280  dep = get_ptr(e.dep);
281  stream << "[";
282  for (size_t i = 0; i < ndeps; ++i) {
283  if (i>0) stream << ", ";
284  if (print_canonical_) {
285  print_canonical(stream, w[dep[i]]);
286  } else {
287  DM::print_scalar(stream, w[dep[i]]);
288  }
289  }
290  stream << "]";
291  stream << std::endl;
292  return;
293  }
294 
295  for (size_t i = 0; i < ndeps; ++i) {
296  stream << i << ": ";
297  if (print_canonical_) {
298  print_canonical(stream, w[dep[i]]);
299  } else {
300  DM::print_scalar(stream, w[dep[i]]);
301  }
302  stream << std::endl;
303  }
304  }
305 
306  void SXFunction::print_arg(CodeGenerator& g, casadi_int k, const ScalarAtomic& el) const {
307  if (el.op==OP_INPUT || el.op==OP_OUTPUT || el.op==OP_CONST) return;
308  g << g.printf(name_ + ":" + str(k) + ": " + print(el) + " inputs:\\n") << "\n";
309  if (el.op==OP_CALL) {
310  const ExtendedAlgEl& m = call_.el[el.i1];
311  g << g.print_vector(m.f.nnz_in(), "arg[" + str(n_in_) + "]");
312  g << g.printf("\\n");
313  } else {
314  casadi_int ndeps = casadi_math<double>::ndeps(el.op);
315  if (ndeps==1) {
316  g << g.printf("0: %.16e\\n", g.sx_work(el.i1));
317  } else if (ndeps==2) {
318  g << g.printf("0: %.16e\\n1: %.16e\\n", g.sx_work(el.i1), g.sx_work(el.i2));
319  }
320  }
321  g << "\n";
322  }
323 
324  void SXFunction::print_res(CodeGenerator& g, casadi_int k, const ScalarAtomic& el) const {
325  if (el.op==OP_INPUT || el.op==OP_OUTPUT) return;
326  g << g.printf(name_ + ":" + str(k) + ": " + print(el) + " outputs:\\n") << "\n";
327  if (el.op==OP_CALL) {
328  const ExtendedAlgEl& m = call_.el[el.i1];
329  g << g.print_vector(m.f.nnz_out(), "w+" + str(m.f.nnz_in()));
330  g << g.printf("\\n");
331  } else {
332  g << g.printf("0: %.16e\\n", g.sx_work(el.i0));
333  }
334  g << "\n";
335  }
336 
337  void SXFunction::print_res(std::ostream &stream, casadi_int k, const ScalarAtomic& el,
338  const double* w) const {
339  if (el.op==OP_INPUT || el.op==OP_OUTPUT) return;
340  stream << name_ << ":" << k << ": " << print(el) << " outputs:" << std::endl;
341 
342  // Default outputs
343  const int* res = &el.i0;
344  casadi_int nres = 1;
345 
346  // Call node overrides these defaults
347  if (el.op==OP_CALL) {
348  const ExtendedAlgEl& e = call_.el.at(el.i1);
349  nres = e.n_res;
350  res = get_ptr(e.res);
351  stream << "[";
352  for (size_t i = 0; i < nres; ++i) {
353  if (i>0) stream << ", ";
354  if (print_canonical_) {
355  print_canonical(stream, w[res[i]]);
356  } else {
357  DM::print_scalar(stream, w[res[i]]);
358  }
359  }
360  stream << "]";
361  stream << std::endl;
362  return;
363  }
364 
365  for (size_t i = 0; i < nres; ++i) {
366  stream << i << ": ";
367  if (print_canonical_) {
368  print_canonical(stream, w[res[i]]);
369  } else {
370  DM::print_scalar(stream, w[res[i]]);
371  }
372  stream << std::endl;
373  }
374 
375  }
376 
379 
380  casadi_int cnt = 0;
381  // Run the algorithm
382  for (auto&& a : algorithm_) {
383  if (a.op==OP_OUTPUT) {
384  g << "if (res[" << a.i0 << "]!=0) "
385  << g.res(a.i0) << "[" << a.i2 << "]=" << g.sx_work(a.i1) << ";\n";
386  } else if (a.op==OP_CALL) {
387  const ExtendedAlgEl& m = call_.el[a.i1];
388 
389  casadi_int worksize = g.avoid_stack() ? worksize_ : 0;
390 
391  // Collect input arguments
392  casadi_int offset = worksize;
393  for (casadi_int i=0; i<m.f_n_in; ++i) {
394  if (m.copy_elision_arg[i]>=0) {
395  g << "arg[" << n_in_+i << "] = "
396  << "arg[" + str(m.copy_elision_arg[i]) << "]? "
397  << "arg[" + str(m.copy_elision_arg[i]) << "] + "
398  << str(m.copy_elision_offset[i]) << " : 0;\n";
399  } else {
400  if (m.f_nnz_in[i]==0) {
401  g << "arg[" << n_in_+i << "]=" << 0 << ";\n";
402  } else {
403  g << "arg[" << n_in_+i << "]=" << "w+" + str(offset) << ";\n";
404  }
405  }
406  offset += m.f_nnz_in[i];
407  }
408 
409 
410  casadi_int out_offset = offset;
411 
412  // Collect output arguments
413  for (casadi_int i=0; i<m.f_n_out; ++i) {
414  g << "res[" << n_out_+i << "]=" << "w+" + str(offset) << ";\n";
415  offset += m.f_nnz_out[i];
416  }
417  casadi_int k=0;
418  for (casadi_int i=0; i<m.f_n_in; ++i) {
419  if (m.copy_elision_arg[i]==-1) {
420  for (casadi_int j=0; j<m.f_nnz_in[i]; ++j) {
421  g << "w["+str(k+worksize) + "] = " << g.sx_work(m.dep[k]) << ";\n";
422  k++;
423  }
424  } else {
425  k+=m.f_nnz_in[i];
426  }
427  }
428  if (print_instructions_) print_arg(g, cnt, a);
429  std::string flag =
430  g(m.f, "arg+"+str(n_in_), "res+"+str(n_out_), "iw", "w+" + str(offset));
431  // Call function
432  g << "if (" << flag << ") return 1;\n";
433  if (print_instructions_) print_res(g, cnt, a);
434  for (casadi_int i=0;i<m.n_res;++i) {
435  if (m.res[i]>=0) {
436  g << g.sx_work(m.res[i]) << " = ";
437  g << "w[" + str(i+out_offset) + "];\n";
438  }
439  }
440  } else if (a.op==OP_INPUT) {
441  if (!copy_elision_[cnt]) {
442  g << g.sx_work(a.i0) << "="
443  << g.arg(a.i1) << "? " << g.arg(a.i1) << "[" << a.i2 << "] : 0;\n";
444  }
445  } else {
446  if (print_instructions_) print_arg(g, cnt, a);
447 
448  // Where to store the result
449  g << g.sx_work(a.i0) << "=";
450 
451  // What to store
452  if (a.op==OP_CONST) {
453  g << g.constant(a.d);
454  } else {
455  casadi_int ndep = casadi_math<double>::ndeps(a.op);
456  casadi_assert_dev(ndep>0);
457  if (ndep==1) g << g.print_op(a.op, g.sx_work(a.i1));
458  if (ndep==2) g << g.print_op(a.op, g.sx_work(a.i1), g.sx_work(a.i2));
459  }
460 
461  g << ";\n";
462 
463  if (print_instructions_) print_res(g, cnt, a);
464  }
465  cnt++;
466  }
467  }
468 
471  {{"default_in",
473  "Default input values"}},
474  {"just_in_time_sparsity",
475  {OT_BOOL,
476  "Propagate sparsity patterns using just-in-time "
477  "compilation to a CPU or GPU using OpenCL"}},
478  {"just_in_time_opencl",
479  {OT_BOOL,
480  "Just-in-time compilation for numeric evaluation using OpenCL (experimental)"}},
481  {"live_variables",
482  {OT_BOOL,
483  "Reuse variables in the work vector"}},
484  {"cse",
485  {OT_BOOL,
486  "Perform common subexpression elimination (complexity is N*log(N) in graph size)"}},
487  {"allow_free",
488  {OT_BOOL,
489  "Allow construction with free variables (Default: false)"}},
490  {"allow_duplicate_io_names",
491  {OT_BOOL,
492  "Allow construction with duplicate io names (Default: false)"}},
493  {"dump_trace",
494  {OT_BOOL,
495  "Dump interpreted instruction values to name.NNNNNN.trace.jsonl in dump_dir, "
496  "using the dump_in/dump_out counter. [false]"}},
497  {"print_instructions",
498  {OT_BOOL,
499  "Print each operation during evaluation. Influenced by print_canonical."}}
500  }
501  };
502 
503  Dict SXFunction::generate_options(const std::string& target) const {
505  opts["dump_trace"] = dump_trace_;
506  if (target=="clone") opts["default_in"] = default_in_;
507  opts["live_variables"] = live_variables_;
508  opts["just_in_time_sparsity"] = just_in_time_sparsity_;
509  opts["just_in_time_opencl"] = just_in_time_opencl_;
510  opts["print_instructions"] = print_instructions_;
511  return opts;
512  }
513 
514  void SXFunction::init(const Dict& opts) {
515  // Call the init function of the base class
517  if (verbose_) casadi_message(name_ + "::init");
518 
519  // Default (temporary) options
520  live_variables_ = true;
521 
522  bool cse_opt = false;
523  bool allow_free = false;
524 
525  // Read options
526  for (auto&& op : opts) {
527  if (op.first=="default_in") {
528  default_in_ = op.second;
529  } else if (op.first=="live_variables") {
530  live_variables_ = op.second;
531  } else if (op.first=="just_in_time_opencl") {
532  just_in_time_opencl_ = op.second;
533  } else if (op.first=="just_in_time_sparsity") {
534  just_in_time_sparsity_ = op.second;
535  } else if (op.first=="cse") {
536  cse_opt = op.second;
537  } else if (op.first=="allow_free") {
538  allow_free = op.second;
539  } else if (op.first=="dump_trace") {
540  dump_trace_ = op.second;
541  } else if (op.first=="print_instructions") {
542  print_instructions_ = op.second;
543  }
544  }
545 
546  // Perform common subexpression elimination
547  // This must be done before the lock, to avoid deadlocks
548  if (cse_opt) out_ = cse(out_);
549 
550  casadi_assert(!dump_trace_ || !jit_, "dump_trace is not supported for JIT evaluation");
551 
552  // Check/set default inputs
553  if (default_in_.empty()) {
554  default_in_.resize(n_in_, 0);
555  } else {
556  casadi_assert(default_in_.size()==n_in_,
557  "Option 'default_in' has incorrect length");
558  }
559 
560 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
561  std::lock_guard<std::mutex> lock(SX::get_mutex_temp());
562 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
563 
564  // Stack used to sort the computational graph
565  std::stack<SXNode*> s;
566 
567  // All nodes
568  std::vector<SXNode*> nodes;
569 
570  // Add the list of nodes
571  casadi_int ind=0;
572  for (auto it = out_.begin(); it != out_.end(); ++it, ++ind) {
573  casadi_int nz=0;
574  for (auto itc = (*it)->begin(); itc != (*it)->end(); ++itc, ++nz) {
575  // Add outputs to the list
576  s.push(itc->get());
577  sort_depth_first(s, nodes);
578 
579  // A null pointer means an output instruction
580  nodes.push_back(static_cast<SXNode*>(nullptr));
581  }
582  }
583 
584  casadi_assert(nodes.size() <= std::numeric_limits<int>::max(), "Integer overflow");
585  // Set the temporary variables to be the corresponding place in the sorted graph
586  for (casadi_int i=0; i<nodes.size(); ++i) {
587  if (nodes[i]) {
588  nodes[i]->temp = static_cast<int>(i);
589  }
590  }
591 
592  // Sort the nodes by type
593  constants_.clear();
594  operations_.clear();
595  for (std::vector<SXNode*>::iterator it = nodes.begin(); it != nodes.end(); ++it) {
596  SXNode* t = *it;
597  if (t) {
598  if (t->is_constant())
599  constants_.push_back(SXElem::create(t));
600  else if (!t->is_symbolic() && t->op()>=0)
601  operations_.push_back(SXElem::create(t));
602  }
603  }
604 
605  // Input instructions
606  std::vector<std::pair<int, SXNode*> > symb_loc;
607 
608  // Current output and nonzero, start with the first one
609  int curr_oind, curr_nz=0;
610  casadi_assert(out_.size() <= std::numeric_limits<int>::max(), "Integer overflow");
611  for (curr_oind=0; curr_oind<out_.size(); ++curr_oind) {
612  if (out_[curr_oind].nnz()!=0) {
613  break;
614  }
615  }
616 
617  // Count the number of times each node is used
618  std::vector<casadi_int> refcount(nodes.size(), 0);
619 
620  // Get the sequence of instructions for the virtual machine
621  algorithm_.resize(0);
622  algorithm_.reserve(nodes.size());
623 
624  // Mapping of node index (cfr. temp) to algorithm index
625  std::vector<int> alg_index;
626  alg_index.reserve(nodes.size());
627 
628  for (std::vector<SXNode*>::iterator it=nodes.begin(); it!=nodes.end(); ++it) {
629  // Current node
630  SXNode* n = *it;
631 
632  // New element in the algorithm
633  AlgEl ae;
634 
635  // Get operation
636  ae.op = n==nullptr ? static_cast<int>(OP_OUTPUT) : static_cast<int>(n->op());
637 
638  // Default dependencies
639  int* dep = &ae.i1;
640  casadi_int ndeps = ae.op == -1 ? 1 : casadi_math<double>::ndeps(ae.op);
641 
642  // Get instruction
643  switch (ae.op) {
644  case OP_CONST: // constant
645  ae.d = n->to_double();
646  ae.i0 = n->temp;
647  break;
648  case OP_PARAMETER: // a parameter or input
649  symb_loc.push_back(std::make_pair(algorithm_.size(), n));
650  ae.i0 = n->temp;
651  ae.d = 0; // value not used, but set here to avoid uninitialized data in serialization
652  break;
653  case OP_OUTPUT: // output instruction
654  ae.i0 = curr_oind;
655  ae.i1 = out_[curr_oind]->at(curr_nz)->temp;
656  ae.i2 = curr_nz;
657 
658  // Go to the next nonzero
659  casadi_assert(curr_nz < std::numeric_limits<int>::max(), "Integer overflow");
660  curr_nz++;
661  if (curr_nz>=out_[curr_oind].nnz()) {
662  curr_nz=0;
663  casadi_assert(curr_oind < std::numeric_limits<int>::max(), "Integer overflow");
664  curr_oind++;
665  for (; curr_oind<out_.size(); ++curr_oind) {
666  if (out_[curr_oind].nnz()!=0) {
667  break;
668  }
669  }
670  }
671  break;
672  case OP_CALL: // Call node
673  {
674  ae.i0 = n->temp;
675 
676  // Index into ExtentedAlgEl collection
677  ae.i1 = call_.el.size();
678 
679  // Create ExtentedAlgEl instance
680  // This allocates space for dep and res
681  const Function& f = static_cast<const CallSX*>(n)->f_;
682  call_.el.emplace_back(f);
683 
684  // Make sure we have enough space to evaluate the Function call,
685  // noting that we wil only ever evaluate one call at a time.
686  call_.sz_arg = std::max(call_.sz_arg, f.sz_arg());
687  call_.sz_res = std::max(call_.sz_res, f.sz_res());
688  call_.sz_iw = std::max(call_.sz_iw, f.sz_iw());
689  call_.sz_w = std::max(call_.sz_w, f.sz_w());
690  call_.sz_w_arg = std::max(call_.sz_w_arg, static_cast<size_t>(f.nnz_in()));
691  call_.sz_w_res = std::max(call_.sz_w_res, static_cast<size_t>(f.nnz_out()));
692 
693  // Set the dependency pointer to the (uninitialised) slots of the ExtendedAlgEl
694  ExtendedAlgEl& m = call_.el.at(ae.i1);
695  dep = get_ptr(m.dep);
696  ndeps = m.n_dep;
697 
698  // Populate the dependency slots with node ids.
699  for (casadi_int i=0; i<ndeps; ++i) {
700  dep[i] = n->dep(i).get()->temp;
701  }
702  }
703  break;
704  case -1: // Output extraction node
705  {
706  dep = &algorithm_.at(alg_index.at(n->dep(0).get()->temp)).i1;
707  int oind = static_cast<OutputSX*>(n)->oind_;
708  casadi_assert(call_.el.at(dep[0]).res.at(oind)==-1, "Duplicate");
709  call_.el.at(dep[0]).res.at(oind) = n->temp;
710  }
711  break;
712  default: // Unary or binary operation
713  ae.i0 = n->temp;
714  ae.i1 = n->dep(0).get()->temp;
715  ae.i2 = n->dep(1).get()->temp;
716  }
717 
718  // Increase count of dependencies
719  for (casadi_int c=0; c<ndeps; ++c) {
720  refcount.at(dep[c])++;
721  }
722 
723  // Amend node index to algorithm index mapping
724  alg_index.push_back(algorithm_.size());
725 
726  // Add to algorithm
727  if (ae.op>=0) algorithm_.push_back(ae);
728 
729  }
730 
731  // Place in the work vector for each of the nodes in the tree (overwrites the reference counter)
732  std::vector<int> place(nodes.size());
733 
734  // Stack with unused elements in the work vector
735  std::stack<int> unused;
736 
737  // Work vector size
738  int worksize = 0;
739 
740  // Find a place in the work vector for the operation
741  for (auto&& a : algorithm_) {
742 
743  // Default dependencies
744  int* dep = &a.i1;
745  casadi_int ndeps = casadi_math<double>::ndeps(a.op);
746 
747  // Default outputs
748  int* res = &a.i0;
749  casadi_int nres = 1;
750 
751  // Call node overrides these defaults
752  if (a.op==OP_CALL) {
753  ExtendedAlgEl& e = call_.el.at(a.i1);
754  ndeps = e.n_dep;
755  dep = get_ptr(e.dep);
756  nres = e.n_res;
757  res = get_ptr(e.res);
758  }
759 
760  // decrease reference count of children
761  // reverse order so that the first argument will end up at the top of the stack
762  for (casadi_int c=ndeps-1; c>=0; --c) {
763  casadi_int ch_ind = dep[c];
764  casadi_int remaining = --refcount.at(ch_ind);
765  if (remaining==0) unused.push(place[ch_ind]);
766  }
767 
768  // Find a place to store the variable
769  if (a.op!=OP_OUTPUT) {
770  for (casadi_int c=0; c<nres; ++c) {
771  if (res[c]<0) continue;
772  if (live_variables_ && !unused.empty()) {
773  // Try to reuse a variable from the stack if possible (last in, first out)
774  res[c] = place[res[c]] = unused.top();
775  unused.pop();
776  } else {
777  // Allocate a new variable
778  res[c] = place[res[c]] = worksize++;
779  }
780  }
781  }
782 
783  // Save the location of the children
784  for (casadi_int c=0; c<ndeps; ++c) {
785  dep[c] = place[dep[c]];
786  }
787 
788  // If binary, make sure that the second argument is the same as the first one
789  // (in order to treat all operations as binary) NOTE: ugly
790  if (ndeps==1 && a.op!=OP_OUTPUT) {
791  a.i2 = a.i1;
792  }
793  }
794 
795  worksize_ = worksize;
796 
797  if (verbose_) {
798  if (live_variables_) {
799  casadi_message("Using live variables: work array is " + str(worksize_)
800  + " instead of " + str(nodes.size()));
801  } else {
802  casadi_message("Live variables disabled.");
803  }
804  }
805 
806  // Allocate work vectors (symbolic/numeric)
808 
809  alloc_arg(call_.sz_arg, true);
810  alloc_res(call_.sz_res, true);
811  alloc_iw(call_.sz_iw, true);
813 
814  // Reset the temporary variables
815  for (casadi_int i=0; i<nodes.size(); ++i) {
816  if (nodes[i]) {
817  nodes[i]->temp = 0;
818  }
819  }
820 
821  // Now mark each input's place in the algorithm
822  for (auto it=symb_loc.begin(); it!=symb_loc.end(); ++it) {
823  it->second->temp = it->first+1;
824  }
825 
826  // Add input instructions
827  casadi_assert(in_.size() <= std::numeric_limits<int>::max(), "Integer overflow");
828  for (int ind=0; ind<in_.size(); ++ind) {
829  int nz=0;
830  for (auto itc = in_[ind]->begin(); itc != in_[ind]->end(); ++itc, ++nz) {
831  int i = itc->get_temp()-1;
832  if (i>=0) {
833  // Mark as input
834  algorithm_[i].op = OP_INPUT;
835 
836  // Location of the input
837  algorithm_[i].i1 = ind;
838  algorithm_[i].i2 = nz;
839 
840  // Mark input as read
841  itc->set_temp(0);
842  }
843  }
844  }
845 
846  // Locate free variables
847  free_vars_.clear();
848  for (std::vector<std::pair<int, SXNode*> >::const_iterator it=symb_loc.begin();
849  it!=symb_loc.end(); ++it) {
850  if (it->second->temp!=0) {
851  // Store the index into free_vars
852  algorithm_[it->first].i1 = free_vars_.size();
853 
854  // Save to list of free parameters
855  free_vars_.push_back(SXElem::create(it->second));
856 
857  // Remove marker
858  it->second->temp=0;
859  }
860  }
861 
862  if (!allow_free && has_free()) {
863  casadi_error(name_ + "::init: Initialization failed since variables [" +
864  join(get_free(), ", ") + "] are free. These symbols occur in the output expressions "
865  "but you forgot to declare these as inputs. "
866  "Set option 'allow_free' to allow free variables.");
867  }
868 
870 
871  // Initialize just-in-time compilation for numeric evaluation using OpenCL
872  if (just_in_time_opencl_) {
873  casadi_error("OpenCL is not supported in this version of CasADi");
874  }
875 
876  // Initialize just-in-time compilation for sparsity propagation using OpenCL
878  casadi_error("OpenCL is not supported in this version of CasADi");
879  }
880 
881  // Print
882  if (verbose_) casadi_message(str(algorithm_.size()) + " elementary operations");
883  }
884 
887  copy_elision_.resize(algorithm_.size(), false);
888  return;
889  }
890  // Perform copy elision (codegen-only)
891  // Remove nodes that only serve to compose CALL inputs
892 
893  // For work vector elements, store the arg source (-1 for no trivial source)
894  std::vector<int> arg_i(worksize_, -1);
895  std::vector<int> nz_i(worksize_, -1);
896 
897  // Which algel corresponds to this source?
898  std::vector<casadi_int> alg_i(worksize_, -1);
899 
900  // Is this algel to be elided?
901  copy_elision_.resize(algorithm_.size(), false);
902 
903  casadi_int k=0;
904  for (auto&& e : algorithm_) {
905  switch (e.op) {
906  case OP_INPUT:
907  // Make source association
908  arg_i[e.i0] = e.i1;
909  nz_i[e.i0] = e.i2;
910  alg_i[e.i0] = k;
911  copy_elision_[k] = true;
912  break;
913  case OP_OUTPUT:
914  if (arg_i[e.i1]>=0) {
915  copy_elision_[alg_i[e.i1]] = false;
916  }
917  break;
918  case OP_CALL:
919  {
920  auto& m = call_.el[e.i1];
921 
922  // Inspect input arguments
923  casadi_int offset_input = 0;
924  for (casadi_int i=0; i<m.f_n_in; ++i) {
925  // Pattern match results
926  casadi_int arg = -1;
927  casadi_int offset = -1;
928  for (casadi_int j=0; j<m.f_nnz_in[i]; ++j) {
929  casadi_int k = offset_input+j;
930  if (j==0) {
931  arg = arg_i[m.dep[k]];
932  offset = nz_i[m.dep[k]];
933  }
934  if (arg_i[m.dep[k]]==-1) {
935  arg = -1;
936  // Pattern match failed
937  break;
938  }
939  if (nz_i[m.dep[k]]!=offset+j) {
940  arg = -1;
941  // Pattern match failed
942  break;
943  }
944  }
945 
946  // If we cannot perform elision
947  if (arg==-1) {
948  // We need copies for all nonzeros of input i
949  for (casadi_int j=0; j<m.f_nnz_in[i]; ++j) {
950  casadi_int k = offset_input+j;
951  if (arg_i[m.dep[k]]>=0) {
952  copy_elision_[alg_i[m.dep[k]]] = false;
953  }
954  }
955  }
956  // Store pattern match results
957  m.copy_elision_arg[i] = arg;
958  m.copy_elision_offset[i] = offset;
959 
960  offset += m.f_nnz_in[i];
961  offset_input += m.f_nnz_in[i];
962  }
963 
964  // Remove source association of all outputs
965  for (casadi_int i=0; i<m.n_res; ++i) {
966  if (m.res[i]>=0) {
967  arg_i[m.res[i]] = -1;
968  }
969  }
970  }
971  break;
972  case OP_CONST:
973  case OP_PARAMETER:
974  // Remove source association
975  arg_i[e.i0] = -1;
976  break;
977  default:
978  if (arg_i[e.i1]>=0) {
979  copy_elision_[alg_i[e.i1]] = false;
980  }
981  if (!casadi_math<double>::is_unary(e.op)) {
982  if (arg_i[e.i2]>=0) {
983  copy_elision_[alg_i[e.i2]] = false;
984  }
985  }
986  // Remove source association
987  arg_i[e.i0] = -1;
988  }
989  k++;
990  }
991  }
992 
994  std::vector<SXElem> ret(algorithm_.size(), casadi_limits<SXElem>::nan);
995 
996  std::vector<SXElem>::iterator it=ret.begin();
997 
998  // Iterator to the binary operations
999  std::vector<SXElem>::const_iterator b_it = operations_.begin();
1000 
1001  // Iterator to stack of constants
1002  std::vector<SXElem>::const_iterator c_it = constants_.begin();
1003 
1004  // Iterator to free variables
1005  std::vector<SXElem>::const_iterator p_it = free_vars_.begin();
1006 
1007  // Evaluate algorithm
1008  if (verbose_) casadi_message("Evaluating algorithm forward");
1009  for (auto&& a : algorithm_) {
1010  switch (a.op) {
1011  case OP_INPUT:
1012  case OP_OUTPUT:
1013  it++;
1014  break;
1015  case OP_CONST:
1016  *it++ = *c_it++;
1017  break;
1018  case OP_PARAMETER:
1019  *it++ = *p_it++;
1020  break;
1021  default:
1022  *it++ = *b_it++;
1023  }
1024  }
1025  casadi_assert(it==ret.end(), "Dimension mismatch");
1026  return ret;
1027  }
1028 
1030  eval_sx(const SXElem** arg, SXElem** res, casadi_int* iw, SXElem* w, void* mem,
1031  bool always_inline, bool never_inline) const {
1032 
1033  always_inline = always_inline || always_inline_;
1034  never_inline = never_inline || never_inline_;
1035 
1036  // non-inlining call is implemented in the base-class
1037  if (!should_inline(true, always_inline, never_inline)) {
1038  return FunctionInternal::eval_sx(arg, res, iw, w, mem, false, true);
1039  }
1040 
1041  if (verbose_) casadi_message(name_ + "::eval_sx");
1042 
1043  // Iterator to the binary operations
1044  std::vector<SXElem>::const_iterator b_it=operations_.begin();
1045 
1046  // Iterator to stack of constants
1047  std::vector<SXElem>::const_iterator c_it = constants_.begin();
1048 
1049  // Iterator to free variables
1050  std::vector<SXElem>::const_iterator p_it = free_vars_.begin();
1051 
1052  // Evaluate algorithm
1053  if (verbose_) casadi_message("Evaluating algorithm forward");
1054  for (auto&& a : algorithm_) {
1055  switch (a.op) {
1056  case OP_INPUT:
1057  w[a.i0] = arg[a.i1]==nullptr ? 0 : arg[a.i1][a.i2];
1058  break;
1059  case OP_OUTPUT:
1060  if (res[a.i0]!=nullptr) res[a.i0][a.i2] = w[a.i1];
1061  break;
1062  case OP_CONST:
1063  w[a.i0] = *c_it++;
1064  break;
1065  case OP_PARAMETER:
1066  w[a.i0] = *p_it++; break;
1067  case OP_CALL:
1068  {
1069  const ExtendedAlgEl& m = call_.el.at(a.i1);
1070  const SXElem& orig = *b_it++;
1071  std::vector<SXElem> deps(m.n_dep);
1072  bool identical = true;
1073 
1074  std::vector<SXElem> ret;
1075  for (casadi_int i=0;i<m.n_dep;++i) {
1076  identical &= SXElem::is_equal(w[m.dep.at(i)], orig->dep(i), 2);
1077  }
1078  if (identical) {
1079  ret = OutputSX::split(orig, m.n_res);
1080  } else {
1081  for (casadi_int i=0;i<m.n_dep;++i) deps[i] = w[m.dep[i]];
1082  ret = SXElem::call(m.f, deps);
1083  }
1084  for (casadi_int i=0;i<m.n_res;++i) {
1085  if (m.res[i]>=0) w[m.res[i]] = ret[i];
1086  }
1087  }
1088  break;
1089  default:
1090  {
1091  // Evaluate the function to a temporary value
1092  // (as it might overwrite the children in the work vector)
1093  SXElem f;
1094  switch (a.op) {
1095  CASADI_MATH_FUN_BUILTIN(w[a.i1], w[a.i2], f)
1096  }
1097 
1098  // If this new expression is identical to the expression used
1099  // to define the algorithm, then reuse
1100  const casadi_int depth = 2; // NOTE: a higher depth could possibly give more savings
1101  f.assignIfDuplicate(*b_it++, depth);
1102 
1103  // Finally save the function value
1104  w[a.i0] = f;
1105  }
1106  }
1107  }
1108  return 0;
1109  }
1110 
1111  void SXFunction::eval_mx(const MXVector& arg, MXVector& res,
1112  bool always_inline, bool never_inline) const {
1113  always_inline = always_inline || always_inline_;
1114  never_inline = never_inline || never_inline_;
1115 
1116  // non-inlining call is implemented in the base-class
1117  if (!always_inline) {
1118  FunctionInternal::eval_mx(arg, res, false, true);
1119  return;
1120  }
1121 
1122  if (verbose_) casadi_message(name_ + "::eval_mx");
1123 
1124  // Iterator to stack of constants
1125  std::vector<SXElem>::const_iterator c_it = constants_.begin();
1126 
1127  casadi_assert(!has_free(),
1128  "Free variables not supported in inlining call to SXFunction::eval_mx");
1129 
1130  // Resize the number of outputs
1131  casadi_assert(arg.size()==n_in_, "Wrong number of input arguments");
1132  res.resize(out_.size());
1133 
1134  // Symbolic work, non-differentiated
1135  std::vector<MX> w(sz_w());
1136  if (verbose_) casadi_message("Allocated work vector");
1137 
1138  // Split up inputs analogous to symbolic primitives
1139  std::vector<std::vector<MX> > arg_split(in_.size());
1140  for (casadi_int i=0; i<in_.size(); ++i) {
1141  // Get nonzeros of argument
1142  std::vector<MX> orig = arg[i].get_nonzeros();
1143 
1144  // Project to needed sparsity
1145  std::vector<MX> target(sparsity_in_[i].nnz(), 0);
1146  std::vector<MX> w(arg[i].size1());
1147  casadi_project(get_ptr(orig), arg[i].sparsity(),
1148  get_ptr(target), sparsity_in_[i], get_ptr(w));
1149 
1150  // Store
1151  arg_split[i] = target;
1152  }
1153 
1154  // Allocate storage for split outputs
1155  std::vector<std::vector<MX> > res_split(out_.size());
1156  for (casadi_int i=0; i<out_.size(); ++i) res_split[i].resize(nnz_out(i));
1157 
1158  // Evaluate algorithm
1159  if (verbose_) casadi_message("Evaluating algorithm forward");
1160  for (auto&& a : algorithm_) {
1161  switch (a.op) {
1162  case OP_INPUT:
1163  w[a.i0] = arg_split[a.i1][a.i2];
1164  break;
1165  case OP_OUTPUT:
1166  res_split[a.i0][a.i2] = w[a.i1];
1167  break;
1168  case OP_CONST:
1169  w[a.i0] = static_cast<double>(*c_it++);
1170  break;
1171  case OP_CALL:
1172  {
1173  const ExtendedAlgEl& m = call_.el.at(a.i1);
1174  std::vector<MX> deps(m.n_dep);
1175  std::vector<MX> args;
1176 
1177  casadi_int k = 0;
1178  // Construct matrix-valued function arguments
1179  for (casadi_int i=0;i<m.f_n_in;++i) {
1180  std::vector<MX> arg;
1181  for (casadi_int j=0;j<m.f_nnz_in[i];++j) {
1182  arg.push_back(w[m.dep[k++]]);
1183  }
1184  args.push_back(sparsity_cast(vertcat(arg), m.f.sparsity_in(i)));
1185  }
1186 
1187 
1188  std::vector<MX> ret = m.f(args);
1189  std::vector<MX> res;
1190 
1191  // Break apart matriv-valued outputs into scalar components
1192  for (casadi_int i=0;i<m.f_n_out;++i) {
1193  std::vector<MX> nz = ret[i].get_nonzeros();
1194  res.insert(res.end(), nz.begin(), nz.end());
1195  }
1196 
1197  // Store into work vector
1198  for (casadi_int i=0;i<m.n_res;++i) {
1199  if (m.res[i]>=0) w[m.res[i]] = res[i];
1200  }
1201  }
1202  break;
1203  default:
1204  // Evaluate the function to a temporary value
1205  // (as it might overwrite the children in the work vector)
1206  MX f;
1207  switch (a.op) {
1208  CASADI_MATH_FUN_BUILTIN(w[a.i1], w[a.i2], f)
1209  }
1210 
1211  // Finally save the function value
1212  w[a.i0] = f;
1213  }
1214  }
1215 
1216  // Join split outputs
1217  for (casadi_int i=0; i<res.size(); ++i) {
1218  res[i] = sparsity_cast(vertcat(res_split[i]), sparsity_out_[i]);
1219  }
1220  }
1221 
1222  bool SXFunction::should_inline(bool with_sx, bool always_inline, bool never_inline) const {
1223  // If inlining has been specified
1224  casadi_assert(!(always_inline && never_inline),
1225  "Inconsistent options for " + definition());
1226  casadi_assert(!(never_inline && has_free()),
1227  "Must inline " + definition());
1228  if (always_inline) return true;
1229  if (never_inline) return false;
1230  // Functions with free variables must be inlined
1231  if (has_free()) return true;
1232  // Inlining by default
1233  return true;
1234  }
1235 
1236  void SXFunction::ad_forward(const std::vector<std::vector<SX> >& fseed,
1237  std::vector<std::vector<SX> >& fsens) const {
1238  if (verbose_) casadi_message(name_ + "::ad_forward");
1239 
1240  // Number of forward seeds
1241  casadi_int nfwd = fseed.size();
1242  fsens.resize(nfwd);
1243 
1244  // Quick return if possible
1245  if (nfwd==0) return;
1246 
1247  // Check if seeds need to have dimensions corrected
1248  casadi_int npar = 1;
1249  for (auto&& r : fseed) {
1250  if (!matching_arg(r, npar)) {
1251  casadi_assert_dev(npar==1);
1252  ad_forward(replace_fseed(fseed, npar), fsens);
1253  return;
1254  }
1255  }
1256 
1257  // Make sure seeds have matching sparsity patterns
1258  for (auto it=fseed.begin(); it!=fseed.end(); ++it) {
1259  casadi_assert_dev(it->size()==n_in_);
1260  for (casadi_int i=0; i<n_in_; ++i) {
1261  if (it->at(i).sparsity()!=sparsity_in_[i]) {
1262  // Correct sparsity
1263  std::vector<std::vector<SX> > fseed2(fseed);
1264  for (auto&& r : fseed2) {
1265  for (casadi_int i=0; i<n_in_; ++i) r[i] = project(r[i], sparsity_in_[i]);
1266  }
1267  ad_forward(fseed2, fsens);
1268  return;
1269  }
1270  }
1271  }
1272 
1273  // Allocate results
1274  for (casadi_int d=0; d<nfwd; ++d) {
1275  fsens[d].resize(n_out_);
1276  for (casadi_int i=0; i<fsens[d].size(); ++i)
1277  if (fsens[d][i].sparsity()!=sparsity_out_[i])
1278  fsens[d][i] = SX::zeros(sparsity_out_[i]);
1279  }
1280 
1281  // Iterator to the binary operations
1282  std::vector<SXElem>::const_iterator b_it=operations_.begin();
1283 
1284  // Tape
1285  std::vector<TapeEl<SXElem> > s_pdwork(operations_.size());
1286  std::vector<TapeEl<SXElem> >::iterator it1 = s_pdwork.begin();
1287 
1288  // Evaluate algorithm
1289  if (verbose_) casadi_message("Evaluating algorithm forward");
1290  for (auto&& e : algorithm_) {
1291  switch (e.op) {
1292  case OP_INPUT:
1293  case OP_OUTPUT:
1294  case OP_CONST:
1295  case OP_PARAMETER:
1296  break;
1297  default:
1298  {
1299  const SXElem& f=*b_it++;
1300  switch (e.op) {
1301  CASADI_MATH_DER_BUILTIN(f->dep(0), f->dep(1), f, it1++->d)
1302  case OP_CALL:
1303  it1++->d[0] = f;
1304  }
1305  }
1306  }
1307  }
1308 
1309  // Work vector
1310  std::vector<SXElem> w(worksize_);
1311 
1312  // Calculate forward sensitivities
1313  if (verbose_) casadi_message("Calculating forward derivatives");
1314  for (casadi_int dir=0; dir<nfwd; ++dir) {
1315  std::vector<TapeEl<SXElem> >::const_iterator it2 = s_pdwork.begin();
1316  for (auto&& a : algorithm_) {
1317  switch (a.op) {
1318  case OP_INPUT:
1319  w[a.i0] = fseed[dir][a.i1].nonzeros()[a.i2]; break;
1320  case OP_OUTPUT:
1321  fsens[dir][a.i0].nonzeros()[a.i2] = w[a.i1]; break;
1322  case OP_CONST:
1323  case OP_PARAMETER:
1324  w[a.i0] = 0;
1325  break;
1326  case OP_IF_ELSE_ZERO:
1327  w[a.i0] = if_else_zero(it2++->d[1], w[a.i2]);
1328  break;
1329  case OP_CALL:
1330  {
1331  const auto& m = call_.el.at(a.i1);
1332  CallSX* call_node = static_cast<CallSX*>(it2->d[0].get());
1333 
1334  // Construct forward sensitivity function
1335  Function ff = m.f.forward(1);
1336 
1337  // Symbolic inputs to forward sensitivity function
1338  std::vector<SXElem> deps;
1339  deps.reserve(2*m.n_dep);
1340 
1341  // Set nominal inputs from node
1342  casadi_int offset = 0;
1343  for (casadi_int i=0;i<m.f_n_in;++i) {
1344  casadi_int nnz = ff.nnz_in(i);
1345  casadi_assert(nnz==0 || nnz==m.f.nnz_in(i), "Not implemented");
1346  for (casadi_int j=0;j<nnz;++j) {
1347  deps.push_back(call_node->dep(offset+j));
1348  }
1349  offset += m.f_nnz_in[i];
1350  }
1351 
1352  // Collect the nominal outputs needed by the derivative function
1353  std::vector<casadi_int> oind;
1354  offset = 0;
1355  for (casadi_int i=0;i<m.f_n_out;++i) {
1356  casadi_int nnz = ff.nnz_in(i+m.f_n_in);
1357  casadi_assert(nnz==0 || nnz==m.f.nnz_out(i), "Not implemented");
1358  for (casadi_int j=0;j<nnz;++j) {
1359  oind.push_back(offset+j);
1360  }
1361  offset += m.f_nnz_out[i];
1362  }
1363 
1364  auto nominal_outputs = call_node->get_output(oind);
1365  deps.insert(deps.end(), nominal_outputs.begin(), nominal_outputs.end());
1366 
1367  // Read in forward seeds from work vector
1368  offset = 0;
1369  for (casadi_int i=0;i<m.f_n_in;++i) {
1370  casadi_int nnz = ff.nnz_in(i+m.f_n_in+m.f_n_out);
1371  // nnz=0 occurs for is_diff_in[i] false
1372  casadi_assert(nnz==0 || nnz==m.f.nnz_in(i), "Not implemented");
1373  if (nnz) {
1374  for (casadi_int j=0;j<nnz;++j) {
1375  deps.push_back(w[m.dep[offset+j]]);
1376  }
1377  }
1378  offset += m.f_nnz_in[i];
1379  }
1380 
1381  // Call forward sensitivity function
1382  std::vector<SXElem> ret = SXElem::call(ff, deps);
1383 
1384  // Retrieve sensitivities
1385  offset = 0;
1386  casadi_int k = 0;
1387  for (casadi_int i=0;i<m.f_n_out;++i) {
1388  casadi_int nnz = ff.nnz_out(i);
1389  // nnz=0 occurs for is_diff_out[i] false
1390  casadi_assert(nnz==0 || nnz==m.f_nnz_out[i], "Not implemented");
1391  if (nnz) {
1392  for (casadi_int j=0;j<nnz;++j) {
1393  if (m.res[offset+j]>=0) w[m.res[offset+j]] = ret[k];
1394  k++;
1395  }
1396  }
1397  offset += m.f_nnz_out[i];
1398  }
1399  }
1400  it2++;
1401  break;
1402  CASADI_MATH_BINARY_BUILTIN // Binary operation
1403  w[a.i0] = it2->d[0] * w[a.i1] + it2->d[1] * w[a.i2];
1404  it2++;
1405  break;
1406  default: // Unary operation
1407  w[a.i0] = it2->d[0] * w[a.i1];
1408  it2++;
1409  }
1410  }
1411  }
1412  }
1413 
1414  void SXFunction::ad_reverse(const std::vector<std::vector<SX> >& aseed,
1415  std::vector<std::vector<SX> >& asens) const {
1416  if (verbose_) casadi_message(name_ + "::ad_reverse");
1417 
1418  // number of adjoint seeds
1419  casadi_int nadj = aseed.size();
1420  asens.resize(nadj);
1421 
1422  // Quick return if possible
1423  if (nadj==0) return;
1424 
1425  // Check if seeds need to have dimensions corrected
1426  casadi_int npar = 1;
1427  for (auto&& r : aseed) {
1428  if (!matching_res(r, npar)) {
1429  casadi_assert_dev(npar==1);
1430  ad_reverse(replace_aseed(aseed, npar), asens);
1431  return;
1432  }
1433  }
1434 
1435  // Make sure matching sparsity of fseed
1436  bool matching_sparsity = true;
1437  for (casadi_int d=0; d<nadj; ++d) {
1438  casadi_assert_dev(aseed[d].size()==n_out_);
1439  for (casadi_int i=0; matching_sparsity && i<n_out_; ++i)
1440  matching_sparsity = aseed[d][i].sparsity()==sparsity_out_[i];
1441  }
1442 
1443  // Correct sparsity if needed
1444  if (!matching_sparsity) {
1445  std::vector<std::vector<SX> > aseed2(aseed);
1446  for (casadi_int d=0; d<nadj; ++d)
1447  for (casadi_int i=0; i<n_out_; ++i)
1448  if (aseed2[d][i].sparsity()!=sparsity_out_[i])
1449  aseed2[d][i] = project(aseed2[d][i], sparsity_out_[i]);
1450  ad_reverse(aseed2, asens);
1451  return;
1452  }
1453 
1454  // Allocate results if needed
1455  for (casadi_int d=0; d<nadj; ++d) {
1456  asens[d].resize(n_in_);
1457  for (casadi_int i=0; i<asens[d].size(); ++i) {
1458  if (asens[d][i].sparsity()!=sparsity_in_[i]) {
1459  asens[d][i] = SX::zeros(sparsity_in_[i]);
1460  } else {
1461  std::fill(asens[d][i]->begin(), asens[d][i]->end(), 0);
1462  }
1463  }
1464  }
1465 
1466  // Iterator to the binary operations
1467  std::vector<SXElem>::const_iterator b_it=operations_.begin();
1468 
1469  // Tape
1470  std::vector<TapeEl<SXElem> > s_pdwork(operations_.size());
1471  std::vector<TapeEl<SXElem> >::iterator it1 = s_pdwork.begin();
1472 
1473  // Evaluate algorithm
1474  if (verbose_) casadi_message("Evaluating algorithm forward");
1475  for (auto&& a : algorithm_) {
1476  switch (a.op) {
1477  case OP_INPUT:
1478  case OP_OUTPUT:
1479  case OP_CONST:
1480  case OP_PARAMETER:
1481  break;
1482  default:
1483  {
1484  const SXElem& f=*b_it++;
1485  switch (a.op) {
1486  CASADI_MATH_DER_BUILTIN(f->dep(0), f->dep(1), f, it1++->d)
1487  case OP_CALL:
1488  it1++->d[0] = f;
1489  }
1490  }
1491  }
1492  }
1493 
1494  // Calculate adjoint sensitivities
1495  if (verbose_) casadi_message("Calculating adjoint derivatives");
1496 
1497  // Work vector
1498  std::vector<SXElem> w(worksize_, 0);
1499 
1500  for (casadi_int dir=0; dir<nadj; ++dir) {
1501  auto it2 = s_pdwork.rbegin();
1502  for (auto it = algorithm_.rbegin(); it!=algorithm_.rend(); ++it) {
1503  SXElem seed;
1504  switch (it->op) {
1505  case OP_INPUT:
1506  asens[dir][it->i1].nonzeros()[it->i2] = w[it->i0];
1507  w[it->i0] = 0;
1508  break;
1509  case OP_OUTPUT:
1510  w[it->i1] += aseed[dir][it->i0].nonzeros()[it->i2];
1511  break;
1512  case OP_CONST:
1513  case OP_PARAMETER:
1514  w[it->i0] = 0;
1515  break;
1516  case OP_IF_ELSE_ZERO:
1517  seed = w[it->i0];
1518  w[it->i0] = 0;
1519  w[it->i2] += if_else_zero(it2++->d[1], seed);
1520  break;
1521  case OP_CALL:
1522  {
1523  const auto& m = call_.el.at(it->i1);
1524  CallSX* call_node = static_cast<CallSX*>(it2->d[0].get());
1525 
1526  // Construct reverse sensitivity function
1527  Function fr = m.f.reverse(1);
1528 
1529  // Symbolic inputs to reverse sensitivity function
1530  std::vector<SXElem> deps;
1531  deps.reserve(m.n_dep+m.n_res);
1532 
1533  // Set nominal inputs from node
1534  casadi_int offset = 0;
1535  for (casadi_int i=0;i<m.f_n_in;++i) {
1536  casadi_int nnz = fr.nnz_in(i);
1537  casadi_assert(nnz==0 || nnz==m.f.nnz_in(i), "Not implemented");
1538  for (casadi_int j=0;j<nnz;++j) {
1539  deps.push_back(call_node->dep(offset+j));
1540  }
1541  offset += m.f_nnz_in[i];
1542  }
1543 
1544  // Collect the nominal outputs needed by the derivative function
1545  std::vector<casadi_int> oind;
1546  offset = 0;
1547  for (casadi_int i=0;i<m.f_n_out;++i) {
1548  casadi_int nnz = fr.nnz_in(i+m.f_n_in);
1549  casadi_assert(nnz==0 || nnz==m.f.nnz_out(i), "Not implemented");
1550  for (casadi_int j=0;j<nnz;++j) {
1551  oind.push_back(offset+j);
1552  }
1553  offset += m.f_nnz_out[i];
1554  }
1555 
1556  auto nominal_outputs = call_node->get_output(oind);
1557  deps.insert(deps.end(), nominal_outputs.begin(), nominal_outputs.end());
1558 
1559  // Read in reverse seeds from work vector
1560  offset = 0;
1561  for (casadi_int i=0;i<m.f_n_out;++i) {
1562  casadi_int nnz = fr.nnz_in(i+m.f_n_in+m.f_n_out);
1563  // nnz=0 occurs for is_diff_out[i] false
1564  casadi_assert(nnz==0 || nnz==m.f.nnz_out(i), "Not implemented");
1565  if (nnz) {
1566  for (casadi_int j=0;j<nnz;++j) {
1567  deps.push_back((m.res[offset+j]>=0) ? w[m.res[offset+j]] : 0);
1568  }
1569  }
1570  offset += m.f.nnz_out(i);
1571  }
1572 
1573  // Call reverse sensitivity function
1574  std::vector<SXElem> ret = SXElem::call(fr, deps);
1575 
1576  // Clear out reverse seeds
1577  for (casadi_int i=0;i<m.n_res;++i) {
1578  if (m.res[i]>=0) w[m.res[i]] = 0;
1579  }
1580 
1581  // Store reverse sensitivities into work vector
1582  offset = 0;
1583  casadi_int k = 0;
1584  for (casadi_int i=0;i<m.f_n_in;++i) {
1585  casadi_int nnz = fr.nnz_out(i);
1586  // nnz=0 occurs for is_diff_in[i] false
1587  casadi_assert(nnz==0 || nnz==m.f_nnz_in[i], "Not implemented");
1588  if (nnz) {
1589  for (casadi_int j=0;j<nnz;++j) {
1590  w[m.dep[offset+j]] += ret[k++];
1591  }
1592  }
1593  offset += m.f_nnz_in[i];
1594  }
1595  }
1596  it2++;
1597  break;
1598  CASADI_MATH_BINARY_BUILTIN // Binary operation
1599  seed = w[it->i0];
1600  w[it->i0] = 0;
1601  w[it->i1] += it2->d[0] * seed;
1602  w[it->i2] += it2++->d[1] * seed;
1603  break;
1604  default: // Unary operation
1605  seed = w[it->i0];
1606  w[it->i0] = 0;
1607  w[it->i1] += it2++->d[0] * seed;
1608  }
1609  }
1610  }
1611 
1612  // Drop sparsity of fully structurally-zero sensitivities, matching MXFunction (#4345)
1613  for (casadi_int d=0; d<nadj; ++d) {
1614  for (casadi_int i=0; i<n_in_; ++i) {
1615  SX& a = asens[d][i];
1616  if (a.is_zero()) a = SX(a.size1(), a.size2());
1617  }
1618  }
1619  }
1620 
1621  template<typename T, typename CT>
1623  CT*** call_arg, T*** call_res, casadi_int** call_iw, T** call_w, T** nz_in, T** nz_out) const {
1624  *call_arg += n_in_;
1625  *call_res += n_out_;
1626  *nz_in = *call_w + worksize_;
1627  *nz_out = *call_w + worksize_ + call_.sz_w_arg;
1628  *call_w = *call_w + worksize_ + call_.sz_w_arg + call_.sz_w_res;
1629 
1630  // Set up call_arg to point to nz_in
1631  T* ptr_w = *nz_in;
1632  for (casadi_int i=0;i<m.f_n_in;++i) {
1633  (*call_arg)[i] = ptr_w;
1634  ptr_w+=m.f_nnz_in[i];
1635  }
1636 
1637  // Set up call_res to point to nz_out
1638  ptr_w = *nz_out;
1639  for (casadi_int i=0;i<m.f_n_out;++i) {
1640  (*call_res)[i] = ptr_w;
1641  ptr_w+=m.f_nnz_out[i];
1642  }
1643  }
1644 
1645  template<typename T>
1646  void SXFunction::call_fwd(const AlgEl& e, const T** arg, T** res, casadi_int* iw, T* w) const {
1647  const auto& m = call_.el[e.i1];
1648  const T** call_arg = arg;
1649  T** call_res = res;
1650  casadi_int* call_iw = iw;
1651  T* call_w = w;
1652  T* nz_in;
1653  T* nz_out;
1654 
1655  call_setup(m, &call_arg, &call_res, &call_iw, &call_w, &nz_in, &nz_out);
1656 
1657  // Populate nz_in from work vector
1658  for (casadi_int i=0;i<m.n_dep;++i) {
1659  nz_in[i] = w[m.dep[i]];
1660  }
1661  // Perform call nz_in -> nz_out
1662  m.f(call_arg, call_res, call_iw, call_w);
1663 
1664  // Store nz_out results back in workvector
1665  for (casadi_int i=0;i<m.n_res;++i) {
1666  // Only if the result is actually needed
1667  if (m.res[i]>=0) {
1668  w[m.res[i]] = nz_out[i];
1669  }
1670  }
1671  }
1672 
1673 
1674  template<typename T>
1675  void SXFunction::call_rev(const AlgEl& e, T** arg, T** res, casadi_int* iw, T* w) const {
1676  const auto& m = call_.el[e.i1];
1677  bvec_t** call_arg = arg;
1678  bvec_t** call_res = res;
1679  casadi_int* call_iw = iw;
1680  bvec_t* call_w = w;
1681  bvec_t* nz_in;
1682  bvec_t* nz_out;
1683 
1684  call_setup(m, &call_arg, &call_res, &call_iw, &call_w, &nz_in, &nz_out);
1685 
1686  std::fill_n(nz_in, m.n_dep, 0);
1687 
1688  // Read in reverse seeds nz_out from work vector
1689  for (casadi_int i=0;i<m.n_res;++i) {
1690  nz_out[i] = (m.res[i]>=0) ? w[m.res[i]] : 0;
1691  }
1692 
1693  // Perform reverse mode call nz_out -> nz_in
1694  m.f.rev(call_arg, call_res, call_iw, call_w);
1695 
1696  // Clear out reverse seeds
1697  for (casadi_int i=0;i<m.n_res;++i) {
1698  if (m.res[i]>=0) w[m.res[i]] = 0;
1699  }
1700 
1701  // Store reverse sensitivities into work vector
1702  for (casadi_int i=0;i<m.n_dep;++i) {
1703  w[m.dep[i]] |= nz_in[i];
1704  }
1705  }
1706 
1708  sp_forward(const bvec_t** arg, bvec_t** res, casadi_int* iw, bvec_t* w, void* mem) const {
1709  // Fall back when forward mode not allowed
1710  if (sp_weight()==1 || sp_weight()==-1)
1711  return FunctionInternal::sp_forward(arg, res, iw, w, mem);
1712  // Propagate sparsity forward
1713  for (auto&& e : algorithm_) {
1714  switch (e.op) {
1715  case OP_CONST:
1716  case OP_PARAMETER:
1717  w[e.i0] = 0; break;
1718  case OP_INPUT:
1719  w[e.i0] = (arg[e.i1]!=nullptr && is_diff_in_[e.i1]) ? arg[e.i1][e.i2] : 0;
1720  break;
1721  case OP_OUTPUT:
1722  if (res[e.i0]!=nullptr) res[e.i0][e.i2] = is_diff_out_[e.i0] ? w[e.i1] : 0;
1723  break;
1724  case OP_CALL:
1725  call_fwd(e, arg, res, iw, w);
1726  break;
1727  default: // Unary or binary operation
1728  w[e.i0] = w[e.i1] | w[e.i2]; break;
1729  }
1730  }
1731  return 0;
1732  }
1733 
1735  eval_activity(const bvec_t** arg, bvec_t** res, casadi_int* iw, bvec_t* w, void* mem) const {
1736  const bvec_t nz = ~static_cast<bvec_t>(0);
1737  // Propagate signal activity forward (bit set = active (possibly nonzero))
1738  for (auto&& e : algorithm_) {
1739  switch (e.op) {
1740  case OP_CONST:
1741  w[e.i0] = (e.d!=0) ? nz : 0; break;
1742  case OP_PARAMETER:
1743  w[e.i0] = nz; break; // free variable: assume nonzero
1744  case OP_INPUT:
1745  w[e.i0] = (arg[e.i1]!=nullptr) ? arg[e.i1][e.i2] : 0;
1746  break;
1747  case OP_OUTPUT:
1748  if (res[e.i0]!=nullptr) res[e.i0][e.i2] = w[e.i1];
1749  break;
1750  case OP_CALL:
1751  call_activity(e, arg, res, iw, w);
1752  break;
1753  default: // Unary or binary operation
1754  if (casadi_math<double>::ndeps(e.op)==1) {
1755  // Zero input yields zero only for zero-preserving ops (sin, sqrt; not cos/exp)
1756  w[e.i0] = w[e.i1] ? nz : (operation_checker<F0XChecker>(e.op) ? 0 : nz);
1757  } else {
1758  const bool z0 = w[e.i1]!=0, z1 = w[e.i2]!=0;
1759  if (!z0 && !z1) w[e.i0] = operation_checker<F00Checker>(e.op) ? 0 : nz;
1760  else if (!z0 && z1) w[e.i0] = operation_checker<F0XChecker>(e.op) ? 0 : nz;
1761  else if ( z0 && !z1) w[e.i0] = operation_checker<FX0Checker>(e.op) ? 0 : nz;
1762  else w[e.i0] = nz;
1763  }
1764  break;
1765  }
1766  }
1767  return 0;
1768  }
1769 
1770  void SXFunction::call_activity(const AlgEl& e, const bvec_t** arg, bvec_t** res,
1771  casadi_int* iw, bvec_t* w) const {
1772  const auto& m = call_.el[e.i1];
1773  const bvec_t** call_arg = arg;
1774  bvec_t** call_res = res;
1775  casadi_int* call_iw = iw;
1776  bvec_t* call_w = w;
1777  bvec_t* nz_in;
1778  bvec_t* nz_out;
1779 
1780  call_setup(m, &call_arg, &call_res, &call_iw, &call_w, &nz_in, &nz_out);
1781 
1782  // Populate nz_in from work vector
1783  for (casadi_int i=0; i<m.n_dep; ++i) nz_in[i] = w[m.dep[i]];
1784  // Recurse: activity through the callee
1785  m.f.eval_activity(call_arg, call_res, call_iw, call_w);
1786  // Store nz_out results back in work vector
1787  for (casadi_int i=0; i<m.n_res; ++i) {
1788  if (m.res[i]>=0) w[m.res[i]] = nz_out[i];
1789  }
1790  }
1791 
1793  casadi_int* iw, bvec_t* w, void* mem) const {
1794  // Fall back when reverse mode not allowed
1795  if (sp_weight()==0 || sp_weight()==-1)
1796  return FunctionInternal::sp_reverse(arg, res, iw, w, mem);
1797  std::fill_n(w, sz_w(), 0);
1798 
1799  // Propagate sparsity backward
1800  for (auto it=algorithm_.rbegin(); it!=algorithm_.rend(); ++it) {
1801  // Temp seed
1802  bvec_t seed;
1803 
1804  // Propagate seeds
1805  switch (it->op) {
1806  case OP_CONST:
1807  case OP_PARAMETER:
1808  w[it->i0] = 0;
1809  break;
1810  case OP_INPUT:
1811  if (arg[it->i1]!=nullptr && is_diff_in_[it->i1])
1812  arg[it->i1][it->i2] |= w[it->i0];
1813  w[it->i0] = 0;
1814  break;
1815  case OP_OUTPUT:
1816  if (res[it->i0]!=nullptr && is_diff_out_[it->i0]) {
1817  w[it->i1] |= res[it->i0][it->i2];
1818  res[it->i0][it->i2] = 0;
1819  }
1820  break;
1821  case OP_CALL:
1822  call_rev(*it, arg, res, iw, w);
1823  break;
1824  default: // Unary or binary operation
1825  seed = w[it->i0];
1826  w[it->i0] = 0;
1827  w[it->i1] |= seed;
1828  w[it->i2] |= seed;
1829  }
1830  }
1831  return 0;
1832  }
1833 
1834  const SX SXFunction::sx_in(casadi_int ind) const {
1835  return in_.at(ind);
1836  }
1837 
1838  const std::vector<SX> SXFunction::sx_in() const {
1839  return in_;
1840  }
1841 
1842  std::vector<std::string> SXFunction::get_function() const {
1843  std::map<std::string, bool> flagged;
1844  for (auto&& a : algorithm_) {
1845  if (a.op==OP_CALL) {
1846  const auto& m = call_.el.at(a.i1);
1847  const Function &f = m.f;
1848  if (flagged.find(f.name())==flagged.end()) {
1849  flagged[f.name()] = true;
1850  }
1851  }
1852  }
1853  std::vector<std::string> ret;
1854  for (auto it : flagged) {
1855  ret.push_back(it.first);
1856  }
1857  return ret;
1858  }
1859 
1860  const Function& SXFunction::get_function(const std::string &name) const {
1861  for (auto&& a : algorithm_) {
1862  if (a.op==OP_CALL) {
1863  const auto& m = call_.el.at(a.i1);
1864  const Function &f = m.f;
1865  if (name==f.name()) return f;
1866  }
1867  }
1868  casadi_error("No such function '" + name + "'.");
1869  }
1870 
1871  bool SXFunction::is_a(const std::string& type, bool recursive) const {
1872  return type=="SXFunction" || (recursive && XFunction<SXFunction,
1873  SX, SXNode>::is_a(type, recursive));
1874  }
1875 
1876  void SXFunction::export_code_body(const std::string& lang,
1877  std::ostream &ss, const Dict& options) const {
1878 
1879  // Default values for options
1880  casadi_int indent_level = 0;
1881 
1882  // Read options
1883  for (auto&& op : options) {
1884  if (op.first=="indent_level") {
1885  indent_level = op.second;
1886  } else {
1887  casadi_error("Unknown option '" + op.first + "'.");
1888  }
1889  }
1890 
1891  // Construct indent string
1892  std::string indent;
1893  for (casadi_int i=0;i<indent_level;++i) {
1894  indent += " ";
1895  }
1896 
1897  // Non-cell aliases for inputs
1898  for (casadi_int i=0;i<n_in_;++i) {
1899  ss << indent << "argin_" << i << " = nonzeros_gen(varargin{" << i+1 << "});" << std::endl;
1900  }
1901 
1902  Function f = shared_from_this<Function>();
1903 
1904  for (casadi_int k=0;k<f.n_instructions();++k) {
1905  // Get operation
1906  casadi_int op = static_cast<casadi_int>(f.instruction_id(k));
1907  // Get input positions into workvector
1908  std::vector<casadi_int> o = f.instruction_output(k);
1909  // Get output positions into workvector
1910  std::vector<casadi_int> i = f.instruction_input(k);
1911  switch (op) {
1912  case OP_INPUT:
1913  {
1914  ss << indent << "w" << o[0] << " = " << "argin_" << i[0] << "(" << i[1]+1 << ");";
1915  ss << std::endl;
1916  }
1917  break;
1918  case OP_OUTPUT:
1919  {
1920  ss << indent << "argout_" << o[0] << "{" << o[1]+1 << "} = w" << i[0] << ";";
1921  ss << std::endl;
1922  }
1923  break;
1924  case OP_CONST:
1925  {
1926  std::ios_base::fmtflags fmtfl = ss.flags();
1927  ss << indent << "w" << o[0] << " = ";
1928  ss << std::scientific << std::setprecision(std::numeric_limits<double>::digits10 + 1);
1929  ss << f.instruction_constant(k) << ";" << std::endl;
1930  ss.flags(fmtfl);
1931  }
1932  break;
1933  case OP_SQ:
1934  {
1935  ss << indent << "w" << o[0] << " = " << "w" << i[0] << "^2;" << std::endl;
1936  }
1937  break;
1938  case OP_FABS:
1939  {
1940  ss << indent << "w" << o[0] << " = abs(" << "w" << i[0] << ");" << std::endl;
1941  }
1942  break;
1943  case OP_POW:
1944  case OP_CONSTPOW:
1945  ss << indent << "w" << o[0] << " = " << "w" << i[0] << ".^w" << i[1] << ";" << std::endl;
1946  break;
1947  case OP_NOT:
1948  ss << indent << "w" << o[0] << " = ~" << "w" << i[0] << ";" << std::endl;
1949  break;
1950  case OP_OR:
1951  ss << indent << "w" << o[0] << " = w" << i[0] << " | w" << i[1] << ";" << std::endl;
1952  break;
1953  case OP_AND:
1954  ss << indent << "w" << o[0] << " = w" << i[0] << " & w" << i[1] << ";" << std::endl;
1955  break;
1956  case OP_NE:
1957  ss << indent << "w" << o[0] << " = w" << i[0] << " ~= w" << i[1] << ";" << std::endl;
1958  break;
1959  case OP_IF_ELSE_ZERO:
1960  ss << indent << "w" << o[0] << " = ";
1961  ss << "if_else_zero_gen(w" << i[0] << ", w" << i[1] << ");" << std::endl;
1962  break;
1963  default:
1965  ss << indent << "w" << o[0] << " = " << casadi::casadi_math<double>::print(op,
1966  "w"+std::to_string(i[0]), "w"+std::to_string(i[1])) << ";" << std::endl;
1967  } else {
1968  ss << indent << "w" << o[0] << " = " << casadi::casadi_math<double>::print(op,
1969  "w"+std::to_string(i[0])) << ";" << std::endl;
1970  }
1971  }
1972  }
1973 
1974  }
1975 
1977  XFunction<SXFunction, SX, SXNode>(s) {
1978  int version = s.version("SXFunction", 1, 4);
1979  size_t n_instructions;
1980  s.unpack("SXFunction::n_instr", n_instructions);
1981 
1982  s.unpack("SXFunction::worksize", worksize_);
1983  s.unpack("SXFunction::free_vars", free_vars_);
1984  s.unpack("SXFunction::operations", operations_);
1985  s.unpack("SXFunction::constants", constants_);
1986  s.unpack("SXFunction::default_in", default_in_);
1987 
1988  if (version>=2) {
1989 
1990  s.unpack("SXFunction::call_sz_arg", call_.sz_arg);
1991  s.unpack("SXFunction::call_sz_res", call_.sz_res);
1992  s.unpack("SXFunction::call_sz_iw", call_.sz_iw);
1993  s.unpack("SXFunction::call_sz_w", call_.sz_w);
1994  s.unpack("SXFunction::call_sz_arg", call_.sz_w_arg);
1995  s.unpack("SXFunction::call_sz_res", call_.sz_w_res);
1996 
1997  size_t el_size;
1998  s.unpack("SXFunction::call_el_size", el_size);
1999  call_.el.reserve(el_size);
2000 
2001  // Loop over nodes
2002  for (casadi_int k=0;k<el_size;++k) {
2003  Function f;
2004  s.unpack("SXFunction::call_el_f", f);
2005  call_.el.emplace_back(f);
2006  auto& e = call_.el[k];
2007  s.unpack("SXFunction::call_el_dep", e.dep);
2008  s.unpack("SXFunction::call_el_res", e.res);
2009  s.unpack("SXFunction::call_el_copy_elision_arg", e.copy_elision_arg);
2010  s.unpack("SXFunction::call_el_copy_elision_offset", e.copy_elision_offset);
2011  }
2012 
2013  s.unpack("SXFunction::copy_elision", copy_elision_);
2014 
2015  } else {
2016  call_.sz_arg = 0;
2017  call_.sz_res = 0;
2018  call_.sz_iw = 0;
2019  call_.sz_w = 0;
2020  call_.sz_w_arg = 0;
2021  call_.sz_w_res = 0;
2022  call_.el.clear();
2023  copy_elision_.resize(n_instructions, false);
2024  }
2025 
2026  algorithm_.resize(n_instructions);
2027  for (casadi_int k=0;k<n_instructions;++k) {
2028  AlgEl& e = algorithm_[k];
2029  s.unpack("SXFunction::ScalarAtomic::op", e.op);
2030  s.unpack("SXFunction::ScalarAtomic::i0", e.i0);
2031  s.unpack("SXFunction::ScalarAtomic::i1", e.i1);
2032  s.unpack("SXFunction::ScalarAtomic::i2", e.i2);
2033  }
2034 
2035  // Default (persistent) options
2036  just_in_time_opencl_ = false;
2037  just_in_time_sparsity_ = false;
2038 
2039  s.unpack("SXFunction::live_variables", live_variables_);
2040  if (version>=3) {
2041  s.unpack("SXFunction::print_instructions", print_instructions_);
2042  } else {
2043  print_instructions_ = false;
2044  }
2045 
2046  if (version >= 4) s.unpack("SXFunction::dump_trace", dump_trace_);
2047 
2049  }
2050 
2053  s.version("SXFunction", 4);
2054  s.pack("SXFunction::n_instr", algorithm_.size());
2055 
2056  s.pack("SXFunction::worksize", worksize_);
2057  s.pack("SXFunction::free_vars", free_vars_);
2058  s.pack("SXFunction::operations", operations_);
2059  s.pack("SXFunction::constants", constants_);
2060  s.pack("SXFunction::default_in", default_in_);
2061 
2062  s.pack("SXFunction::call_sz_arg", call_.sz_arg);
2063  s.pack("SXFunction::call_sz_res", call_.sz_res);
2064  s.pack("SXFunction::call_sz_iw", call_.sz_iw);
2065  s.pack("SXFunction::call_sz_w", call_.sz_w);
2066  s.pack("SXFunction::call_sz_arg", call_.sz_w_arg);
2067  s.pack("SXFunction::call_sz_res", call_.sz_w_res);
2068 
2069  s.pack("SXFunction::call_el_size", call_.el.size());
2070  // Loop over ExtendedALgEl elements
2071  for (const auto& n : call_.el) {
2072  s.pack("SXFunction::call_el_f", n.f);
2073  s.pack("SXFunction::call_el_dep", n.dep);
2074  s.pack("SXFunction::call_el_res", n.res);
2075  s.pack("SXFunction::call_el_copy_elision_arg", n.copy_elision_arg);
2076  s.pack("SXFunction::call_el_copy_elision_offset", n.copy_elision_offset);
2077  }
2078 
2079  s.pack("SXFunction::copy_elision", copy_elision_);
2080 
2081  // Loop over algorithm
2082  for (const auto& e : algorithm_) {
2083  s.pack("SXFunction::ScalarAtomic::op", e.op);
2084  s.pack("SXFunction::ScalarAtomic::i0", e.i0);
2085  s.pack("SXFunction::ScalarAtomic::i1", e.i1);
2086  s.pack("SXFunction::ScalarAtomic::i2", e.i2);
2087  }
2088 
2089  s.pack("SXFunction::live_variables", live_variables_);
2090  s.pack("SXFunction::print_instructions", print_instructions_);
2091  s.pack("SXFunction::dump_trace", dump_trace_);
2092 
2094  }
2095 
2097  return new SXFunction(s);
2098  }
2099 
2100  void SXFunction::find(std::map<FunctionInternal*, std::pair<Function, size_t> >& all_fun,
2101  casadi_int max_depth) const {
2102  // Call to base class
2103  FunctionInternal::find(all_fun, max_depth);
2104  for (auto&& e : algorithm_) {
2105  if (e.op == OP_CALL) {
2106  const ExtendedAlgEl& m = call_.el.at(e.i1);
2107  add_embedded(all_fun, m.f, max_depth);
2108  }
2109  }
2110  }
2111 
2112  void SXFunction::change_option(const std::string& option_name,
2113  const GenericType& option_value) {
2114  if (option_name == "print_instructions") {
2115  print_instructions_ = option_value;
2116  } else if (option_name == "dump_trace") {
2117  bool value = option_value;
2118  casadi_assert(!value || !jit_, "dump_trace is not supported for JIT evaluation");
2119  dump_trace_ = value;
2120  } else {
2121  // Option not found - continue to base classes
2122  XFunction<SXFunction, SX, SXNode>::change_option(option_name, option_value);
2123  }
2124  }
2125 
2126  std::vector<SX> SXFunction::order(const std::vector<SX>& expr) {
2127 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
2128  std::lock_guard<std::mutex> lock(SX::get_mutex_temp());
2129 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
2130  // Stack used to sort the computational graph
2131  std::stack<SXNode*> s;
2132 
2133  // All nodes
2134  std::vector<SXNode*> nodes;
2135 
2136  // Add the list of nodes
2137  casadi_int ind=0;
2138  for (auto it = expr.begin(); it != expr.end(); ++it, ++ind) {
2139  casadi_int nz=0;
2140  for (auto itc = (*it)->begin(); itc != (*it)->end(); ++itc, ++nz) {
2141  // Add outputs to the list
2142  s.push(itc->get());
2144  }
2145  }
2146 
2147  // Clear temporary markers
2148  for (casadi_int i=0; i<nodes.size(); ++i) {
2149  nodes[i]->temp = 0;
2150  }
2151 
2152  std::vector<SX> ret(nodes.size());
2153  for (casadi_int i=0; i<nodes.size(); ++i) {
2154  ret[i] = SXElem::create(nodes[i]);
2155  }
2156 
2157  return ret;
2158  }
2159 
2160 } // namespace casadi
SXElem get_output(casadi_int oind) const override
Get an output.
Definition: call_sx.hpp:106
const SXElem & dep(casadi_int i) const override
get the reference of a dependency
Definition: call_sx.hpp:135
Helper class for C code generation.
std::string add_dependency(const Function &f)
Add a function dependency.
std::string arg(casadi_int i) const
Refer to argument.
void reserve_work(casadi_int n)
Reserve a maximum size of work elements, used for padding of index.
std::string constant(const std::vector< casadi_int > &v)
Represent an array constant; adding it when new.
std::string printf(const std::string &str, const std::vector< std::string > &arg=std::vector< std::string >())
Printf.
std::string print_op(casadi_int op, const std::string &a0)
Print an operation to a c file.
void print_vector(std::ostream &s, const std::string &name, const std::vector< casadi_int > &v)
Print casadi_int vector to a c file.
std::string res(casadi_int i) const
Refer to resuly.
bool avoid_stack() const
Avoid stack?
std::string sx_work(casadi_int i)
Declare a work vector element.
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
Internal class for Function.
void finish_trace(std::ostream &trace, double **res, int ret) const
void alloc_iw(size_t sz_iw, bool persistent=false)
Ensure required length of iw field.
std::vector< Sparsity > sparsity_in_
Input and output sparsity.
std::vector< std::vector< M > > replace_fseed(const std::vector< std::vector< M >> &fseed, casadi_int npar) const
Replace 0-by-0 forward seeds.
std::vector< bool > is_diff_out_
void alloc_res(size_t sz_res, bool persistent=false)
Ensure required length of res field.
std::string definition() const
Get function signature: name:(inputs)->(outputs)
void alloc_arg(size_t sz_arg, bool persistent=false)
Ensure required length of arg field.
static void print_canonical(std::ostream &stream, const Sparsity &sp, const double *nz)
Print canonical representation of a numeric matrix.
bool jit_
Use just-in-time compiler.
void add_embedded(std::map< FunctionInternal *, std::pair< Function, size_t > > &all_fun, const Function &dep, casadi_int max_depth) const
virtual void find(std::map< FunctionInternal *, std::pair< Function, size_t > > &all_fun, casadi_int max_depth) const
virtual double sp_weight() const
Weighting factor for chosing forward/reverse mode,.
size_t n_in_
Number of inputs and outputs.
virtual void eval_mx(const MXVector &arg, MXVector &res, bool always_inline, bool never_inline) const
Evaluate with symbolic matrices.
virtual int eval_sx(const SXElem **arg, SXElem **res, casadi_int *iw, SXElem *w, void *mem, bool always_inline, bool never_inline) const
Evaluate with symbolic scalars.
bool matching_arg(const std::vector< M > &arg, casadi_int &npar) const
Check if input arguments that needs to be replaced.
virtual int sp_forward(const bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const
Propagate sparsity forward.
std::vector< double > nz_in(const std::vector< DM > &arg) const
Convert from/to flat vector of input/output nonzeros.
static const Options options_
Options.
std::vector< Sparsity > sparsity_out_
void call(const std::vector< M > &arg, std::vector< M > &res, bool always_inline, bool never_inline) const
Call a function, templated.
bool matching_res(const std::vector< M > &arg, casadi_int &npar) const
Check if output arguments that needs to be replaced.
void disp(std::ostream &stream, bool more) const override
Display object.
size_t sz_w() const
Get required length of w field.
virtual int sp_reverse(bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const
Propagate sparsity backwards.
std::unique_ptr< std::ostream > open_trace(const double **arg, casadi_int dump_id) const
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
std::vector< double > nz_out(const std::vector< DM > &res) const
Convert from/to flat vector of input/output nonzeros.
casadi_int nnz_out() const
Number of input/output nonzeros.
void setup(void *mem, const double **arg, double **res, casadi_int *iw, double *w) const
Set the (persistent and temporary) work vectors.
static void trace_values(std::ostream &trace, const double *values, casadi_int nnz)
std::vector< bool > is_diff_in_
Are inputs and outputs differentiable?
void change_option(const std::string &option_name, const GenericType &option_value) override
Change option after object creation for debugging.
Dict generate_options(const std::string &target) const override
Reconstruct options dict.
std::vector< std::vector< M > > replace_aseed(const std::vector< std::vector< M >> &aseed, casadi_int npar) const
Replace 0-by-0 reverse seeds.
Function object.
Definition: function.hpp:60
Function forward(casadi_int nfwd) const
Get a function that calculates nfwd forward derivatives.
Definition: function.cpp:1324
casadi_int nnz_out() const
Get number of output nonzeros.
Definition: function.cpp:1007
casadi_int n_instructions() const
Number of instruction in the algorithm (SXFunction/MXFunction)
Definition: function.cpp:1906
size_t sz_res() const
Get required length of res field.
Definition: function.cpp:1237
const std::string & name() const
Name of the function.
Definition: function.cpp:1504
Function reverse(casadi_int nadj) const
Get a function that calculates nadj adjoint derivatives.
Definition: function.cpp:1332
std::vector< casadi_int > instruction_input(casadi_int k) const
Locations in the work vector for the inputs of the instruction.
Definition: function.cpp:1938
const Sparsity & sparsity_in(casadi_int ind) const
Get sparsity of a given input.
Definition: function.cpp:1167
std::vector< casadi_int > instruction_output(casadi_int k) const
Location in the work vector for the output of the instruction.
Definition: function.cpp:1954
size_t sz_iw() const
Get required length of iw field.
Definition: function.cpp:1239
casadi_int n_out() const
Get the number of function outputs.
Definition: function.cpp:975
casadi_int n_in() const
Get the number of function inputs.
Definition: function.cpp:971
size_t sz_w() const
Get required length of w field.
Definition: function.cpp:1241
size_t sz_arg() const
Get required length of arg field.
Definition: function.cpp:1235
casadi_int nnz_in() const
Get number of input nonzeros.
Definition: function.cpp:1003
double instruction_constant(casadi_int k) const
Get the floating point output argument of an instruction (SXFunction)
Definition: function.cpp:1946
casadi_int instruction_id(casadi_int k) const
Identifier index of the instruction (SXFunction/MXFunction)
Definition: function.cpp:1930
casadi_int size2() const
Get the second dimension (i.e. number of columns)
casadi_int size1() const
Get the first dimension (i.e. number of rows)
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.
Generic data type, can hold different types such as bool, casadi_int, std::string etc.
static casadi_int copy_elision_min_size
static void check()
Raises an error if an interrupt was captured.
MX - Matrix expression.
Definition: mx.hpp:92
Sparse matrix class. SX and DM are specializations.
Definition: matrix_decl.hpp:99
bool is_zero() const
check if the matrix is 0 (note that false negative answers are possible)
void print_scalar(std::ostream &stream) const
Print scalar.
static std::vector< SXElem > split(const SXElem &e, casadi_int n)
Definition: output_sx.hpp:139
Base class for FunctionInternal and LinsolInternal.
bool verbose_
Verbose printout.
void clear_mem()
Clear all memory (called from destructor)
The basic scalar symbolic class of CasADi.
Definition: sx_elem.hpp:75
void assignIfDuplicate(const SXElem &scalar, casadi_int depth=1)
Assign to another expression, if a duplicate.
Definition: sx_elem.cpp:117
static std::vector< SXElem > call(const Function &f, const std::vector< SXElem > &deps)
Definition: sx_elem.cpp:232
static SXElem create(SXNode *node)
Definition: sx_elem.cpp:62
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
Internal node class for SXFunction.
Definition: sx_function.hpp:54
std::vector< SXElem > operations_
The expressions corresponding to each binary operation.
SXFunction(const std::string &name, const std::vector< Matrix< SXElem > > &inputv, const std::vector< Matrix< SXElem > > &outputv, const std::vector< std::string > &name_in, const std::vector< std::string > &name_out)
Constructor.
void eval_mx(const MXVector &arg, MXVector &res, bool always_inline, bool never_inline) const override
Evaluate symbolically, MX type.
SX instructions_sx() const override
get SX expression associated with instructions
void call_activity(const AlgEl &e, const bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w) const
void ad_reverse(const std::vector< std::vector< SX > > &aseed, std::vector< std::vector< SX > > &asens) const
Calculate reverse mode directional derivatives.
std::string print(const ScalarAtomic &a) const
bool should_inline(bool with_sx, bool always_inline, bool never_inline) const override
void codegen_declarations(CodeGenerator &g) const override
Generate code for the declarations of the C function.
void print_res(std::ostream &stream, casadi_int k, const ScalarAtomic &el, const double *w) const
const std::vector< SX > sx_in() const override
Get function input(s) and output(s)
void init(const Dict &opts) override
Initialize.
void export_code_body(const std::string &lang, std::ostream &stream, const Dict &options) const override
Export function in a specific language.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
void init_copy_elision()
Part of initialize responsible of prepaprign copy elision.
static const Options options_
Options.
std::vector< bool > copy_elision_
Copy elision per algel.
int eval_activity(const bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const override
Propagate signal activity forward.
bool is_smooth() const
Check if smooth.
int sp_forward(const bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const override
Propagate sparsity forward.
size_t codegen_sz_w(const CodeGenerator &g) const override
Get the size of the work vector, for codegen.
void call_rev(const AlgEl &e, T **arg, T **res, casadi_int *iw, T *w) const
bool has_free() const override
Does the function have free variables.
std::vector< std::string > get_function() const override
Get list of dependency functions.
void call_fwd(const AlgEl &e, const T **arg, T **res, casadi_int *iw, T *w) const
std::vector< SXElem > constants_
The expressions corresponding to each constant.
int sp_reverse(bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const override
Propagate sparsity backwards.
bool just_in_time_opencl_
With just-in-time compilation using OpenCL.
struct casadi::SXFunction::CallInfo call_
void change_option(const std::string &option_name, const GenericType &option_value) override
Change option after object creation for debugging.
void codegen_body(CodeGenerator &g) const override
Generate code for the body of the C function.
static std::vector< SX > order(const std::vector< SX > &expr)
void print_arg(std::ostream &stream, casadi_int k, const ScalarAtomic &el, const double *w) const
int eval_sx(const SXElem **arg, SXElem **res, casadi_int *iw, SXElem *w, void *mem, bool always_inline, bool never_inline) const override
evaluate symbolically while also propagating directional derivatives
std::vector< std::string > get_free() const override
Print free variables.
std::vector< AlgEl > algorithm_
all binary nodes of the tree in the order of execution
int eval(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Evaluate numerically, work vectors given.
Definition: sx_function.cpp:72
std::vector< SXElem > free_vars_
Free variables.
bool is_a(const std::string &type, bool recursive) const override
Check if the function is of a particular type.
std::vector< double > default_in_
Default input values.
void trace_instruction(std::ostream &trace, casadi_int k, const double *w, bool output) const
void disp_more(std::ostream &stream) const override
Print the algorithm.
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize without type information.
~SXFunction() override
Destructor.
Definition: sx_function.cpp:68
Dict generate_options(const std::string &target="clone") const override
Reconstruct options dict.
bool print_instructions_
Print each operation during evaluation.
void call_setup(const ExtendedAlgEl &m, CT ***call_arg, T ***call_res, casadi_int **call_iw, T **call_w, T **nz_in, T **nz_out) const
bool live_variables_
Live variables?
void find(std::map< FunctionInternal *, std::pair< Function, size_t > > &all_fun, casadi_int max_depth) const override
void ad_forward(const std::vector< std::vector< SX > > &fseed, std::vector< std::vector< SX > > &fsens) const
Calculate forward mode directional derivatives.
casadi_int n_instructions() const override
Get the number of atomic operations.
bool just_in_time_sparsity_
With just-in-time compilation for the sparsity propagation.
Internal node class for SX.
Definition: sx_node.hpp:49
virtual const SXElem & dep(casadi_int i) const
get the reference of a child
Definition: sx_node.cpp:80
virtual double to_double() const
Get value of a constant node.
Definition: sx_node.cpp:56
virtual bool is_symbolic() const
check properties of a node
Definition: sx_node.hpp:71
virtual casadi_int op() const =0
get the operation
virtual bool is_constant() const
check properties of a node
Definition: sx_node.hpp:69
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
Internal node class for the base class of SXFunction and MXFunction.
Definition: x_function.hpp:57
std::vector< Matrix< SXElem > > out_
Outputs of the function (needed for symbolic calculations)
Definition: x_function.hpp:279
void delayed_deserialize_members(DeserializingStream &s)
Definition: x_function.hpp:316
void init(const Dict &opts) override
Initialize.
Definition: x_function.hpp:336
std::vector< Matrix< SXElem > > in_
Inputs of the function (needed for symbolic calculations)
Definition: x_function.hpp:274
void delayed_serialize_members(SerializingStream &s) const
Helper functions to avoid recursion limit.
Definition: x_function.hpp:322
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: x_function.hpp:328
static void sort_depth_first(std::stack< SXNode * > &s, std::vector< SXNode * > &nodes)
Topological sorting of the nodes based on Depth-First Search (DFS)
Definition: x_function.hpp:412
casadi_limits class
The casadi namespace.
Definition: archiver.cpp:28
std::string join(const std::vector< std::string > &l, const std::string &delim)
double if_else_zero(double x, double y)
Conditional assignment.
Definition: calculus.hpp:295
unsigned long long bvec_t
void casadi_project(const T1 *x, const casadi_int *sp_x, T1 *y, const casadi_int *sp_y, T1 *w)
Sparse copy: y <- x, w work vector (length >= number of rows)
std::vector< MX > MXVector
Definition: mx.hpp:1107
@ OT_DOUBLEVECTOR
Matrix< SXElem > SX
Definition: sx_fwd.hpp:32
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::ostream & uout()
@ OP_NE
Definition: calculus.hpp:70
@ OP_IF_ELSE_ZERO
Definition: calculus.hpp:71
@ OP_AND
Definition: calculus.hpp:70
@ OP_OUTPUT
Definition: calculus.hpp:82
@ OP_CONST
Definition: calculus.hpp:79
@ OP_OR
Definition: calculus.hpp:70
@ OP_INPUT
Definition: calculus.hpp:82
@ OP_POW
Definition: calculus.hpp:66
@ OP_PARAMETER
Definition: calculus.hpp:85
@ OP_FABS
Definition: calculus.hpp:71
@ OP_CALL
Definition: calculus.hpp:88
@ OP_CONSTPOW
Definition: calculus.hpp:66
@ OP_NOT
Definition: calculus.hpp:70
@ OP_SQ
Definition: calculus.hpp:67
Function memory with temporary work vectors.
Options metadata for a class.
Definition: options.hpp:40
std::vector< ExtendedAlgEl > el
std::vector< int > copy_elision_offset
std::vector< int > copy_elision_arg
ExtendedAlgEl(const Function &fun)
Definition: sx_function.cpp:44
An atomic operation for the SXElem virtual machine.
Definition: sx_function.hpp:37
int i0
Operator index.
Definition: sx_function.hpp:39
Easy access to all the functions for a particular type.
Definition: calculus.hpp:1135
static casadi_int ndeps(unsigned char op)
Number of dependencies.
Definition: calculus.hpp:1633
static std::string print(unsigned char op, const std::string &x, const std::string &y)
Print.
Definition: calculus.hpp:1651