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