mx_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 #include "mx_function.hpp"
26 #include "casadi_misc.hpp"
27 #include "casadi_common.hpp"
28 #include "global_options.hpp"
29 #include "casadi_interrupt.hpp"
30 #include "io_instruction.hpp"
31 #include "serializing_stream.hpp"
32 
33 #include <stack>
34 #include <typeinfo>
35 
36 // Throw informative error message
37 #define CASADI_THROW_ERROR(FNAME, WHAT) \
38 throw CasadiException("Error in MXFunction::" FNAME " at " + CASADI_WHERE + ":\n"\
39  + std::string(WHAT));
40 
41 namespace casadi {
42 
43  MXFunction::MXFunction(const std::string& name,
44  const std::vector<MX>& inputv,
45  const std::vector<MX>& outputv,
46  const std::vector<std::string>& name_in,
47  const std::vector<std::string>& name_out) :
48  XFunction<MXFunction, MX, MXNode>(name, inputv, outputv, name_in, name_out) {
49  }
50 
52  clear_mem();
53  }
54 
57  {{"default_in",
59  "Default input values"}},
60  {"live_variables",
61  {OT_BOOL,
62  "Reuse variables in the work vector"}},
63  {"dump_trace",
64  {OT_BOOL,
65  "Dump interpreted instruction values to name.NNNNNN.trace.jsonl in dump_dir, "
66  "using the dump_in/dump_out counter. [false]"}},
67  {"print_instructions",
68  {OT_BOOL,
69  "Print each operation during evaluation. Influenced by print_canonical."}},
70  {"cse",
71  {OT_BOOL,
72  "Perform common subexpression elimination (complexity is N*log(N) in graph size)"}},
73  {"allow_free",
74  {OT_BOOL,
75  "Allow construction with free variables (Default: false)"}},
76  {"allow_duplicate_io_names",
77  {OT_BOOL,
78  "Allow construction with duplicate io names (Default: false)"}}
79  }
80  };
81 
82  Dict MXFunction::generate_options(const std::string& target) const {
84  opts["dump_trace"] = dump_trace_;
85  if (target=="clone") opts["default_in"] = default_in_;
86  opts["live_variables"] = live_variables_;
87  opts["print_instructions"] = print_instructions_;
88  return opts;
89  }
90 
91  MX MXFunction::instruction_MX(casadi_int k) const {
92  return algorithm_.at(k).data;
93  }
94 
95  std::vector<casadi_int> MXFunction::instruction_input(casadi_int k) const {
96  auto e = algorithm_.at(k);
97  if (e.op==OP_INPUT) {
98  const IOInstruction* io = static_cast<const IOInstruction*>(e.data.get());
99  return { io->ind() };
100  } else {
101  return e.arg;
102  }
103  }
104 
105  std::vector<casadi_int> MXFunction::instruction_output(casadi_int k) const {
106  auto e = algorithm_.at(k);
107  if (e.op==OP_OUTPUT) {
108  const IOInstruction* io = static_cast<const IOInstruction*>(e.data.get());
109  return { io->ind() };
110  } else {
111  return e.res;
112  }
113  }
114 
115  void MXFunction::init(const Dict& opts) {
116  // Call the init function of the base class
118  if (verbose_) casadi_message(name_ + "::init");
119 
120  // Default (temporary) options
121  live_variables_ = true;
122  print_instructions_ = false;
123  bool cse_opt = false;
124  bool allow_free = false;
125 
126  // Read options
127  for (auto&& op : opts) {
128  if (op.first=="default_in") {
129  default_in_ = op.second;
130  } else if (op.first=="live_variables") {
131  live_variables_ = op.second;
132  } else if (op.first=="dump_trace") {
133  dump_trace_ = op.second;
134  } else if (op.first=="print_instructions") {
135  print_instructions_ = op.second;
136  } else if (op.first=="cse") {
137  cse_opt = op.second;
138  } else if (op.first=="allow_free") {
139  allow_free = op.second;
140  }
141  }
142 
143  casadi_assert(!dump_trace_ || !jit_, "dump_trace is not supported for JIT evaluation");
144 
145  // Check/set default inputs
146  if (default_in_.empty()) {
147  default_in_.resize(n_in_, 0);
148  } else {
149  casadi_assert(default_in_.size()==n_in_,
150  "Option 'default_in' has incorrect length");
151  }
152 
153  // Check if naked MultiOutput nodes are present
154  for (const MX& e : out_) {
155  casadi_assert(!e->has_output(),
156  "Function output contains MultiOutput nodes. "
157  "You must use get_output() to make a concrete instance.");
158  }
159 
160  if (cse_opt) out_ = cse(out_);
161 
162  // Stack used to sort the computational graph
163  std::stack<MXNode*> s;
164 
165  // All nodes
166  std::vector<MXNode*> nodes;
167 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
168  std::lock_guard<std::mutex> lock(MX::get_mutex_temp());
169 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
170 
171  // Add the list of nodes
172  for (casadi_int ind=0; ind<out_.size(); ++ind) {
173  // Loop over primitives of each output
174  std::vector<MX> prim = out_[ind].primitives();
175  casadi_int nz_offset=0;
176  for (casadi_int p=0; p<prim.size(); ++p) {
177  // Get the nodes using a depth first search
178  s.push(prim[p].get());
179  sort_depth_first(s, nodes);
180  // Add an output instruction ("data" below will take ownership)
181  nodes.push_back(new Output(prim[p], ind, p, nz_offset));
182  // Update offset
183  nz_offset += prim[p].nnz();
184  }
185  }
186 
187  // Set the temporary variables to be the corresponding place in the sorted graph
188  for (casadi_int i=0; i<nodes.size(); ++i) {
189  nodes[i]->temp = i;
190  }
191 
192  // Place in the algorithm for each node
193  std::vector<casadi_int> place_in_alg;
194  place_in_alg.reserve(nodes.size());
195 
196  // Input instructions
197  std::vector<std::pair<casadi_int, MXNode*> > symb_loc;
198 
199  // Count the number of times each node is used
200  std::vector<casadi_int> refcount(nodes.size(), 0);
201 
202  // Get the sequence of instructions for the virtual machine
203  algorithm_.resize(0);
204  algorithm_.reserve(nodes.size());
205  for (MXNode* n : nodes) {
206 
207  // Get the operation
208  casadi_int op = n->op();
209 
210  // Store location if parameter (or input)
211  if (op==OP_PARAMETER) {
212  symb_loc.push_back(std::make_pair(algorithm_.size(), n));
213  }
214 
215  // If a new element in the algorithm needs to be added
216  if (op>=0) {
217  AlgEl ae;
218  ae.op = op;
219  ae.data.own(n);
220  ae.arg.resize(n->n_dep());
221  for (casadi_int i=0; i<n->n_dep(); ++i) {
222  ae.arg[i] = n->dep(i)->temp;
223  }
224  ae.res.resize(n->nout());
225  if (n->has_output()) {
226  std::fill(ae.res.begin(), ae.res.end(), -1);
227  } else if (!ae.res.empty()) {
228  ae.res[0] = n->temp;
229  }
230 
231  // Increase the reference count of the dependencies
232  for (casadi_int c=0; c<ae.arg.size(); ++c) {
233  if (ae.arg[c]>=0) {
234  refcount[ae.arg[c]]++;
235  }
236  }
237 
238  // Save to algorithm
239  place_in_alg.push_back(algorithm_.size());
240  algorithm_.push_back(ae);
241 
242  } else { // Function output node
243  // Get the output index
244  casadi_int oind = n->which_output();
245 
246  // Get the index of the parent node
247  casadi_int pind = place_in_alg[n->dep(0)->temp];
248 
249  // Save location in the algorithm element corresponding to the parent node
250  casadi_int& otmp = algorithm_[pind].res.at(oind);
251  if (otmp<0) {
252  otmp = n->temp; // First time this function output is encountered, save to algorithm
253  } else {
254  n->temp = otmp; // Function output is a duplicate, use the node encountered first
255  }
256 
257  // Not in the algorithm
258  place_in_alg.push_back(-1);
259  }
260  }
261 
262  // Place in the work vector for each of the nodes in the tree (overwrites the reference counter)
263  std::vector<casadi_int>& place = place_in_alg; // Reuse memory as it is no longer needed
264  place.resize(nodes.size());
265 
266  // Stack with unused elements in the work vector, sorted by sparsity pattern
267  SPARSITY_MAP<casadi_int, std::stack<casadi_int> > unused_all;
268 
269  // Work vector size
270  casadi_int worksize = 0;
271 
272  // Find a place in the work vector for the operation
273  for (auto&& e : algorithm_) {
274 
275  // There are two tasks, allocate memory of the result and free the
276  // memory off the arguments, order depends on whether inplace is possible
277  casadi_int first_to_free = 0;
278  casadi_int last_to_free = e.data->n_inplace();
279  for (casadi_int task=0; task<2; ++task) {
280 
281  // Dereference or free the memory of the arguments
282  for (casadi_int c=last_to_free-1; c>=first_to_free; --c) { // reverse order so that the
283  // first argument will end up
284  // at the top of the stack
285 
286  // Index of the argument
287  casadi_int& ch_ind = e.arg[c];
288  if (ch_ind>=0) {
289 
290  // Decrease reference count and add to the stack of
291  // unused variables if the count hits zero
292  casadi_int remaining = --refcount[ch_ind];
293 
294  // Free variable for reuse
295  if (live_variables_ && remaining==0) {
296 
297  // Get a pointer to the sparsity pattern of the argument that can be freed
298  casadi_int nnz = nodes[ch_ind]->sparsity().nnz();
299 
300  // Add to the stack of unused work vector elements for the current sparsity
301  unused_all[nnz].push(place[ch_ind]);
302  }
303 
304  // Point to the place in the work vector instead of to the place in the list of nodes
305  ch_ind = place[ch_ind];
306  }
307  }
308 
309  // Nothing more to allocate
310  if (task==1) break;
311 
312  // Free the rest in the next iteration
313  first_to_free = last_to_free;
314  last_to_free = e.arg.size();
315 
316  // Allocate/reuse memory for the results of the operation
317  for (casadi_int c=0; c<e.res.size(); ++c) {
318  if (e.res[c]>=0) {
319 
320  // Are reuse of variables (live variables) enabled?
321  if (live_variables_) {
322  // Get a pointer to the sparsity pattern node
323  casadi_int nnz = e.data->sparsity(c).nnz();
324 
325  // Get a reference to the stack for the current sparsity
326  std::stack<casadi_int>& unused = unused_all[nnz];
327 
328  // Try to reuse a variable from the stack if possible (last in, first out)
329  if (!unused.empty()) {
330  e.res[c] = place[e.res[c]] = unused.top();
331  unused.pop();
332  continue; // Success, no new element needed in the work vector
333  }
334  }
335 
336  // Allocate a new element in the work vector
337  e.res[c] = place[e.res[c]] = worksize++;
338  }
339  }
340  }
341  }
342 
343  if (verbose_) {
344  if (live_variables_) {
345  casadi_message("Using live variables: work array is " + str(worksize)
346  + " instead of " + str(nodes.size()));
347  } else {
348  casadi_message("Live variables disabled.");
349  }
350  }
351 
352  // Allocate work vectors (numeric)
353  workloc_.resize(worksize+1);
354  std::fill(workloc_.begin(), workloc_.end(), -1);
355  size_t wind=0, sz_w=0;
356  for (auto&& e : algorithm_) {
357  if (e.op!=OP_OUTPUT) {
358  for (casadi_int c=0; c<e.res.size(); ++c) {
359  if (e.res[c]>=0) {
360  alloc_arg(e.data->sz_arg());
361  alloc_res(e.data->sz_res());
362  alloc_iw(e.data->sz_iw());
363  // workloc_ (register offsets) is shared by VM eval and codegen, so
364  // the scratch region must cover whichever needs more
365  sz_w = std::max(sz_w, std::max(e.data->sz_w(), e.data->codegen_sz_w()));
366  if (workloc_[e.res[c]] < 0) {
367  workloc_[e.res[c]] = wind;
368  wind += e.data->sparsity(c).nnz();
369  }
370  }
371  }
372  }
373  }
374  workloc_.back()=wind;
375  for (casadi_int i=0; i<workloc_.size(); ++i) {
376  if (workloc_[i]<0) workloc_[i] = i==0 ? 0 : workloc_[i-1];
377  workloc_[i] += sz_w;
378  }
379  sz_w += wind;
380  alloc_w(sz_w);
381 
382  // Reset the temporary variables
383  for (casadi_int i=0; i<nodes.size(); ++i) {
384  if (nodes[i]) {
385  nodes[i]->temp = 0;
386  }
387  }
388 
389  // Now mark each input's place in the algorithm
390  for (auto it=symb_loc.begin(); it!=symb_loc.end(); ++it) {
391  it->second->temp = it->first+1;
392  }
393 
394  // Add input instructions, loop over inputs
395  for (casadi_int ind=0; ind<in_.size(); ++ind) {
396  // Loop over symbolic primitives of each input
397  std::vector<MX> prim = in_[ind].primitives();
398  casadi_int nz_offset=0;
399  for (casadi_int p=0; p<prim.size(); ++p) {
400  casadi_int i = prim[p].get_temp()-1;
401  if (i>=0) {
402  // Mark read
403  prim[p].set_temp(0);
404 
405  // Replace parameter with input instruction
406  algorithm_[i].data.own(new Input(prim[p].sparsity(), ind, p, nz_offset));
407  algorithm_[i].op = OP_INPUT;
408  }
409  nz_offset += prim[p]->nnz();
410  }
411  }
412 
413  // Locate free variables
414  free_vars_.clear();
415  for (auto it=symb_loc.begin(); it!=symb_loc.end(); ++it) {
416  casadi_int i = it->second->temp-1;
417  if (i>=0) {
418  // Save to list of free parameters
419  free_vars_.push_back(MX::create(it->second));
420 
421  // Remove marker
422  it->second->temp=0;
423  }
424  }
425 
426  if (!allow_free && has_free()) {
427  casadi_error(name_ + "::init: Initialization failed since variables [" +
428  join(get_free(), ", ") + "] are free. These symbols occur in the output expressions "
429  "but you forgot to declare these as inputs. "
430  "Set option 'allow_free' to allow free variables.");
431  }
432 
433  // Does any nodes require reference counting for codegen?
434  // NOTE: this excludes CALL nodes
435  for (auto&& a : algorithm_) {
436  if (a.data->has_refcount()) {
437  has_refcount_ = true;
438  break;
439  }
440  }
441 
442  }
443 
444  int MXFunction::eval(const double** arg, double** res,
445  casadi_int* iw, double* w, void* mem) const {
446  auto trace = dump_trace_ ? open_trace(arg, static_cast<FunctionMemory*>(mem)->dump_id)
447  : nullptr;
448  try {
449  if (verbose_) casadi_message(name_ + "::eval");
450  setup(mem, arg, res, iw, w);
451  // Work vector and temporaries to hold pointers to operation input and outputs
452  const double** arg1 = arg+n_in_;
453  double** res1 = res+n_out_;
454 
455  // Make sure that there are no free variables
456  if (!free_vars_.empty()) {
457  std::stringstream ss;
458  disp(ss, false);
459  casadi_error("Cannot evaluate \"" + ss.str() + "\" since variables "
460  + str(free_vars_) + " are free.");
461  }
462 
463  // Operation number (for printing)
464  casadi_int k = 0;
465 
466  // Evaluate all of the nodes of the algorithm:
467  // should only evaluate nodes that have not yet been calculated!
468  for (auto&& e : algorithm_) {
469  if (trace) trace_instruction(*trace, k, w, false);
470  // Perform the operation
471  if (e.op==OP_INPUT) {
472  // Pass an input
473  double *w1 = w+workloc_[e.res.front()];
474  casadi_int nnz=e.data.nnz();
475  casadi_int i=e.data->ind();
476  casadi_int nz_offset=e.data->offset();
477  if (arg[i]==nullptr) {
478  std::fill(w1, w1+nnz, 0);
479  } else {
480  std::copy(arg[i]+nz_offset, arg[i]+nz_offset+nnz, w1);
481  }
482  } else if (e.op==OP_OUTPUT) {
483  // Get an output
484  double *w1 = w+workloc_[e.arg.front()];
485  casadi_int nnz=e.data->dep().nnz();
486  casadi_int i=e.data->ind();
487  casadi_int nz_offset=e.data->offset();
488  if (res[i]) std::copy(w1, w1+nnz, res[i]+nz_offset);
489  } else {
490  // Point pointers to the data corresponding to the element
491  for (casadi_int i=0; i<e.arg.size(); ++i)
492  arg1[i] = e.arg[i]>=0 ? w+workloc_[e.arg[i]] : nullptr;
493  for (casadi_int i=0; i<e.res.size(); ++i)
494  res1[i] = e.res[i]>=0 ? w+workloc_[e.res[i]] : nullptr;
495 
496  // Evaluate
497  if (print_instructions_) print_arg(uout(), k, e, arg1);
498  if (e.data->eval(arg1, res1, iw, w)) {
499  if (trace) finish_trace(*trace, res, 1);
500  return 1;
501  }
502  if (print_instructions_) print_res(uout(), k, e, res1);
503  }
504  if (trace) trace_instruction(*trace, k, w, true);
505  // Increase counter
506  k++;
507  }
508  } catch (...) {
509  if (trace) *trace << "{\"event\":\"error\"}\n";
510  throw;
511  }
512  if (trace) finish_trace(*trace, res, 0);
513  return 0;
514  }
515 
516  void MXFunction::trace_instruction(std::ostream& trace, casadi_int k,
517  const double* w, bool output) const {
518  const auto& e = algorithm_.at(k);
519  const auto& slots = output ? e.res : e.arg;
520  casadi_int n = slots.size();
521  if (e.op == OP_INPUT && !output) n = 0;
522  if (e.op == OP_OUTPUT && output) n = 0;
523  trace << "{\"instruction\":" << k << ",\"op\":" << e.op
524  << ",\"phase\":\"" << (output ? "outputs" : "inputs") << "\",\"values\":[";
525  for (casadi_int i = 0; i < n; ++i) {
526  if (i) trace << ",";
527  casadi_int nnz = output ? e.data->sparsity(i).nnz() : e.data->dep(i).nnz();
528  trace_values(trace, slots[i] < 0 ? nullptr : w + workloc_[slots[i]], nnz);
529  }
530  trace << "]}\n";
531  }
532 
533  std::string MXFunction::print(const AlgEl& el) const {
534  std::stringstream s;
535  if (el.op==OP_OUTPUT) {
536  s << "output[" << el.data->ind() << "][" << el.data->segment() << "]"
537  << " = @" << el.arg.at(0);
538  } else if (el.op==OP_SETNONZEROS || el.op==OP_ADDNONZEROS) {
539  if (el.res.front()!=el.arg.at(0)) {
540  s << "@" << el.res.front() << " = @" << el.arg.at(0) << "; ";
541  }
542  std::vector<std::string> arg(2);
543  arg[0] = "@" + str(el.res.front());
544  arg[1] = "@" + str(el.arg.at(1));
545  s << el.data->disp(arg);
546  } else {
547  if (el.res.size()==1) {
548  s << "@" << el.res.front() << " = ";
549  } else {
550  s << "{";
551  for (casadi_int i=0; i<el.res.size(); ++i) {
552  if (i!=0) s << ", ";
553  if (el.res[i]>=0) {
554  s << "@" << el.res[i];
555  } else {
556  s << "NULL";
557  }
558  }
559  s << "} = ";
560  }
561  std::vector<std::string> arg;
562  if (el.op!=OP_INPUT) {
563  arg.resize(el.arg.size());
564  for (casadi_int i=0; i<el.arg.size(); ++i) {
565  if (el.arg[i]>=0) {
566  arg[i] = "@" + str(el.arg[i]);
567  } else {
568  arg[i] = "NULL";
569  }
570  }
571  }
572  s << el.data->disp(arg);
573  }
574  return s.str();
575  }
576 
577  void MXFunction::print_arg(std::ostream &stream, casadi_int k, const AlgEl& el,
578  const double** arg) const {
579  stream << name_ << ":" << k << ": " << print(el) << " inputs:" << std::endl;
580  for (size_t i = 0; i < el.arg.size(); ++i) {
581  if (arg[i]) {
582  stream << i << ": ";
583  if (print_canonical_) {
584  print_canonical(stream, el.data->dep(i).sparsity(), arg[i]);
585  } else {
586  DM::print_default(stream, el.data->dep(i).sparsity(), arg[i], true);
587  }
588  stream << std::endl;
589  }
590  }
591  }
592 
593  void MXFunction::print_arg(CodeGenerator& g, casadi_int k, const AlgEl& el,
594  const std::vector<casadi_int>& arg, const std::vector<bool>& arg_is_ref) const {
595  g << g.printf(name_ + ":" + str(k) + ": " + print(el) + " inputs:\\n") << "\n";
596  for (size_t i = 0; i < el.arg.size(); ++i) {
597  if (arg[i]>=0) {
598  g << g.printf(str(i) + ": ");
599  std::string a = g.work(arg[i], el.data->dep(i).nnz(), arg_is_ref[i]);
600  g << g.print_canonical(el.data->dep(i).sparsity(), a);
601  g << g.printf("\\n") << "\n";
602  }
603  }
604  }
605 
606  void MXFunction::print_res(CodeGenerator& g, casadi_int k, const AlgEl& el,
607  const std::vector<casadi_int>& res, const std::vector<bool>& res_is_ref) const {
608  g << g.printf(name_ + ":" + str(k) + ": " + print(el) + " outputs:\\n") << "\n";
609  for (size_t i = 0; i < el.res.size(); ++i) {
610  if (res[i]>=0) {
611  g << g.printf(str(i) + ": ");
612  std::string a = g.work(res[i], el.data->sparsity(i).nnz(), res_is_ref[i]);
613  g << g.print_canonical(el.data->sparsity(i), a);
614  g << g.printf("\\n") << "\n";
615  }
616  }
617  }
618 
619  void MXFunction::print_res(std::ostream &stream, casadi_int k, const AlgEl& el,
620  double** res) const {
621  stream << name_ << ":" << k << ": " << print(el) << " outputs:" << std::endl;
622  for (size_t i = 0; i < el.res.size(); ++i) {
623  if (res[i]) {
624  stream << i << ": ";
625  if (print_canonical_) {
626  print_canonical(stream, el.data->sparsity(i), res[i]);
627  } else {
628  DM::print_default(stream, el.data->sparsity(i), res[i], true);
629  }
630  stream << std::endl;
631  }
632  }
633  }
634 
635  void MXFunction::disp_more(std::ostream &stream) const {
636  stream << "Algorithm:";
637  for (auto&& e : algorithm_) {
639  stream << std::endl << print(e);
640  }
641  }
642 
644  sp_forward(const bvec_t** arg, bvec_t** res, casadi_int* iw, bvec_t* w, void* mem) const {
645  // Fall back when forward mode not allowed
646  if (sp_weight()==1 || sp_weight()==-1)
647  return FunctionInternal::sp_forward(arg, res, iw, w, mem);
648  // Temporaries to hold pointers to operation input and outputs
649  const bvec_t** arg1=arg+n_in_;
650  bvec_t** res1=res+n_out_;
651 
652  // Propagate sparsity forward
653  for (auto&& e : algorithm_) {
654  if (e.op==OP_INPUT) {
655  // Pass input seeds
656  casadi_int nnz=e.data.nnz();
657  casadi_int i=e.data->ind();
658  casadi_int nz_offset=e.data->offset();
659  const bvec_t* argi = arg[i];
660  bvec_t* w1 = w + workloc_[e.res.front()];
661  if (argi!=nullptr && is_diff_in_[i]) {
662  std::copy(argi+nz_offset, argi+nz_offset+nnz, w1);
663  } else {
664  std::fill_n(w1, nnz, 0);
665  }
666  } else if (e.op==OP_OUTPUT) {
667  // Get the output sensitivities
668  casadi_int nnz=e.data.dep().nnz();
669  casadi_int i=e.data->ind();
670  casadi_int nz_offset=e.data->offset();
671  bvec_t* resi = res[i];
672  bvec_t* w1 = w + workloc_[e.arg.front()];
673  if (resi!=nullptr && is_diff_out_[i]) {
674  std::copy(w1, w1+nnz, resi+nz_offset);
675  } else if (resi!=nullptr) {
676  std::fill_n(resi+nz_offset, nnz, 0);
677  }
678  } else {
679  // Point pointers to the data corresponding to the element
680  for (casadi_int i=0; i<e.arg.size(); ++i)
681  arg1[i] = e.arg[i]>=0 ? w+workloc_[e.arg[i]] : nullptr;
682  for (casadi_int i=0; i<e.res.size(); ++i)
683  res1[i] = e.res[i]>=0 ? w+workloc_[e.res[i]] : nullptr;
684 
685  // Propagate sparsity forwards
686  if (e.data->sp_forward(arg1, res1, iw, w)) return 1;
687  }
688  }
689  return 0;
690  }
691 
693  eval_activity(const bvec_t** arg, bvec_t** res, casadi_int* iw, bvec_t* w, void* mem) const {
694  // Temporaries to hold pointers to operation input and outputs
695  const bvec_t** arg1=arg+n_in_;
696  bvec_t** res1=res+n_out_;
697 
698  // Propagate signal activity forward (ignores is_diff: this is a value analysis)
699  for (auto&& e : algorithm_) {
700  if (e.op==OP_INPUT) {
701  casadi_int nnz=e.data.nnz();
702  casadi_int i=e.data->ind();
703  casadi_int nz_offset=e.data->offset();
704  const bvec_t* argi = arg[i];
705  bvec_t* w1 = w + workloc_[e.res.front()];
706  if (argi!=nullptr) {
707  std::copy(argi+nz_offset, argi+nz_offset+nnz, w1);
708  } else {
709  std::fill_n(w1, nnz, 0);
710  }
711  } else if (e.op==OP_OUTPUT) {
712  casadi_int nnz=e.data.dep().nnz();
713  casadi_int i=e.data->ind();
714  casadi_int nz_offset=e.data->offset();
715  bvec_t* resi = res[i];
716  bvec_t* w1 = w + workloc_[e.arg.front()];
717  if (resi!=nullptr) std::copy(w1, w1+nnz, resi+nz_offset);
718  } else {
719  for (casadi_int i=0; i<e.arg.size(); ++i)
720  arg1[i] = e.arg[i]>=0 ? w+workloc_[e.arg[i]] : nullptr;
721  for (casadi_int i=0; i<e.res.size(); ++i)
722  res1[i] = e.res[i]>=0 ? w+workloc_[e.res[i]] : nullptr;
723  if (e.data->eval_activity(arg1, res1, iw, w)) return 1;
724  }
725  }
726  return 0;
727  }
728 
729  std::vector<std::string> MXFunction::get_function() const {
730  std::map<std::string, bool> flagged;
731  for (auto it=algorithm_.begin(); it!=algorithm_.end(); it++) {
732  if (it->op==OP_CALL) {
733  const Function &f = it->data->which_function();
734  if (flagged.find(f.name())==flagged.end()) {
735  flagged[f.name()] = true;
736  }
737  }
738  }
739  std::vector<std::string> ret;
740  for (auto it : flagged) {
741  ret.push_back(it.first);
742  }
743  return ret;
744  }
745 
746  const Function& MXFunction::get_function(const std::string &name) const {
747  for (auto it=algorithm_.begin(); it!=algorithm_.end(); it++) {
748  if (it->op==OP_CALL) {
749  const Function &f = it->data->which_function();
750  if (name==f.name()) return f;
751  }
752  }
753  casadi_error("No such function '" + name + "'.");
754  }
755 
757  casadi_int* iw, bvec_t* w, void* mem) const {
758  // Fall back when reverse mode not allowed
759  if (sp_weight()==0 || sp_weight()==-1)
760  return FunctionInternal::sp_reverse(arg, res, iw, w, mem);
761  // Temporaries to hold pointers to operation input and outputs
762  bvec_t** arg1=arg+n_in_;
763  bvec_t** res1=res+n_out_;
764 
765  std::fill_n(w, sz_w(), 0);
766 
767  // Propagate sparsity backwards
768  for (auto it=algorithm_.rbegin(); it!=algorithm_.rend(); it++) {
769  if (it->op==OP_INPUT) {
770  // Get the input sensitivities and clear it from the work vector
771  casadi_int nnz=it->data.nnz();
772  casadi_int i=it->data->ind();
773  casadi_int nz_offset=it->data->offset();
774  bvec_t* argi = arg[i];
775  bvec_t* w1 = w + workloc_[it->res.front()];
776  if (argi!=nullptr && is_diff_in_[i])
777  for (casadi_int k=0; k<nnz; ++k) argi[nz_offset+k] |= w1[k];
778  std::fill_n(w1, nnz, 0);
779  } else if (it->op==OP_OUTPUT) {
780  // Pass output seeds
781  casadi_int nnz=it->data.dep().nnz();
782  casadi_int i=it->data->ind();
783  casadi_int nz_offset=it->data->offset();
784  bvec_t* resi = res[i] ? res[i] + nz_offset : nullptr;
785  bvec_t* w1 = w + workloc_[it->arg.front()];
786  if (resi!=nullptr && is_diff_out_[i]) {
787  for (casadi_int k=0; k<nnz; ++k) w1[k] |= resi[k];
788  std::fill_n(resi, nnz, 0);
789  }
790  } else {
791  // Point pointers to the data corresponding to the element
792  for (casadi_int i=0; i<it->arg.size(); ++i)
793  arg1[i] = it->arg[i]>=0 ? w+workloc_[it->arg[i]] : nullptr;
794  for (casadi_int i=0; i<it->res.size(); ++i)
795  res1[i] = it->res[i]>=0 ? w+workloc_[it->res[i]] : nullptr;
796 
797  // Propagate sparsity backwards
798  if (it->data->sp_reverse(arg1, res1, iw, w)) return 1;
799  }
800  }
801  return 0;
802  }
803 
804  std::vector<MX> MXFunction::symbolic_output(const std::vector<MX>& arg) const {
805  // Check if input is given
806  const casadi_int checking_depth = 2;
807  bool input_given = true;
808  for (casadi_int i=0; i<arg.size() && input_given; ++i) {
809  if (!is_equal(arg[i], in_[i], checking_depth)) {
810  input_given = false;
811  }
812  }
813 
814  // Return output if possible, else fall back to base class
815  if (input_given) {
816  return out_;
817  } else {
819  }
820  }
821 
822  void MXFunction::eval_mx(const MXVector& arg, MXVector& res,
823  bool always_inline, bool never_inline) const {
824  always_inline = always_inline || always_inline_;
825  never_inline = never_inline || never_inline_;
826  if (verbose_) casadi_message(name_ + "::eval_mx");
827  try {
828  // Resize the number of outputs
829  casadi_assert(arg.size()==n_in_, "Wrong number of input arguments");
830  res.resize(out_.size());
831 
832  // Trivial inline by default if output known
833  if (!never_inline && isInput(arg)) {
834  std::copy(out_.begin(), out_.end(), res.begin());
835  return;
836  }
837 
838  // non-inlining call is implemented in the base-class
839  if (!should_inline(false, always_inline, never_inline)) {
840  FunctionInternal::eval_mx(arg, res, false, true);
841  return;
842  }
843 
844  // Symbolic work, non-differentiated
845  std::vector<MX> swork(workloc_.size()-1);
846  if (verbose_) casadi_message("Allocated work vector");
847 
848  // Split up inputs analogous to symbolic primitives
849  std::vector<std::vector<MX> > arg_split(in_.size());
850  for (casadi_int i=0; i<in_.size(); ++i) arg_split[i] = in_[i].split_primitives(arg[i]);
851 
852  // Allocate storage for split outputs
853  std::vector<std::vector<MX> > res_split(out_.size());
854  for (casadi_int i=0; i<out_.size(); ++i) res_split[i].resize(out_[i].n_primitives());
855 
856  std::vector<MX> arg1, res1;
857 
858  // Loop over computational nodes in forward order
859  casadi_int alg_counter = 0;
860  for (auto it=algorithm_.begin(); it!=algorithm_.end(); ++it, ++alg_counter) {
861  if (it->op == OP_INPUT) {
862  swork[it->res.front()] = project(arg_split.at(it->data->ind()).at(it->data->segment()),
863  it->data.sparsity(), true);
864  } else if (it->op==OP_OUTPUT) {
865  // Collect the results
866  res_split.at(it->data->ind()).at(it->data->segment()) = swork[it->arg.front()];
867  } else if (it->op==OP_PARAMETER) {
868  // Fetch parameter
869  swork[it->res.front()] = it->data;
870  } else {
871  // Arguments of the operation
872  arg1.resize(it->arg.size());
873  for (casadi_int i=0; i<arg1.size(); ++i) {
874  casadi_int el = it->arg[i]; // index of the argument
875  arg1[i] = el<0 ? MX(it->data->dep(i).size()) : swork[el];
876  }
877 
878  // Perform the operation
879  res1.resize(it->res.size());
880  it->data->eval_mx(arg1, res1);
881 
882  // Get the result
883  for (casadi_int i=0; i<res1.size(); ++i) {
884  casadi_int el = it->res[i]; // index of the output
885  if (el>=0) swork[el] = res1[i];
886  }
887  }
888  }
889 
890  // Join split outputs
891  for (casadi_int i=0; i<res.size(); ++i) res[i] = out_[i].join_primitives(res_split[i]);
892  } catch (std::exception& e) {
893  CASADI_THROW_ERROR("eval_mx", e.what());
894  }
895  }
896 
897  void MXFunction::ad_forward(const std::vector<std::vector<MX> >& fseed,
898  std::vector<std::vector<MX> >& fsens) const {
899  if (verbose_) casadi_message(name_ + "::ad_forward(" + str(fseed.size())+ ")");
900  try {
901  // Allocate results
902  casadi_int nfwd = fseed.size();
903  fsens.resize(nfwd);
904  for (casadi_int d=0; d<nfwd; ++d) {
905  fsens[d].resize(n_out_);
906  }
907 
908  // Quick return if no directions
909  if (nfwd==0) return;
910 
911  // Check if seeds need to have dimensions corrected
912  casadi_int npar = 1;
913  for (auto&& r : fseed) {
914  if (!matching_arg(r, npar)) {
915  casadi_assert_dev(npar==1);
916  ad_forward(replace_fseed(fseed, npar), fsens);
917  return;
918  }
919  }
920 
921  // Check if there are any zero seeds
922  for (auto&& r : fseed) {
923  if (purgable(r)) {
924  // New argument without all-zero directions
925  std::vector<std::vector<MX> > fseed_purged, fsens_purged;
926  fseed_purged.reserve(nfwd);
927  std::vector<casadi_int> index_purged;
928  for (casadi_int d=0; d<nfwd; ++d) {
929  if (purgable(fseed[d])) {
930  for (casadi_int i=0; i<fsens[d].size(); ++i) {
931  fsens[d][i] = MX(size_out(i));
932  }
933  } else {
934  fseed_purged.push_back(fsens[d]);
935  index_purged.push_back(d);
936  }
937  }
938 
939  // Call recursively
940  ad_forward(fseed_purged, fsens_purged);
941 
942  // Fetch result
943  for (casadi_int d=0; d<fseed_purged.size(); ++d) {
944  fsens[index_purged[d]] = fsens_purged[d];
945  }
946  return;
947  }
948  }
949 
950  if (!enable_forward_) {
951  // Do the non-inlining call from FunctionInternal
952  // NOLINTNEXTLINE(bugprone-parent-virtual-call)
953  FunctionInternal::call_forward(in_, out_, fseed, fsens, false, false);
954  return;
955  }
956 
957  // Work vector, forward derivatives
958  std::vector<std::vector<MX> > dwork(workloc_.size()-1);
959  fill(dwork.begin(), dwork.end(), std::vector<MX>(nfwd));
960  if (verbose_) casadi_message("Allocated derivative work vector (forward mode)");
961 
962  // Split up fseed analogous to symbolic primitives
963  std::vector<std::vector<std::vector<MX>>> fseed_split(nfwd);
964  for (casadi_int d=0; d<nfwd; ++d) {
965  fseed_split[d].resize(fseed[d].size());
966  for (casadi_int i=0; i<fseed[d].size(); ++i) {
967  fseed_split[d][i] = in_[i].split_primitives(fseed[d][i]);
968  }
969  }
970 
971  // Allocate splited forward sensitivities
972  std::vector<std::vector<std::vector<MX>>> fsens_split(nfwd);
973  for (casadi_int d=0; d<nfwd; ++d) {
974  fsens_split[d].resize(out_.size());
975  for (casadi_int i=0; i<out_.size(); ++i) {
976  fsens_split[d][i].resize(out_[i].n_primitives());
977  }
978  }
979 
980  // Pointers to the arguments of the current operation
981  std::vector<std::vector<MX> > oseed, osens;
982  oseed.reserve(nfwd);
983  osens.reserve(nfwd);
984  std::vector<bool> skip(nfwd, false);
985 
986  // Loop over computational nodes in forward order
987  for (auto&& e : algorithm_) {
988  if (e.op == OP_INPUT) {
989  // Fetch forward seed
990  for (casadi_int d=0; d<nfwd; ++d) {
991  dwork[e.res.front()][d] =
992  project(fseed_split[d].at(e.data->ind()).at(e.data->segment()),
993  e.data.sparsity(), true);
994  }
995  } else if (e.op==OP_OUTPUT) {
996  // Collect forward sensitivity
997  for (casadi_int d=0; d<nfwd; ++d) {
998  fsens_split[d][e.data->ind()][e.data->segment()] = dwork[e.arg.front()][d];
999  }
1000  } else if (e.op==OP_PARAMETER) {
1001  // Fetch parameter
1002  for (casadi_int d=0; d<nfwd; ++d) {
1003  dwork[e.res.front()][d] = MX();
1004  }
1005  } else {
1006  // Get seeds, ignoring all-zero directions
1007  oseed.clear();
1008  for (casadi_int d=0; d<nfwd; ++d) {
1009  // Collect seeds, skipping directions with only zeros
1010  std::vector<MX> seed(e.arg.size());
1011  skip[d] = true; // All seeds are zero?
1012  for (casadi_int i=0; i<e.arg.size(); ++i) {
1013  casadi_int el = e.arg[i];
1014  if (el<0 || dwork[el][d].is_empty(true)) {
1015  seed[i] = MX(e.data->dep(i).size());
1016  } else {
1017  seed[i] = dwork[el][d];
1018  }
1019  if (skip[d] && !seed[i].is_zero()) skip[d] = false;
1020  }
1021  if (!skip[d]) oseed.push_back(seed);
1022  }
1023 
1024  // Perform the operation
1025  osens.resize(oseed.size());
1026  if (!osens.empty()) {
1027  fill(osens.begin(), osens.end(), std::vector<MX>(e.res.size()));
1028  e.data.ad_forward(oseed, osens);
1029  }
1030 
1031  // Store sensitivities
1032  casadi_int d1=0;
1033  for (casadi_int d=0; d<nfwd; ++d) {
1034  for (casadi_int i=0; i<e.res.size(); ++i) {
1035  casadi_int el = e.res[i];
1036  if (el>=0) {
1037  dwork[el][d] = skip[d] ? MX(e.data->sparsity(i).size()) : osens[d1][i];
1038  }
1039  }
1040  if (!skip[d]) d1++;
1041  }
1042  }
1043  }
1044 
1045  // Get forward sensitivities
1046  for (casadi_int d=0; d<nfwd; ++d) {
1047  for (casadi_int i=0; i<out_.size(); ++i) {
1048  fsens[d][i] = out_[i].join_primitives(fsens_split[d][i]);
1049  }
1050  }
1051  } catch (std::exception& e) {
1052  CASADI_THROW_ERROR("ad_forward", e.what());
1053  }
1054  }
1055 
1056  void MXFunction::ad_reverse(const std::vector<std::vector<MX> >& aseed,
1057  std::vector<std::vector<MX> >& asens) const {
1058  if (verbose_) casadi_message(name_ + "::ad_reverse(" + str(aseed.size())+ ")");
1059  try {
1060 
1061  // Allocate results
1062  casadi_int nadj = aseed.size();
1063  asens.resize(nadj);
1064  for (casadi_int d=0; d<nadj; ++d) {
1065  asens[d].resize(n_in_);
1066  }
1067 
1068  // Quick return if no directions
1069  if (nadj==0) return;
1070 
1071  // Check if seeds need to have dimensions corrected
1072  casadi_int npar = 1;
1073  for (auto&& r : aseed) {
1074  if (!matching_res(r, npar)) {
1075  casadi_assert_dev(npar==1);
1076  ad_reverse(replace_aseed(aseed, npar), asens);
1077  return;
1078  }
1079  }
1080 
1081  // Check if there are any zero seeds
1082  for (auto&& r : aseed) {
1083  // If any direction can be skipped
1084  if (purgable(r)) {
1085  // New argument without all-zero directions
1086  std::vector<std::vector<MX> > aseed_purged, asens_purged;
1087  aseed_purged.reserve(nadj);
1088  std::vector<casadi_int> index_purged;
1089  for (casadi_int d=0; d<nadj; ++d) {
1090  if (purgable(aseed[d])) {
1091  for (casadi_int i=0; i<asens[d].size(); ++i) {
1092  asens[d][i] = MX(size_in(i));
1093  }
1094  } else {
1095  aseed_purged.push_back(asens[d]);
1096  index_purged.push_back(d);
1097  }
1098  }
1099 
1100  // Call recursively
1101  ad_reverse(aseed_purged, asens_purged);
1102 
1103  // Fetch result
1104  for (casadi_int d=0; d<aseed_purged.size(); ++d) {
1105  asens[index_purged[d]] = asens_purged[d];
1106  }
1107  return;
1108  }
1109  }
1110 
1111  if (!enable_reverse_) {
1112  std::vector<std::vector<MX> > v;
1113  // Do the non-inlining call from FunctionInternal
1114  // NOLINTNEXTLINE(bugprone-parent-virtual-call)
1115  FunctionInternal::call_reverse(in_, out_, aseed, v, false, false);
1116  for (casadi_int i=0; i<v.size(); ++i) {
1117  for (casadi_int j=0; j<v[i].size(); ++j) {
1118  if (!v[i][j].is_empty()) { // TODO(@jaeandersson): Hack
1119  if (asens[i][j].is_empty()) {
1120  asens[i][j] = v[i][j];
1121  } else {
1122  asens[i][j] += v[i][j];
1123  }
1124  }
1125  }
1126  }
1127  return;
1128  }
1129 
1130  // Split up aseed analogous to symbolic primitives
1131  std::vector<std::vector<std::vector<MX>>> aseed_split(nadj);
1132  for (casadi_int d=0; d<nadj; ++d) {
1133  aseed_split[d].resize(out_.size());
1134  for (casadi_int i=0; i<out_.size(); ++i) {
1135  aseed_split[d][i] = out_[i].split_primitives(aseed[d][i]);
1136  }
1137  }
1138 
1139  // Allocate splited adjoint sensitivities
1140  std::vector<std::vector<std::vector<MX>>> asens_split(nadj);
1141  for (casadi_int d=0; d<nadj; ++d) {
1142  asens_split[d].resize(in_.size());
1143  for (casadi_int i=0; i<in_.size(); ++i) {
1144  asens_split[d][i].resize(in_[i].n_primitives());
1145  }
1146  }
1147 
1148  // Pointers to the arguments of the current operation
1149  std::vector<std::vector<MX>> oseed, osens;
1150  oseed.reserve(nadj);
1151  osens.reserve(nadj);
1152  std::vector<bool> skip(nadj, false);
1153 
1154  // Work vector, adjoint derivatives
1155  std::vector<std::vector<MX> > dwork(workloc_.size()-1);
1156  fill(dwork.begin(), dwork.end(), std::vector<MX>(nadj));
1157 
1158  // Loop over computational nodes in reverse order
1159  for (auto it=algorithm_.rbegin(); it!=algorithm_.rend(); ++it) {
1160  if (it->op == OP_INPUT) {
1161  // Get the adjoint sensitivities
1162  for (casadi_int d=0; d<nadj; ++d) {
1163  asens_split[d].at(it->data->ind()).at(it->data->segment()) = dwork[it->res.front()][d];
1164  dwork[it->res.front()][d] = MX();
1165  }
1166  } else if (it->op==OP_OUTPUT) {
1167  // Pass the adjoint seeds
1168  for (casadi_int d=0; d<nadj; ++d) {
1169  MX a = project(aseed_split[d].at(it->data->ind()).at(it->data->segment()),
1170  it->data.dep().sparsity(), true);
1171  if (dwork[it->arg.front()][d].is_empty(true)) {
1172  dwork[it->arg.front()][d] = a;
1173  } else {
1174  dwork[it->arg.front()][d] += a;
1175  }
1176  }
1177  } else if (it->op==OP_PARAMETER) {
1178  // Clear adjoint seeds
1179  for (casadi_int d=0; d<nadj; ++d) {
1180  dwork[it->res.front()][d] = MX();
1181  }
1182  } else {
1183  // Collect and reset seeds
1184  oseed.clear();
1185  for (casadi_int d=0; d<nadj; ++d) {
1186  // Can the direction be skipped completely?
1187  skip[d] = true;
1188 
1189  // Seeds for direction d
1190  std::vector<MX> seed(it->res.size());
1191  for (casadi_int i=0; i<it->res.size(); ++i) {
1192  // Get and clear seed
1193  casadi_int el = it->res[i];
1194  if (el>=0) {
1195  seed[i] = dwork[el][d];
1196  dwork[el][d] = MX();
1197  } else {
1198  seed[i] = MX();
1199  }
1200 
1201  // If first time encountered, reset to zero of right dimension
1202  if (seed[i].is_empty(true)) seed[i] = MX(it->data->sparsity(i).size());
1203 
1204  // If nonzero seeds, keep direction
1205  if (skip[d] && !seed[i].is_zero()) skip[d] = false;
1206  }
1207  // Add to list of derivatives
1208  if (!skip[d]) oseed.push_back(seed);
1209  }
1210 
1211  // Get values of sensitivities before addition
1212  osens.resize(oseed.size());
1213  casadi_int d1=0;
1214  for (casadi_int d=0; d<nadj; ++d) {
1215  if (skip[d]) continue;
1216  osens[d1].resize(it->arg.size());
1217  for (casadi_int i=0; i<it->arg.size(); ++i) {
1218  // Pass seed and reset to avoid counting twice
1219  casadi_int el = it->arg[i];
1220  if (el>=0) {
1221  osens[d1][i] = dwork[el][d];
1222  dwork[el][d] = MX();
1223  } else {
1224  osens[d1][i] = MX();
1225  }
1226 
1227  // If first time encountered, reset to zero of right dimension
1228  if (osens[d1][i].is_empty(true)) osens[d1][i] = MX(it->data->dep(i).size());
1229  }
1230  d1++;
1231  }
1232 
1233  // Perform the operation
1234  if (!osens.empty()) {
1235  it->data.ad_reverse(oseed, osens);
1236  }
1237 
1238  // Store sensitivities
1239  d1=0;
1240  for (casadi_int d=0; d<nadj; ++d) {
1241  if (skip[d]) continue;
1242  for (casadi_int i=0; i<it->arg.size(); ++i) {
1243  casadi_int el = it->arg[i];
1244  if (el>=0) {
1245  if (dwork[el][d].is_empty(true)) {
1246  dwork[el][d] = osens[d1][i];
1247  } else {
1248  dwork[el][d] += osens[d1][i];
1249  }
1250  }
1251  }
1252  d1++;
1253  }
1254  }
1255  }
1256 
1257  // Get adjoint sensitivities
1258  for (casadi_int d=0; d<nadj; ++d) {
1259  for (casadi_int i=0; i<in_.size(); ++i) {
1260  asens[d][i] = in_[i].join_primitives(asens_split[d][i]);
1261  }
1262  }
1263  } catch (std::exception& e) {
1264  CASADI_THROW_ERROR("ad_reverse", e.what());
1265  }
1266  }
1267 
1268  int MXFunction::eval_sx(const SXElem** arg, SXElem** res,
1269  casadi_int* iw, SXElem* w, void* mem,
1270  bool always_inline, bool never_inline) const {
1271  always_inline = always_inline || always_inline_;
1272  never_inline = never_inline || never_inline_;
1273 
1274  // non-inlining call is implemented in the base-class
1275  if (!should_inline(true, always_inline, never_inline)) {
1276  return FunctionInternal::eval_sx(arg, res, iw, w, mem, false, true);
1277  }
1278 
1279  // Work vector and temporaries to hold pointers to operation input and outputs
1280  std::vector<const SXElem*> argp(sz_arg());
1281  std::vector<SXElem*> resp(sz_res());
1282 
1283  // Evaluate all of the nodes of the algorithm:
1284  // should only evaluate nodes that have not yet been calculated!
1285  for (auto&& a : algorithm_) {
1286  if (a.op==OP_INPUT) {
1287  // Pass an input
1288  SXElem *w1 = w+workloc_[a.res.front()];
1289  casadi_int nnz=a.data.nnz();
1290  casadi_int i=a.data->ind();
1291  casadi_int nz_offset=a.data->offset();
1292  if (arg[i]==nullptr) {
1293  std::fill(w1, w1+nnz, 0);
1294  } else {
1295  std::copy(arg[i]+nz_offset, arg[i]+nz_offset+nnz, w1);
1296  }
1297  } else if (a.op==OP_OUTPUT) {
1298  // Get the outputs
1299  SXElem *w1 = w+workloc_[a.arg.front()];
1300  casadi_int nnz=a.data.dep().nnz();
1301  casadi_int i=a.data->ind();
1302  casadi_int nz_offset=a.data->offset();
1303  if (res[i]) std::copy(w1, w1+nnz, res[i]+nz_offset);
1304  } else if (a.op==OP_PARAMETER) {
1305  continue; // FIXME
1306  } else {
1307  // Point pointers to the data corresponding to the element
1308  for (casadi_int i=0; i<a.arg.size(); ++i)
1309  argp[i] = a.arg[i]>=0 ? w+workloc_[a.arg[i]] : nullptr;
1310  for (casadi_int i=0; i<a.res.size(); ++i)
1311  resp[i] = a.res[i]>=0 ? w+workloc_[a.res[i]] : nullptr;
1312 
1313  // Evaluate
1314  if (a.data->eval_sx(get_ptr(argp), get_ptr(resp), iw, w)) return 1;
1315  }
1316  }
1317  return 0;
1318  }
1319 
1321 
1322  // Make sure that there are no free variables
1323  if (!free_vars_.empty()) {
1324  casadi_error("Code generation of '" + name_ + "' is not possible since variables "
1325  + str(free_vars_) + " are free.");
1326  }
1327 
1328  // Generate code for the embedded functions
1329  for (auto&& a : algorithm_) {
1330  a.data->add_dependency(g);
1331  }
1332  }
1333 
1336  std::set<void*> added;
1337  for (auto&& a : algorithm_) {
1338  a.data->codegen_incref(g, added);
1339  }
1340  }
1341 
1344  std::set<void*> added;
1345  for (auto&& a : algorithm_) {
1346  a.data->codegen_decref(g, added);
1347  }
1348  }
1349 
1351  // Temporary variables and vectors
1352  g.init_local("arg1", "arg+" + str(n_in_));
1353  g.init_local("res1", "res+" + str(n_out_));
1354 
1355  g.reserve_work(workloc_.size()-1);
1356 
1357  // Operation number (for printing)
1358  casadi_int k=0;
1359 
1360  // Names of operation argument and results
1361  std::vector<casadi_int> arg, res;
1362 
1363  // State of work vector: reference or not (value types)
1364  std::vector<bool> work_is_ref(workloc_.size()-1, false);
1365 
1366  // State of operation arguments and results: reference or not
1367  std::vector<bool> arg_is_ref, res_is_ref;
1368 
1369  // Collect for each work vector element if reference or value needed
1370  std::vector<bool> needs_reference(workloc_.size()-1, false);
1371  std::vector<bool> needs_value(workloc_.size()-1, false);
1372 
1373  // Codegen the algorithm
1374  for (auto&& e : algorithm_) {
1375  // Generate comment
1376  if (g.verbose) {
1377  g << "/* #" << k << ": " << print(e) << " */\n";
1378  }
1379 
1380  // Get the names of the operation arguments
1381  arg.resize(e.arg.size());
1382  arg_is_ref.resize(e.arg.size());
1383  for (casadi_int i=0; i<e.arg.size(); ++i) {
1384  casadi_int j=e.arg.at(i);
1385  if (j>=0 && workloc_.at(j)!=workloc_.at(j+1)) {
1386  arg.at(i) = j;
1387  arg_is_ref.at(i) = work_is_ref.at(j);
1388  } else {
1389  arg.at(i) = -1;
1390  arg_is_ref.at(i) = false;
1391  }
1392  }
1393 
1394  // Get the names of the operation results
1395  res.resize(e.res.size());
1396  for (casadi_int i=0; i<e.res.size(); ++i) {
1397  casadi_int j=e.res.at(i);
1398  if (j>=0 && workloc_.at(j)!=workloc_.at(j+1)) {
1399  res.at(i) = j;
1400  } else {
1401  res.at(i) = -1;
1402  }
1403  }
1404 
1405  res_is_ref.resize(e.res.size());
1406  // By default, don't assume references
1407  std::fill(res_is_ref.begin(), res_is_ref.end(), false);
1408 
1409  if (print_instructions_ && e.op!=OP_INPUT && e.op!=OP_OUTPUT) {
1410  print_arg(g, k, e, arg, arg_is_ref);
1411  }
1412 
1413  // Generate operation
1414  e.data->generate(g, arg, res, arg_is_ref, res_is_ref);
1415 
1416  for (casadi_int i=0; i<e.res.size(); ++i) {
1417  casadi_int j=e.res.at(i);
1418  if (j>=0 && workloc_.at(j)!=workloc_.at(j+1)) {
1419  work_is_ref.at(j) = res_is_ref.at(i);
1420  if (res_is_ref.at(i)) {
1421  needs_reference[j] = true;
1422  } else {
1423  needs_value[j] = true;
1424  }
1425  }
1426  }
1427 
1428  if (print_instructions_ && e.op!=OP_INPUT && e.op!=OP_OUTPUT) {
1429  print_res(g, k, e, res, res_is_ref);
1430  }
1431 
1432  k++;
1433 
1434  }
1435 
1436  // Compute new work vector offsets, taking into account copy elision
1437  std::vector<casadi_int> cg_off(workloc_.size()-1, 0);
1438  casadi_int cg_wind = workloc_.front();
1439  for (casadi_int i=0; i<workloc_.size()-1; ++i) {
1440  casadi_int n=workloc_[i+1]-workloc_[i];
1441  cg_off[i] = cg_wind;
1442  if (n==0 || !needs_value[i]) continue;
1443  if (!g.codegen_scalars && n==1) continue;
1444  cg_wind += n;
1445  }
1446  g.set_codegen_sz_w(this, sz_w() - static_cast<size_t>(workloc_.back()-cg_wind));
1447 
1448  // Declare scalar work vector elements as local variables
1449  for (casadi_int i=0; i<workloc_.size()-1; ++i) {
1450  casadi_int n=workloc_[i+1]-workloc_[i];
1451  if (n==0) continue;
1452  /* Could use local variables for small work vector elements here, e.g.:
1453  ...
1454  } else if (n<10) {
1455  g << "w" << i << "[" << n << "]";
1456  } else {
1457  ...
1458  */
1459  if (!g.codegen_scalars && n==1) {
1460  g.local("w" + g.format_padded(i), "casadi_real");
1461  } else {
1462  if (needs_value[i]) {
1463  g.local("w" + g.format_padded(i), "casadi_real", "*");
1464  g.init_local("w" + g.format_padded(i), "w+" + str(cg_off[i]));
1465  }
1466  if (needs_reference[i]) {
1467  g.local("wr" + g.format_padded(i), "const casadi_real", "*");
1468  }
1469  }
1470  }
1471  }
1472 
1473  size_t MXFunction::codegen_sz_w(const CodeGenerator& g) const {
1474  size_t sz_w;
1475  if (g.get_codegen_sz_w(this, sz_w)) return sz_w;
1476  return this->sz_w();
1477  }
1478 
1479  void MXFunction::generate_lifted(Function& vdef_fcn, Function& vinit_fcn) const {
1480  std::vector<MX> swork(workloc_.size()-1);
1481 
1482  std::vector<MX> arg1, res1;
1483 
1484  // Get input primitives
1485  std::vector<std::vector<MX> > in_split(in_.size());
1486  for (casadi_int i=0; i<in_.size(); ++i) in_split[i] = in_[i].primitives();
1487 
1488  // Definition of intermediate variables
1489  std::vector<MX> y;
1490  std::vector<MX> g;
1491  std::vector<std::vector<MX> > f_G(out_.size());
1492  for (casadi_int i=0; i<out_.size(); ++i) f_G[i].resize(out_[i].n_primitives());
1493 
1494  // Initial guess for intermediate variables
1495  std::vector<MX> x_init;
1496 
1497  // Temporary std::stringstream
1498  std::stringstream ss;
1499 
1500  for (casadi_int algNo=0; algNo<2; ++algNo) {
1501  for (auto&& e : algorithm_) {
1502  switch (e.op) {
1503  case OP_LIFT:
1504  {
1505  MX& arg = swork[e.arg.at(0)];
1506  MX& arg_init = swork[e.arg.at(1)];
1507  MX& res = swork[e.res.front()];
1508  switch (algNo) {
1509  case 0:
1510  ss.str(std::string());
1511  ss << "y" << y.size();
1512  y.push_back(MX::sym(ss.str(), arg.sparsity()));
1513  g.push_back(arg);
1514  res = y.back();
1515  break;
1516  case 1:
1517  x_init.push_back(arg_init);
1518  res = arg_init;
1519  break;
1520  }
1521  break;
1522  }
1523  case OP_INPUT:
1524  swork[e.res.front()] = in_split.at(e.data->ind()).at(e.data->segment());
1525  break;
1526  case OP_PARAMETER:
1527  swork[e.res.front()] = e.data;
1528  break;
1529  case OP_OUTPUT:
1530  if (algNo==0) {
1531  f_G.at(e.data->ind()).at(e.data->segment()) = swork[e.arg.front()];
1532  }
1533  break;
1534  default:
1535  {
1536  // Arguments of the operation
1537  arg1.resize(e.arg.size());
1538  for (casadi_int i=0; i<arg1.size(); ++i) {
1539  casadi_int el = e.arg[i]; // index of the argument
1540  arg1[i] = el<0 ? MX(e.data->dep(i).size()) : swork[el];
1541  }
1542 
1543  // Perform the operation
1544  res1.resize(e.res.size());
1545  e.data->eval_mx(arg1, res1);
1546 
1547  // Get the result
1548  for (casadi_int i=0; i<res1.size(); ++i) {
1549  casadi_int el = e.res[i]; // index of the output
1550  if (el>=0) swork[el] = res1[i];
1551  }
1552  }
1553  }
1554  }
1555  }
1556 
1557  // Definition of intermediate variables
1558  std::vector<MX> f_in = in_;
1559  f_in.insert(f_in.end(), y.begin(), y.end());
1560  std::vector<MX> f_out;
1561  for (casadi_int i=0; i<out_.size(); ++i) f_out.push_back(out_[i].join_primitives(f_G[i]));
1562  f_out.insert(f_out.end(), g.begin(), g.end());
1563  vdef_fcn = Function("lifting_variable_definition", f_in, f_out);
1564 
1565  // Initial guess of intermediate variables
1566  f_in = in_;
1567  f_out = x_init;
1568  vinit_fcn = Function("lifting_variable_guess", f_in, f_out);
1569  }
1570 
1571  const MX MXFunction::mx_in(casadi_int ind) const {
1572  return in_.at(ind);
1573  }
1574 
1575  const std::vector<MX> MXFunction::mx_in() const {
1576  return in_;
1577  }
1578 
1579  bool MXFunction::is_a(const std::string& type, bool recursive) const {
1580  return type=="MXFunction"
1581  || (recursive && XFunction<MXFunction,
1582  MX, MXNode>::is_a(type, recursive));
1583  }
1584 
1585  void MXFunction::substitute_inplace(std::vector<MX>& vdef, std::vector<MX>& ex) const {
1586  std::vector<MX> work(workloc_.size()-1);
1587  std::vector<MX> oarg, ores;
1588 
1589  // Output segments
1590  std::vector<std::vector<MX>> out_split(out_.size());
1591  for (casadi_int i = 0; i < out_split.size(); ++i) out_split[i].resize(out_[i].n_primitives());
1592 
1593  // Evaluate algorithm
1594  for (auto it=algorithm_.begin(); it!=algorithm_.end(); ++it) {
1595  switch (it->op) {
1596  case OP_INPUT:
1597  casadi_assert(it->data->segment()==0, "Not implemented");
1598  work.at(it->res.front())
1599  = out_.at(it->data->ind()).join_primitives(out_split.at(it->data->ind()));
1600  break;
1601  case OP_PARAMETER:
1602  case OP_CONST:
1603  work.at(it->res.front()) = it->data;
1604  break;
1605  case OP_OUTPUT:
1606  out_split.at(it->data->ind()).at(it->data->segment()) = work.at(it->arg.front());
1607  break;
1608  default:
1609  {
1610  // Arguments of the operation
1611  oarg.resize(it->arg.size());
1612  for (casadi_int i=0; i<oarg.size(); ++i) {
1613  casadi_int el = it->arg[i];
1614  oarg[i] = el<0 ? MX(it->data->dep(i).size()) : work.at(el);
1615  }
1616 
1617  // Perform the operation
1618  ores.resize(it->res.size());
1619  it->data->eval_mx(oarg, ores);
1620 
1621  // Get the result
1622  for (casadi_int i=0; i<ores.size(); ++i) {
1623  casadi_int el = it->res[i];
1624  if (el>=0) work.at(el) = ores[i];
1625  }
1626  }
1627  }
1628  }
1629  // Join primitives
1630  for (size_t k = 0; k < out_split.size(); ++k) {
1631  MX a = out_.at(k).join_primitives(out_split.at(k));
1632  if (k < vdef.size()) {
1633  vdef.at(k) = a;
1634  } else {
1635  ex.at(k - vdef.size()) = a;
1636  }
1637  }
1638  }
1639 
1640  bool MXFunction::should_inline(bool with_sx, bool always_inline, bool never_inline) const {
1641  // If inlining has been specified
1642  casadi_assert(!(always_inline && never_inline),
1643  "Inconsistent options for " + definition());
1644  casadi_assert(!(never_inline && has_free()),
1645  "Must inline " + definition());
1646  if (always_inline) return true;
1647  if (never_inline) return false;
1648  // Functions with free variables must be inlined
1649  if (has_free()) return true;
1650  // Default inlining only when called with sx
1651  return with_sx;
1652  }
1653 
1654  void MXFunction::export_code_body(const std::string& lang,
1655  std::ostream &ss, const Dict& options) const {
1656 
1657  // Default values for options
1658  casadi_int indent_level = 0;
1659 
1660  // Read options
1661  for (auto&& op : options) {
1662  if (op.first=="indent_level") {
1663  indent_level = op.second;
1664  } else {
1665  casadi_error("Unknown option '" + op.first + "'.");
1666  }
1667  }
1668 
1669  // Construct indent string
1670  std::string indent;
1671  for (casadi_int i=0;i<indent_level;++i) {
1672  indent += " ";
1673  }
1674 
1675  Function f = shared_from_this<Function>();
1676 
1677  // Loop over algorithm
1678  for (casadi_int k=0;k<f.n_instructions();++k) {
1679  // Get operation
1680  casadi_int op = static_cast<casadi_int>(f.instruction_id(k));
1681  // Get MX node
1682  MX x = f.instruction_MX(k);
1683  // Get input positions into workvector
1684  std::vector<casadi_int> o = f.instruction_output(k);
1685  // Get output positions into workvector
1686  std::vector<casadi_int> i = f.instruction_input(k);
1687 
1688  switch (op) {
1689  case OP_INPUT:
1690  ss << indent << "w" << o[0] << " = varargin{" << i[0]+1 << "};" << std::endl;
1691  break;
1692  case OP_OUTPUT:
1693  {
1694  Dict info = x.info();
1695  casadi_int segment = info["segment"];
1696  x.dep(0).sparsity().export_code("matlab", ss,
1697  {{"name", "sp_in"}, {"indent_level", indent_level}, {"as_matrix", true}});
1698  ss << indent << "argout_" << o[0] << "{" << (1+segment) << "} = ";
1699  ss << "w" << i[0] << "(sp_in==1);" << std::endl;
1700  }
1701  break;
1702  case OP_CONST:
1703  {
1704  DM v = static_cast<DM>(x);
1705  Dict opts;
1706  opts["name"] = "m";
1707  opts["indent_level"] = indent_level;
1708  v.export_code("matlab", ss, opts);
1709  ss << indent << "w" << o[0] << " = m;" << std::endl;
1710  }
1711  break;
1712  case OP_SQ:
1713  ss << indent << "w" << o[0] << " = " << "w" << i[0] << ".^2;" << std::endl;
1714  break;
1715  case OP_MTIMES:
1716  ss << indent << "w" << o[0] << " = ";
1717  ss << "w" << i[1] << "*w" << i[2] << "+w" << i[0] << ";" << std::endl;
1718  break;
1719  case OP_MUL:
1720  {
1721  std::string prefix = (x.dep(0).is_scalar() || x.dep(1).is_scalar()) ? "" : ".";
1722  ss << indent << "w" << o[0] << " = " << "w" << i[0] << prefix << "*w" << i[1] << ";";
1723  ss << std::endl;
1724  }
1725  break;
1726  case OP_TWICE:
1727  ss << indent << "w" << o[0] << " = 2*w" << i[0] << ";" << std::endl;
1728  break;
1729  case OP_INV:
1730  ss << indent << "w" << o[0] << " = 1./w" << i[0] << ";" << std::endl;
1731  break;
1732  case OP_DOT:
1733  ss << indent << "w" << o[0] << " = dot(w" << i[0] << ",w" << i[1]<< ");" << std::endl;
1734  break;
1735  case OP_BILIN:
1736  ss << indent << "w" << o[0] << " = w" << i[1] << ".'*w" << i[0]<< "*w" << i[2] << ";";
1737  ss << std::endl;
1738  break;
1739  case OP_RANK1:
1740  ss << indent << "w" << o[0] << " = w" << i[0] << "+";
1741  ss << "w" << i[1] << "*w" << i[2] << "*w" << i[3] << ".';";
1742  ss << std::endl;
1743  break;
1744  case OP_FABS:
1745  ss << indent << "w" << o[0] << " = abs(w" << i[0] << ");" << std::endl;
1746  break;
1747  case OP_DETERMINANT:
1748  ss << indent << "w" << o[0] << " = det(w" << i[0] << ");" << std::endl;
1749  break;
1750  case OP_INVERSE:
1751  ss << indent << "w" << o[0] << " = inv(w" << i[0] << ");";
1752  ss << "w" << o[0] << "(w" << o[0] << "==0) = 1e-200;" << std::endl;
1753  break;
1754  case OP_SOLVE:
1755  {
1756  bool tr = x.info()["tr"];
1757  if (tr) {
1758  ss << indent << "w" << o[0] << " = ((w" << i[1] << ".')\\w" << i[0] << ").';";
1759  ss << std::endl;
1760  } else {
1761  ss << indent << "w" << o[0] << " = w" << i[1] << "\\w" << i[0] << ";" << std::endl;
1762  }
1763  ss << "w" << o[0] << "(w" << o[0] << "==0) = 1e-200;" << std::endl;
1764  }
1765  break;
1766  case OP_DIV:
1767  {
1768  std::string prefix = (x.dep(0).is_scalar() || x.dep(1).is_scalar()) ? "" : ".";
1769  ss << indent << "w" << o[0] << " = " << "w" << i[0] << prefix << "/w" << i[1] << ";";
1770  ss << std::endl;
1771  }
1772  break;
1773  case OP_POW:
1774  case OP_CONSTPOW:
1775  ss << indent << "w" << o[0] << " = " << "w" << i[0] << ".^w" << i[1] << ";" << std::endl;
1776  break;
1777  case OP_TRANSPOSE:
1778  ss << indent << "w" << o[0] << " = " << "w" << i[0] << ".';" << std::endl;
1779  break;
1780  case OP_HORZCAT:
1781  case OP_VERTCAT:
1782  {
1783  ss << indent << "w" << o[0] << " = [";
1784  for (casadi_int e : i) {
1785  ss << "w" << e << (op==OP_HORZCAT ? " " : ";");
1786  }
1787  ss << "];" << std::endl;
1788  }
1789  break;
1790  case OP_DIAGCAT:
1791  {
1792  for (casadi_int k=0;k<i.size();++k) {
1793  x.dep(k).sparsity().export_code("matlab", ss,
1794  {{"name", "sp_in" + str(k)}, {"indent_level", indent_level}, {"as_matrix", true}});
1795  }
1796  ss << indent << "w" << o[0] << " = [";
1797  for (casadi_int k=0;k<i.size();++k) {
1798  ss << "w" << i[k] << "(sp_in" << k << "==1);";
1799  }
1800  ss << "];" << std::endl;
1801  Dict opts;
1802  opts["name"] = "sp";
1803  opts["indent_level"] = indent_level;
1804  opts["as_matrix"] = false;
1805  x.sparsity().export_code("matlab", ss, opts);
1806  ss << indent << "w" << o[0] << " = ";
1807  ss << "sparse(sp_i, sp_j, w" << o[0] << ", sp_m, sp_n);" << std::endl;
1808  }
1809  break;
1810  case OP_HORZSPLIT:
1811  case OP_VERTSPLIT:
1812  {
1813  Dict info = x.info();
1814  std::vector<casadi_int> offset = info["offset"];
1815  casadi::Function output = info["output"];
1816  std::vector<Sparsity> sp;
1817  for (casadi_int i=0;i<output.n_out();i++)
1818  sp.push_back(output.sparsity_out(i));
1819  for (casadi_int k=0;k<o.size();++k) {
1820  if (o[k]==-1) continue;
1821  x.dep(0).sparsity().export_code("matlab", ss,
1822  {{"name", "sp_in"}, {"indent_level", indent_level}, {"as_matrix", true}});
1823  ss << indent << "tmp = w" << i[0]<< "(sp_in==1);" << std::endl;
1824  Dict opts;
1825  opts["name"] = "sp";
1826  opts["indent_level"] = indent_level;
1827  opts["as_matrix"] = false;
1828  sp[k].export_code("matlab", ss, opts);
1829  ss << indent << "w" << o[k] << " = sparse(sp_i, sp_j, ";
1830  ss << "tmp(" << offset[k]+1 << ":" << offset[k+1] << "), sp_m, sp_n);" << std::endl;
1831  }
1832  }
1833  break;
1834  case OP_GETNONZEROS:
1835  case OP_SETNONZEROS:
1836  {
1837  Dict info = x.info();
1838 
1839  std::string nonzeros;
1840  if (info.find("nz")!=info.end()) {
1841  nonzeros = "1+" + str(info["nz"]);
1842  } else if (info.find("slice")!=info.end()) {
1843  Dict s = info["slice"];
1844  casadi_int start = s["start"];
1845  casadi_int step = s["step"];
1846  casadi_int stop = s["stop"];
1847  nonzeros = str(start+1) + ":" + str(step) + ":" + str(stop);
1848  nonzeros = "nonzeros(" + nonzeros + ")";
1849  } else {
1850  Dict inner = info["inner"];
1851  Dict outer = info["outer"];
1852  casadi_int inner_start = inner["start"];
1853  casadi_int inner_step = inner["step"];
1854  casadi_int inner_stop = inner["stop"];
1855  casadi_int outer_start = outer["start"];
1856  casadi_int outer_step = outer["step"];
1857  casadi_int outer_stop = outer["stop"];
1858  std::string inner_slice = "(" + str(inner_start) + ":" +
1859  str(inner_step) + ":" + str(inner_stop-1)+")";
1860  std::string outer_slice = "(" + str(outer_start+1) + ":" +
1861  str(outer_step) + ":" + str(outer_stop)+")";
1862  casadi_int N = range(outer_start, outer_stop, outer_step).size();
1863  casadi_int M = range(inner_start, inner_stop, inner_step).size();
1864  nonzeros = "repmat("+ inner_slice +"', 1, " + str(N) + ")+" +
1865  "repmat("+ outer_slice +", " + str(M) + ", 1)";
1866  nonzeros = "nonzeros(" + nonzeros + ")";
1867  }
1868 
1869  Dict opts;
1870  opts["name"] = "sp";
1871  opts["indent_level"] = indent_level;
1872  opts["as_matrix"] = false;
1873  x.sparsity().export_code("matlab", ss, opts);
1874 
1875  if (op==OP_GETNONZEROS) {
1876  x.dep(0).sparsity().export_code("matlab", ss,
1877  {{"name", "sp_in"}, {"indent_level", indent_level}, {"as_matrix", true}});
1878  //ss << indent << "w" << i[0] << "" << std::endl;
1879  //ss << indent << "size(w" << i[0] << ")" << std::endl;
1880  ss << indent << "in_flat = w" << i[0] << "(sp_in==1);" << std::endl;
1881  //ss << indent << "in_flat" << std::endl;
1882  //ss << indent << "size(in_flat)" << std::endl;
1883  ss << indent << "w" << o[0] << " = in_flat(" << nonzeros << ");" << std::endl;
1884  } else {
1885  x.dep(0).sparsity().export_code("matlab", ss,
1886  {{"name", "sp_in0"}, {"indent_level", indent_level}, {"as_matrix", true}});
1887  x.dep(1).sparsity().export_code("matlab", ss,
1888  {{"name", "sp_in1"}, {"indent_level", indent_level}, {"as_matrix", true}});
1889  ss << indent << "in_flat = w" << i[1] << "(sp_in1==1);" << std::endl;
1890  ss << indent << "w" << o[0] << " = w" << i[0] << "(sp_in0==1);" << std::endl;
1891  ss << indent << "w" << o[0] << "(" << nonzeros << ") = ";
1892  if (info["add"]) ss << "w" << o[0] << "(" << nonzeros << ") + ";
1893  ss << "in_flat;";
1894  }
1895  ss << indent << "w" << o[0] << " = ";
1896  ss << "sparse(sp_i, sp_j, w" << o[0] << ", sp_m, sp_n);" << std::endl;
1897  }
1898  break;
1899  case OP_PROJECT:
1900  {
1901  Dict opts;
1902  opts["name"] = "sp";
1903  opts["indent_level"] = indent_level;
1904  x.sparsity().export_code("matlab", ss, opts);
1905  ss << indent << "w" << o[0] << " = ";
1906  ss << "sparse(sp_i, sp_j, w" << i[0] << "(sp==1), sp_m, sp_n);" << std::endl;
1907  }
1908  break;
1909  case OP_NORM1:
1910  ss << indent << "w" << o[0] << " = norm(w" << i[0] << ", 1);" << std::endl;
1911  break;
1912  case OP_NORM2:
1913  ss << indent << "w" << o[0] << " = norm(w" << i[0] << ", 2);" << std::endl;
1914  break;
1915  case OP_NORMF:
1916  ss << indent << "w" << o[0] << " = norm(w" << i[0] << ", 'fro');" << std::endl;
1917  break;
1918  case OP_NORMINF:
1919  ss << indent << "w" << o[0] << " = norm(w" << i[0] << ", inf);" << std::endl;
1920  break;
1921  case OP_MMIN:
1922  ss << indent << "w" << o[0] << " = min(w" << i[0] << ");" << std::endl;
1923  break;
1924  case OP_MMAX:
1925  ss << indent << "w" << o[0] << " = max(w" << i[0] << ");" << std::endl;
1926  break;
1927  case OP_NOT:
1928  ss << indent << "w" << o[0] << " = ~" << "w" << i[0] << ";" << std::endl;
1929  break;
1930  case OP_OR:
1931  ss << indent << "w" << o[0] << " = w" << i[0] << " | w" << i[1] << ";" << std::endl;
1932  break;
1933  case OP_AND:
1934  ss << indent << "w" << o[0] << " = w" << i[0] << " & w" << i[1] << ";" << std::endl;
1935  break;
1936  case OP_NE:
1937  ss << indent << "w" << o[0] << " = w" << i[0] << " ~= w" << i[1] << ";" << std::endl;
1938  break;
1939  case OP_IF_ELSE_ZERO:
1940  ss << indent << "w" << o[0] << " = ";
1941  ss << "if_else_zero_gen(w" << i[0] << ", w" << i[1] << ");" << std::endl;
1942  break;
1943  case OP_RESHAPE:
1944  {
1945  x.dep(0).sparsity().export_code("matlab", ss,
1946  {{"name", "sp_in"}, {"indent_level", indent_level}, {"as_matrix", true}});
1947  x.sparsity().export_code("matlab", ss,
1948  {{"name", "sp_out"}, {"indent_level", indent_level}, {"as_matrix", false}});
1949  ss << indent << "w" << o[0] << " = sparse(sp_out_i, sp_out_j, ";
1950  ss << "w" << i[0] << "(sp_in==1), sp_out_m, sp_out_n);" << std::endl;
1951  }
1952  break;
1953  default:
1954  if (x.is_binary()) {
1955  ss << indent << "w" << o[0] << " = " << casadi::casadi_math<double>::print(op,
1956  "w"+std::to_string(i[0]), "w"+std::to_string(i[1])) << ";" << std::endl;
1957  } else if (x.is_unary()) {
1958  ss << indent << "w" << o[0] << " = " << casadi::casadi_math<double>::print(op,
1959  "w"+std::to_string(i[0])) << ";" << std::endl;
1960  } else {
1961  ss << "unknown" + x.class_name() << std::endl;
1962  }
1963  }
1964  }
1965  }
1966 
1967  Dict MXFunction::get_stats(void* mem) const {
1968  Dict stats = XFunction::get_stats(mem);
1969 
1970  Function dep;
1971  for (auto&& e : algorithm_) {
1972  if (e.op==OP_CALL) {
1973  Function d = e.data.which_function();
1974  if (d.is_a("Conic", true) || d.is_a("Nlpsol")) {
1975  if (!dep.is_null()) return stats;
1976  dep = d;
1977  }
1978  }
1979  }
1980  if (dep.is_null()) return stats;
1981  return dep.stats(1);
1982  }
1983 
1986 
1987  s.version("MXFunction", 3);
1988  s.pack("MXFunction::n_instr", algorithm_.size());
1989 
1990  // Loop over algorithm
1991  for (const auto& e : algorithm_) {
1992  s.pack("MXFunction::alg::data", e.data);
1993  s.pack("MXFunction::alg::arg", e.arg);
1994  s.pack("MXFunction::alg::res", e.res);
1995  }
1996 
1997  s.pack("MXFunction::workloc", workloc_);
1998  s.pack("MXFunction::free_vars", free_vars_);
1999  s.pack("MXFunction::default_in", default_in_);
2000  s.pack("MXFunction::live_variables", live_variables_);
2001  s.pack("MXFunction::print_instructions", print_instructions_);
2002  s.pack("MXFunction::dump_trace", dump_trace_);
2003 
2005  }
2006 
2007 
2009  int version = s.version("MXFunction", 1, 3);
2010  size_t n_instructions;
2011  s.unpack("MXFunction::n_instr", n_instructions);
2012  algorithm_.resize(n_instructions);
2013  for (casadi_int k=0;k<n_instructions;++k) {
2014  AlgEl& e = algorithm_[k];
2015  s.unpack("MXFunction::alg::data", e.data);
2016  e.op = e.data.op();
2017  s.unpack("MXFunction::alg::arg", e.arg);
2018  s.unpack("MXFunction::alg::res", e.res);
2019  }
2020 
2021  s.unpack("MXFunction::workloc", workloc_);
2022  s.unpack("MXFunction::free_vars", free_vars_);
2023  s.unpack("MXFunction::default_in", default_in_);
2024  s.unpack("MXFunction::live_variables", live_variables_);
2025  print_instructions_ = false;
2026  if (version >= 2) s.unpack("MXFunction::print_instructions", print_instructions_);
2027 
2028  if (version >= 3) s.unpack("MXFunction::dump_trace", dump_trace_);
2029 
2031  }
2032 
2034  return new MXFunction(s);
2035  }
2036 
2037  void MXFunction::find(std::map<FunctionInternal*, std::pair<Function, size_t> >& all_fun,
2038  casadi_int max_depth) const {
2039  // Call to base class
2040  FunctionInternal::find(all_fun, max_depth);
2041  for (auto&& e : algorithm_) {
2042  if (e.op == OP_CALL) add_embedded(all_fun, e.data.which_function(), max_depth);
2043  }
2044  }
2045 
2046  void MXFunction::change_option(const std::string& option_name,
2047  const GenericType& option_value) {
2048  if (option_name == "print_instructions") {
2049  print_instructions_ = option_value;
2050  } else if (option_name == "dump_trace") {
2051  bool value = option_value;
2052  casadi_assert(!value || !jit_, "dump_trace is not supported for JIT evaluation");
2053  dump_trace_ = value;
2054  } else {
2055  // Option not found - continue to base classes
2056  XFunction<MXFunction, MX, MXNode>::change_option(option_name, option_value);
2057  }
2058  }
2059 
2060  std::vector<MX> MXFunction::order(const std::vector<MX>& expr) {
2061 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
2062  std::lock_guard<std::mutex> lock(MX::get_mutex_temp());
2063 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
2064  // Stack used to sort the computational graph
2065  std::stack<MXNode*> s;
2066 
2067  // All nodes
2068  std::vector<MXNode*> nodes;
2069 
2070  // Add the list of nodes
2071  for (casadi_int ind=0; ind<expr.size(); ++ind) {
2072  // Loop over primitives of each output
2073  std::vector<MX> prim = expr[ind].primitives();
2074  for (casadi_int p=0; p<prim.size(); ++p) {
2075  // Get the nodes using a depth first search
2076  s.push(prim[p].get());
2078  }
2079  }
2080 
2081  // Clear temporary markers
2082  for (casadi_int i=0; i<nodes.size(); ++i) {
2083  nodes[i]->temp = 0;
2084  }
2085 
2086  std::vector<MX> ret(nodes.size());
2087  for (casadi_int i=0; i<nodes.size(); ++i) {
2088  ret[i].own(nodes[i]);
2089  }
2090 
2091  return ret;
2092  }
2093 
2094 } // namespace casadi
Helper class for C code generation.
bool codegen_scalars
Codegen scalar.
std::string work(casadi_int n, casadi_int sz, bool is_ref) const
void reserve_work(casadi_int n)
Reserve a maximum size of work elements, used for padding of index.
std::string printf(const std::string &str, const std::vector< std::string > &arg=std::vector< std::string >())
Printf.
void local(const std::string &name, const std::string &type, const std::string &ref="")
Declare a local variable.
void init_local(const std::string &name, const std::string &def)
Specify the default value for a local variable.
bool get_codegen_sz_w(const FunctionInternal *f, size_t &sz_w) const
Retrieve a work vector size registered by set_codegen_sz_w.
void set_codegen_sz_w(const FunctionInternal *f, size_t sz_w)
Register the work vector size a function needs in generated code.
std::string print_canonical(const Sparsity &sp, const std::string &arg)
Print canonical representaion of a matrix.
std::string format_padded(casadi_int i) const
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.
bool has_refcount_
Reference counting in codegen?
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.
Dict get_stats(void *mem) const override
Get all statistics.
virtual void call_forward(const std::vector< MX > &arg, const std::vector< MX > &res, const std::vector< std::vector< MX > > &fseed, std::vector< std::vector< MX > > &fsens, bool always_inline, bool never_inline) const
Forward mode AD, virtual functions overloaded in derived classes.
virtual void codegen_decref(CodeGenerator &g) const
Codegen decref for dependencies.
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::pair< casadi_int, casadi_int > size_in(casadi_int ind) const
Input/output dimensions.
std::string definition() const
Get function signature: name:(inputs)->(outputs)
virtual void call_reverse(const std::vector< MX > &arg, const std::vector< MX > &res, const std::vector< std::vector< MX > > &aseed, std::vector< std::vector< MX > > &asens, bool always_inline, bool never_inline) const
Reverse mode, virtual functions overloaded in derived classes.
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.
size_t sz_res() const
Get required length of res field.
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.
std::pair< casadi_int, casadi_int > size_out(casadi_int ind) const
Input/output dimensions.
virtual int sp_forward(const bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const
Propagate sparsity forward.
static const Options options_
Options.
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.
size_t sz_arg() const
Get required length of arg field.
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)
virtual std::vector< MX > symbolic_output(const std::vector< MX > &arg) const
Get a vector of symbolic variables corresponding to the outputs.
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.
static bool purgable(const std::vector< MatType > &seed)
Can a derivative direction be skipped.
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.
virtual void codegen_incref(CodeGenerator &g) const
Codegen incref for dependencies.
Function object.
Definition: function.hpp:60
casadi_int n_instructions() const
Number of instruction in the algorithm (SXFunction/MXFunction)
Definition: function.cpp:1906
const std::string & name() const
Name of the function.
Definition: function.cpp:1504
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
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
MX instruction_MX(casadi_int k) const
Get the MX node corresponding to an instruction (MXFunction)
Definition: function.cpp:1914
bool is_a(const std::string &type, bool recursive=true) const
Check if the function is of a particular type.
Definition: function.cpp:1861
Dict stats(int mem=0) const
Get all statistics obtained at the end of the last evaluate call.
Definition: function.cpp:1080
casadi_int instruction_id(casadi_int k) const
Identifier index of the instruction (SXFunction/MXFunction)
Definition: function.cpp:1930
std::pair< casadi_int, casadi_int > size() const
Get the shape.
casadi_int nnz() const
Get the number of (structural) non-zero elements.
static MX sym(const std::string &name, casadi_int nrow=1, casadi_int ncol=1)
Create an nrow-by-ncol symbolic primitive.
bool is_scalar(bool scalar_and_dense=false) const
Check if the matrix expression is scalar.
bool is_null() const
Is a null pointer?
void own(Internal *node)
Generic data type, can hold different types such as bool, casadi_int, std::string etc.
An input or output instruction.
Input instruction.
static void check()
Raises an error if an interrupt was captured.
Internal node class for MXFunction.
Definition: mx_function.hpp:67
void codegen_body(CodeGenerator &g) const override
Generate code for the body of the C function.
static const Options options_
Options.
std::vector< casadi_int > workloc_
Offsets for elements in the w_ vector.
Definition: mx_function.hpp:82
bool live_variables_
Live variables?
Definition: mx_function.hpp:94
MX instruction_MX(casadi_int k) const override
get MX expression associated with instruction
Definition: mx_function.cpp:91
std::vector< casadi_int > instruction_output(casadi_int k) const override
Get the (integer) output argument of an atomic operation.
std::vector< double > default_in_
Default input values.
Definition: mx_function.hpp:91
void change_option(const std::string &option_name, const GenericType &option_value) override
Change option after object creation for debugging.
std::vector< std::string > get_function() const override
Get list of dependency functions.
int sp_reverse(bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const override
Propagate sparsity backwards.
const std::vector< MX > mx_in() const override
Get function input(s) and output(s)
void trace_instruction(std::ostream &trace, casadi_int k, const double *w, bool output) const
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
Work vector size of the generated code.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
void init(const Dict &opts) override
Initialize.
int eval_activity(const bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, void *mem) const override
Propagate signal activity forward.
void print_arg(std::ostream &stream, casadi_int k, const AlgEl &el, const double **arg) const
void ad_reverse(const std::vector< std::vector< MX > > &adjSeed, std::vector< std::vector< MX > > &adjSens) const
Calculate reverse mode directional derivatives.
MXFunction(const std::string &name, const std::vector< MX > &input, const std::vector< MX > &output, const std::vector< std::string > &name_in, const std::vector< std::string > &name_out)
Constructor.
Definition: mx_function.cpp:43
void codegen_decref(CodeGenerator &g) const override
Codegen decref for dependencies.
std::vector< std::string > get_free() const override
Print free variables.
~MXFunction() override
Destructor.
Definition: mx_function.cpp:51
std::string print(const AlgEl &el) const
bool has_free() const override
Does the function have free variables.
int eval(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Evaluate numerically, work vectors given.
void disp_more(std::ostream &stream) const override
Print description.
void codegen_declarations(CodeGenerator &g) const override
Generate code for the declarations of the C function.
bool print_instructions_
Print instructions during evaluation.
Definition: mx_function.hpp:97
void substitute_inplace(std::vector< MX > &vdef, std::vector< MX > &ex) const
Substitute inplace, internal implementation.
std::vector< casadi_int > instruction_input(casadi_int k) const override
Get the (integer) input arguments of an atomic operation.
Definition: mx_function.cpp:95
void find(std::map< FunctionInternal *, std::pair< Function, size_t > > &all_fun, casadi_int max_depth) const override
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, SX type.
casadi_int n_instructions() const override
Get the number of atomic operations.
void codegen_incref(CodeGenerator &g) const override
Codegen incref for dependencies.
bool should_inline(bool with_sx, bool always_inline, bool never_inline) const override
std::vector< AlgEl > algorithm_
All the runtime elements in the order of evaluation.
Definition: mx_function.hpp:77
void eval_mx(const MXVector &arg, MXVector &res, bool always_inline, bool never_inline) const override
Evaluate symbolically, MX type.
static std::vector< MX > order(const std::vector< MX > &expr)
bool is_a(const std::string &type, bool recursive) const override
Check if the function is of a particular type.
void print_res(std::ostream &stream, casadi_int k, const AlgEl &el, double **res) const
void ad_forward(const std::vector< std::vector< MX > > &fwdSeed, std::vector< std::vector< MX > > &fwdSens) const
Calculate forward mode directional derivatives.
std::vector< MX > free_vars_
Free variables.
Definition: mx_function.hpp:88
Dict generate_options(const std::string &target="clone") const override
Reconstruct options dict.
Definition: mx_function.cpp:82
void generate_lifted(Function &vdef_fcn, Function &vinit_fcn) const override
Extract the residual function G and the modified function Z out of an expression.
std::vector< MX > symbolic_output(const std::vector< MX > &arg) const override
Get a vector of symbolic variables corresponding to the outputs.
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize with type disambiguation.
Dict get_stats(void *mem) const override
Get all statistics.
void export_code_body(const std::string &lang, std::ostream &stream, const Dict &options) const override
Export function in a specific language.
Node class for MX objects.
Definition: mx_node.hpp:51
virtual casadi_int ind() const
Definition: mx_node.cpp:212
const Sparsity & sparsity() const
Get the sparsity.
Definition: mx_node.hpp:410
const MX & dep(casadi_int ind=0) const
dependencies - functions that have to be evaluated before this one
Definition: mx_node.hpp:392
virtual casadi_int segment() const
Definition: mx_node.cpp:216
virtual std::string disp(const std::vector< std::string > &arg) const =0
Print expression.
MX - Matrix expression.
Definition: mx.hpp:92
static MX create(MXNode *node)
Create from node.
Definition: mx.cpp:69
const Sparsity & sparsity() const
Get the sparsity pattern.
Definition: mx.cpp:612
Dict info() const
Definition: mx.cpp:855
MX dep(casadi_int ch=0) const
Get the nth dependency as MX.
Definition: mx.cpp:783
bool is_binary() const
Is binary operation.
Definition: mx.cpp:843
bool is_unary() const
Is unary operation.
Definition: mx.cpp:847
casadi_int op() const
Get operation type.
Definition: mx.cpp:851
static void print_default(std::ostream &stream, const Sparsity &sp, const double *nonzeros, bool truncate=true)
Print default style.
void export_code(const std::string &lang, std::ostream &stream=casadi::uout(), const Dict &options=Dict()) const
Export matrix in specific language.
Input instruction.
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
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
std::string class_name() const
Get class name.
casadi_int nnz() const
Get the number of (structural) non-zeros.
Definition: sparsity.cpp:148
void export_code(const std::string &lang, std::ostream &stream=casadi::uout(), const Dict &options=Dict()) const
Export matrix in specific language.
Definition: sparsity.cpp:789
Internal node class for the base class of SXFunction and MXFunction.
Definition: x_function.hpp:57
std::vector< MX > 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
virtual bool isInput(const std::vector< MX > &arg) const
Helper function: Check if a vector equals ex_in.
std::vector< MX > 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< MXNode * > &s, std::vector< MXNode * > &nodes)
Topological sorting of the nodes based on Depth-First Search (DFS)
Definition: x_function.hpp:412
The casadi namespace.
Definition: archiver.cpp:28
bool is_equal(double x, double y, casadi_int depth=0)
Definition: calculus.hpp:287
std::vector< casadi_int > range(casadi_int start, casadi_int stop, casadi_int step, casadi_int len)
Range function.
std::string join(const std::vector< std::string > &l, const std::string &delim)
unsigned long long bvec_t
std::vector< MX > MXVector
Definition: mx.hpp:1107
@ OT_DOUBLEVECTOR
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.
bool is_zero(const T &x)
std::ostream & uout()
@ OP_DIAGCAT
Definition: calculus.hpp:130
@ OP_NE
Definition: calculus.hpp:70
@ OP_HORZCAT
Definition: calculus.hpp:124
@ OP_VERTCAT
Definition: calculus.hpp:127
@ OP_IF_ELSE_ZERO
Definition: calculus.hpp:71
@ OP_MMAX
Definition: calculus.hpp:181
@ OP_AND
Definition: calculus.hpp:70
@ OP_INV
Definition: calculus.hpp:73
@ OP_INVERSE
Definition: calculus.hpp:112
@ OP_OUTPUT
Definition: calculus.hpp:82
@ OP_MMIN
Definition: calculus.hpp:181
@ OP_SETNONZEROS
Definition: calculus.hpp:163
@ OP_VERTSPLIT
Definition: calculus.hpp:136
@ OP_CONST
Definition: calculus.hpp:79
@ OP_OR
Definition: calculus.hpp:70
@ OP_TWICE
Definition: calculus.hpp:67
@ OP_INPUT
Definition: calculus.hpp:82
@ OP_LIFT
Definition: calculus.hpp:191
@ OP_DETERMINANT
Definition: calculus.hpp:109
@ OP_DOT
Definition: calculus.hpp:115
@ OP_POW
Definition: calculus.hpp:66
@ OP_PROJECT
Definition: calculus.hpp:169
@ OP_ADDNONZEROS
Definition: calculus.hpp:157
@ OP_PARAMETER
Definition: calculus.hpp:85
@ OP_FABS
Definition: calculus.hpp:71
@ OP_BILIN
Definition: calculus.hpp:118
@ OP_MTIMES
Definition: calculus.hpp:100
@ OP_NORM1
Definition: calculus.hpp:178
@ OP_CALL
Definition: calculus.hpp:88
@ OP_NORM2
Definition: calculus.hpp:178
@ OP_RESHAPE
Definition: calculus.hpp:142
@ OP_DIV
Definition: calculus.hpp:65
@ OP_TRANSPOSE
Definition: calculus.hpp:106
@ OP_SOLVE
Definition: calculus.hpp:103
@ OP_RANK1
Definition: calculus.hpp:121
@ OP_CONSTPOW
Definition: calculus.hpp:66
@ OP_NOT
Definition: calculus.hpp:70
@ OP_MUL
Definition: calculus.hpp:65
@ OP_HORZSPLIT
Definition: calculus.hpp:133
@ OP_SQ
Definition: calculus.hpp:67
@ OP_NORMF
Definition: calculus.hpp:178
@ OP_GETNONZEROS
Definition: calculus.hpp:151
@ OP_NORMINF
Definition: calculus.hpp:178
Function memory with temporary work vectors.
An element of the algorithm, namely an MX node.
Definition: mx_function.hpp:45
MX data
Data associated with the operation.
Definition: mx_function.hpp:50
std::vector< casadi_int > arg
Work vector indices of the arguments.
Definition: mx_function.hpp:53
casadi_int op
Operator index.
Definition: mx_function.hpp:47
std::vector< casadi_int > res
Work vector indices of the results.
Definition: mx_function.hpp:56
Options metadata for a class.
Definition: options.hpp:40
static std::string print(unsigned char op, const std::string &x, const std::string &y)
Print.
Definition: calculus.hpp:1651