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