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. Influenced by print_canonical."}},
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  if (target=="clone") 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  // workloc_ (register offsets) is shared by VM eval and codegen, so
355  // the scratch region must cover whichever needs more
356  sz_w = std::max(sz_w, std::max(e.data->sz_w(), e.data->codegen_sz_w()));
357  if (workloc_[e.res[c]] < 0) {
358  workloc_[e.res[c]] = wind;
359  wind += e.data->sparsity(c).nnz();
360  }
361  }
362  }
363  }
364  }
365  workloc_.back()=wind;
366  for (casadi_int i=0; i<workloc_.size(); ++i) {
367  if (workloc_[i]<0) workloc_[i] = i==0 ? 0 : workloc_[i-1];
368  workloc_[i] += sz_w;
369  }
370  sz_w += wind;
371  alloc_w(sz_w);
372 
373  // Reset the temporary variables
374  for (casadi_int i=0; i<nodes.size(); ++i) {
375  if (nodes[i]) {
376  nodes[i]->temp = 0;
377  }
378  }
379 
380  // Now mark each input's place in the algorithm
381  for (auto it=symb_loc.begin(); it!=symb_loc.end(); ++it) {
382  it->second->temp = it->first+1;
383  }
384 
385  // Add input instructions, loop over inputs
386  for (casadi_int ind=0; ind<in_.size(); ++ind) {
387  // Loop over symbolic primitives of each input
388  std::vector<MX> prim = in_[ind].primitives();
389  casadi_int nz_offset=0;
390  for (casadi_int p=0; p<prim.size(); ++p) {
391  casadi_int i = prim[p].get_temp()-1;
392  if (i>=0) {
393  // Mark read
394  prim[p].set_temp(0);
395 
396  // Replace parameter with input instruction
397  algorithm_[i].data.own(new Input(prim[p].sparsity(), ind, p, nz_offset));
398  algorithm_[i].op = OP_INPUT;
399  }
400  nz_offset += prim[p]->nnz();
401  }
402  }
403 
404  // Locate free variables
405  free_vars_.clear();
406  for (auto it=symb_loc.begin(); it!=symb_loc.end(); ++it) {
407  casadi_int i = it->second->temp-1;
408  if (i>=0) {
409  // Save to list of free parameters
410  free_vars_.push_back(MX::create(it->second));
411 
412  // Remove marker
413  it->second->temp=0;
414  }
415  }
416 
417  if (!allow_free && has_free()) {
418  casadi_error(name_ + "::init: Initialization failed since variables [" +
419  join(get_free(), ", ") + "] are free. These symbols occur in the output expressions "
420  "but you forgot to declare these as inputs. "
421  "Set option 'allow_free' to allow free variables.");
422  }
423 
424  // Does any nodes require reference counting for codegen?
425  // NOTE: this excludes CALL nodes
426  for (auto&& a : algorithm_) {
427  if (a.data->has_refcount()) {
428  has_refcount_ = true;
429  break;
430  }
431  }
432 
433  }
434 
435  int MXFunction::eval(const double** arg, double** res,
436  casadi_int* iw, double* w, void* mem) const {
437  if (verbose_) casadi_message(name_ + "::eval");
438  setup(mem, arg, res, iw, w);
439  // Work vector and temporaries to hold pointers to operation input and outputs
440  const double** arg1 = arg+n_in_;
441  double** res1 = res+n_out_;
442 
443  // Make sure that there are no free variables
444  if (!free_vars_.empty()) {
445  std::stringstream ss;
446  disp(ss, false);
447  casadi_error("Cannot evaluate \"" + ss.str() + "\" since variables "
448  + str(free_vars_) + " are free.");
449  }
450 
451  // Operation number (for printing)
452  casadi_int k = 0;
453 
454  // Evaluate all of the nodes of the algorithm:
455  // should only evaluate nodes that have not yet been calculated!
456  for (auto&& e : algorithm_) {
457  // Perform the operation
458  if (e.op==OP_INPUT) {
459  // Pass an input
460  double *w1 = w+workloc_[e.res.front()];
461  casadi_int nnz=e.data.nnz();
462  casadi_int i=e.data->ind();
463  casadi_int nz_offset=e.data->offset();
464  if (arg[i]==nullptr) {
465  std::fill(w1, w1+nnz, 0);
466  } else {
467  std::copy(arg[i]+nz_offset, arg[i]+nz_offset+nnz, w1);
468  }
469  } else if (e.op==OP_OUTPUT) {
470  // Get an output
471  double *w1 = w+workloc_[e.arg.front()];
472  casadi_int nnz=e.data->dep().nnz();
473  casadi_int i=e.data->ind();
474  casadi_int nz_offset=e.data->offset();
475  if (res[i]) std::copy(w1, w1+nnz, res[i]+nz_offset);
476  } else {
477  // Point pointers to the data corresponding to the element
478  for (casadi_int i=0; i<e.arg.size(); ++i)
479  arg1[i] = e.arg[i]>=0 ? w+workloc_[e.arg[i]] : nullptr;
480  for (casadi_int i=0; i<e.res.size(); ++i)
481  res1[i] = e.res[i]>=0 ? w+workloc_[e.res[i]] : nullptr;
482 
483  // Evaluate
484  if (print_instructions_) print_arg(uout(), k, e, arg1);
485  if (e.data->eval(arg1, res1, iw, w)) return 1;
486  if (print_instructions_) print_res(uout(), k, e, res1);
487  }
488  // Increase counter
489  k++;
490  }
491  return 0;
492  }
493 
494  std::string MXFunction::print(const AlgEl& el) const {
495  std::stringstream s;
496  if (el.op==OP_OUTPUT) {
497  s << "output[" << el.data->ind() << "][" << el.data->segment() << "]"
498  << " = @" << el.arg.at(0);
499  } else if (el.op==OP_SETNONZEROS || el.op==OP_ADDNONZEROS) {
500  if (el.res.front()!=el.arg.at(0)) {
501  s << "@" << el.res.front() << " = @" << el.arg.at(0) << "; ";
502  }
503  std::vector<std::string> arg(2);
504  arg[0] = "@" + str(el.res.front());
505  arg[1] = "@" + str(el.arg.at(1));
506  s << el.data->disp(arg);
507  } else {
508  if (el.res.size()==1) {
509  s << "@" << el.res.front() << " = ";
510  } else {
511  s << "{";
512  for (casadi_int i=0; i<el.res.size(); ++i) {
513  if (i!=0) s << ", ";
514  if (el.res[i]>=0) {
515  s << "@" << el.res[i];
516  } else {
517  s << "NULL";
518  }
519  }
520  s << "} = ";
521  }
522  std::vector<std::string> arg;
523  if (el.op!=OP_INPUT) {
524  arg.resize(el.arg.size());
525  for (casadi_int i=0; i<el.arg.size(); ++i) {
526  if (el.arg[i]>=0) {
527  arg[i] = "@" + str(el.arg[i]);
528  } else {
529  arg[i] = "NULL";
530  }
531  }
532  }
533  s << el.data->disp(arg);
534  }
535  return s.str();
536  }
537 
538  void MXFunction::print_arg(std::ostream &stream, casadi_int k, const AlgEl& el,
539  const double** arg) const {
540  stream << name_ << ":" << k << ": " << print(el) << " inputs:" << std::endl;
541  for (size_t i = 0; i < el.arg.size(); ++i) {
542  if (arg[i]) {
543  stream << i << ": ";
544  if (print_canonical_) {
545  print_canonical(stream, el.data->dep(i).sparsity(), arg[i]);
546  } else {
547  DM::print_default(stream, el.data->dep(i).sparsity(), arg[i], true);
548  }
549  stream << std::endl;
550  }
551  }
552  }
553 
554  void MXFunction::print_arg(CodeGenerator& g, casadi_int k, const AlgEl& el,
555  const std::vector<casadi_int>& arg, const std::vector<bool>& arg_is_ref) const {
556  g << g.printf(name_ + ":" + str(k) + ": " + print(el) + " inputs:\\n") << "\n";
557  for (size_t i = 0; i < el.arg.size(); ++i) {
558  if (arg[i]>=0) {
559  g << g.printf(str(i) + ": ");
560  std::string a = g.work(arg[i], el.data->dep(i).nnz(), arg_is_ref[i]);
561  g << g.print_canonical(el.data->dep(i).sparsity(), a);
562  g << g.printf("\\n") << "\n";
563  }
564  }
565  }
566 
567  void MXFunction::print_res(CodeGenerator& g, casadi_int k, const AlgEl& el,
568  const std::vector<casadi_int>& res, const std::vector<bool>& res_is_ref) const {
569  g << g.printf(name_ + ":" + str(k) + ": " + print(el) + " outputs:\\n") << "\n";
570  for (size_t i = 0; i < el.res.size(); ++i) {
571  if (res[i]>=0) {
572  g << g.printf(str(i) + ": ");
573  std::string a = g.work(res[i], el.data->sparsity(i).nnz(), res_is_ref[i]);
574  g << g.print_canonical(el.data->sparsity(i), a);
575  g << g.printf("\\n") << "\n";
576  }
577  }
578  }
579 
580  void MXFunction::print_res(std::ostream &stream, casadi_int k, const AlgEl& el,
581  double** res) const {
582  stream << name_ << ":" << k << ": " << print(el) << " outputs:" << std::endl;
583  for (size_t i = 0; i < el.res.size(); ++i) {
584  if (res[i]) {
585  stream << i << ": ";
586  if (print_canonical_) {
587  print_canonical(stream, el.data->sparsity(i), res[i]);
588  } else {
589  DM::print_default(stream, el.data->sparsity(i), res[i], true);
590  }
591  stream << std::endl;
592  }
593  }
594  }
595 
596  void MXFunction::disp_more(std::ostream &stream) const {
597  stream << "Algorithm:";
598  for (auto&& e : algorithm_) {
600  stream << std::endl << print(e);
601  }
602  }
603 
605  sp_forward(const bvec_t** arg, bvec_t** res, casadi_int* iw, bvec_t* w, void* mem) const {
606  // Fall back when forward mode not allowed
607  if (sp_weight()==1 || sp_weight()==-1)
608  return FunctionInternal::sp_forward(arg, res, iw, w, mem);
609  // Temporaries to hold pointers to operation input and outputs
610  const bvec_t** arg1=arg+n_in_;
611  bvec_t** res1=res+n_out_;
612 
613  // Propagate sparsity forward
614  for (auto&& e : algorithm_) {
615  if (e.op==OP_INPUT) {
616  // Pass input seeds
617  casadi_int nnz=e.data.nnz();
618  casadi_int i=e.data->ind();
619  casadi_int nz_offset=e.data->offset();
620  const bvec_t* argi = arg[i];
621  bvec_t* w1 = w + workloc_[e.res.front()];
622  if (argi!=nullptr && is_diff_in_[i]) {
623  std::copy(argi+nz_offset, argi+nz_offset+nnz, w1);
624  } else {
625  std::fill_n(w1, nnz, 0);
626  }
627  } else if (e.op==OP_OUTPUT) {
628  // Get the output sensitivities
629  casadi_int nnz=e.data.dep().nnz();
630  casadi_int i=e.data->ind();
631  casadi_int nz_offset=e.data->offset();
632  bvec_t* resi = res[i];
633  bvec_t* w1 = w + workloc_[e.arg.front()];
634  if (resi!=nullptr && is_diff_out_[i]) {
635  std::copy(w1, w1+nnz, resi+nz_offset);
636  } else if (resi!=nullptr) {
637  std::fill_n(resi+nz_offset, nnz, 0);
638  }
639  } else {
640  // Point pointers to the data corresponding to the element
641  for (casadi_int i=0; i<e.arg.size(); ++i)
642  arg1[i] = e.arg[i]>=0 ? w+workloc_[e.arg[i]] : nullptr;
643  for (casadi_int i=0; i<e.res.size(); ++i)
644  res1[i] = e.res[i]>=0 ? w+workloc_[e.res[i]] : nullptr;
645 
646  // Propagate sparsity forwards
647  if (e.data->sp_forward(arg1, res1, iw, w)) return 1;
648  }
649  }
650  return 0;
651  }
652 
654  eval_activity(const bvec_t** arg, bvec_t** res, casadi_int* iw, bvec_t* w, void* mem) const {
655  // Temporaries to hold pointers to operation input and outputs
656  const bvec_t** arg1=arg+n_in_;
657  bvec_t** res1=res+n_out_;
658 
659  // Propagate signal activity forward (ignores is_diff: this is a value analysis)
660  for (auto&& e : algorithm_) {
661  if (e.op==OP_INPUT) {
662  casadi_int nnz=e.data.nnz();
663  casadi_int i=e.data->ind();
664  casadi_int nz_offset=e.data->offset();
665  const bvec_t* argi = arg[i];
666  bvec_t* w1 = w + workloc_[e.res.front()];
667  if (argi!=nullptr) {
668  std::copy(argi+nz_offset, argi+nz_offset+nnz, w1);
669  } else {
670  std::fill_n(w1, nnz, 0);
671  }
672  } else if (e.op==OP_OUTPUT) {
673  casadi_int nnz=e.data.dep().nnz();
674  casadi_int i=e.data->ind();
675  casadi_int nz_offset=e.data->offset();
676  bvec_t* resi = res[i];
677  bvec_t* w1 = w + workloc_[e.arg.front()];
678  if (resi!=nullptr) std::copy(w1, w1+nnz, resi+nz_offset);
679  } else {
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  if (e.data->eval_activity(arg1, res1, iw, w)) return 1;
685  }
686  }
687  return 0;
688  }
689 
690  std::vector<std::string> MXFunction::get_function() const {
691  std::map<std::string, bool> flagged;
692  for (auto it=algorithm_.begin(); it!=algorithm_.end(); it++) {
693  if (it->op==OP_CALL) {
694  const Function &f = it->data->which_function();
695  if (flagged.find(f.name())==flagged.end()) {
696  flagged[f.name()] = true;
697  }
698  }
699  }
700  std::vector<std::string> ret;
701  for (auto it : flagged) {
702  ret.push_back(it.first);
703  }
704  return ret;
705  }
706 
707  const Function& MXFunction::get_function(const std::string &name) const {
708  for (auto it=algorithm_.begin(); it!=algorithm_.end(); it++) {
709  if (it->op==OP_CALL) {
710  const Function &f = it->data->which_function();
711  if (name==f.name()) return f;
712  }
713  }
714  casadi_error("No such function '" + name + "'.");
715  }
716 
718  casadi_int* iw, bvec_t* w, void* mem) const {
719  // Fall back when reverse mode not allowed
720  if (sp_weight()==0 || sp_weight()==-1)
721  return FunctionInternal::sp_reverse(arg, res, iw, w, mem);
722  // Temporaries to hold pointers to operation input and outputs
723  bvec_t** arg1=arg+n_in_;
724  bvec_t** res1=res+n_out_;
725 
726  std::fill_n(w, sz_w(), 0);
727 
728  // Propagate sparsity backwards
729  for (auto it=algorithm_.rbegin(); it!=algorithm_.rend(); it++) {
730  if (it->op==OP_INPUT) {
731  // Get the input sensitivities and clear it from the work vector
732  casadi_int nnz=it->data.nnz();
733  casadi_int i=it->data->ind();
734  casadi_int nz_offset=it->data->offset();
735  bvec_t* argi = arg[i];
736  bvec_t* w1 = w + workloc_[it->res.front()];
737  if (argi!=nullptr && is_diff_in_[i])
738  for (casadi_int k=0; k<nnz; ++k) argi[nz_offset+k] |= w1[k];
739  std::fill_n(w1, nnz, 0);
740  } else if (it->op==OP_OUTPUT) {
741  // Pass output seeds
742  casadi_int nnz=it->data.dep().nnz();
743  casadi_int i=it->data->ind();
744  casadi_int nz_offset=it->data->offset();
745  bvec_t* resi = res[i] ? res[i] + nz_offset : nullptr;
746  bvec_t* w1 = w + workloc_[it->arg.front()];
747  if (resi!=nullptr && is_diff_out_[i]) {
748  for (casadi_int k=0; k<nnz; ++k) w1[k] |= resi[k];
749  std::fill_n(resi, nnz, 0);
750  }
751  } else {
752  // Point pointers to the data corresponding to the element
753  for (casadi_int i=0; i<it->arg.size(); ++i)
754  arg1[i] = it->arg[i]>=0 ? w+workloc_[it->arg[i]] : nullptr;
755  for (casadi_int i=0; i<it->res.size(); ++i)
756  res1[i] = it->res[i]>=0 ? w+workloc_[it->res[i]] : nullptr;
757 
758  // Propagate sparsity backwards
759  if (it->data->sp_reverse(arg1, res1, iw, w)) return 1;
760  }
761  }
762  return 0;
763  }
764 
765  std::vector<MX> MXFunction::symbolic_output(const std::vector<MX>& arg) const {
766  // Check if input is given
767  const casadi_int checking_depth = 2;
768  bool input_given = true;
769  for (casadi_int i=0; i<arg.size() && input_given; ++i) {
770  if (!is_equal(arg[i], in_[i], checking_depth)) {
771  input_given = false;
772  }
773  }
774 
775  // Return output if possible, else fall back to base class
776  if (input_given) {
777  return out_;
778  } else {
780  }
781  }
782 
783  void MXFunction::eval_mx(const MXVector& arg, MXVector& res,
784  bool always_inline, bool never_inline) const {
785  always_inline = always_inline || always_inline_;
786  never_inline = never_inline || never_inline_;
787  if (verbose_) casadi_message(name_ + "::eval_mx");
788  try {
789  // Resize the number of outputs
790  casadi_assert(arg.size()==n_in_, "Wrong number of input arguments");
791  res.resize(out_.size());
792 
793  // Trivial inline by default if output known
794  if (!never_inline && isInput(arg)) {
795  std::copy(out_.begin(), out_.end(), res.begin());
796  return;
797  }
798 
799  // non-inlining call is implemented in the base-class
800  if (!should_inline(false, always_inline, never_inline)) {
801  FunctionInternal::eval_mx(arg, res, false, true);
802  return;
803  }
804 
805  // Symbolic work, non-differentiated
806  std::vector<MX> swork(workloc_.size()-1);
807  if (verbose_) casadi_message("Allocated work vector");
808 
809  // Split up inputs analogous to symbolic primitives
810  std::vector<std::vector<MX> > arg_split(in_.size());
811  for (casadi_int i=0; i<in_.size(); ++i) arg_split[i] = in_[i].split_primitives(arg[i]);
812 
813  // Allocate storage for split outputs
814  std::vector<std::vector<MX> > res_split(out_.size());
815  for (casadi_int i=0; i<out_.size(); ++i) res_split[i].resize(out_[i].n_primitives());
816 
817  std::vector<MX> arg1, res1;
818 
819  // Loop over computational nodes in forward order
820  casadi_int alg_counter = 0;
821  for (auto it=algorithm_.begin(); it!=algorithm_.end(); ++it, ++alg_counter) {
822  if (it->op == OP_INPUT) {
823  swork[it->res.front()] = project(arg_split.at(it->data->ind()).at(it->data->segment()),
824  it->data.sparsity(), true);
825  } else if (it->op==OP_OUTPUT) {
826  // Collect the results
827  res_split.at(it->data->ind()).at(it->data->segment()) = swork[it->arg.front()];
828  } else if (it->op==OP_PARAMETER) {
829  // Fetch parameter
830  swork[it->res.front()] = it->data;
831  } else {
832  // Arguments of the operation
833  arg1.resize(it->arg.size());
834  for (casadi_int i=0; i<arg1.size(); ++i) {
835  casadi_int el = it->arg[i]; // index of the argument
836  arg1[i] = el<0 ? MX(it->data->dep(i).size()) : swork[el];
837  }
838 
839  // Perform the operation
840  res1.resize(it->res.size());
841  it->data->eval_mx(arg1, res1);
842 
843  // Get the result
844  for (casadi_int i=0; i<res1.size(); ++i) {
845  casadi_int el = it->res[i]; // index of the output
846  if (el>=0) swork[el] = res1[i];
847  }
848  }
849  }
850 
851  // Join split outputs
852  for (casadi_int i=0; i<res.size(); ++i) res[i] = out_[i].join_primitives(res_split[i]);
853  } catch (std::exception& e) {
854  CASADI_THROW_ERROR("eval_mx", e.what());
855  }
856  }
857 
858  void MXFunction::ad_forward(const std::vector<std::vector<MX> >& fseed,
859  std::vector<std::vector<MX> >& fsens) const {
860  if (verbose_) casadi_message(name_ + "::ad_forward(" + str(fseed.size())+ ")");
861  try {
862  // Allocate results
863  casadi_int nfwd = fseed.size();
864  fsens.resize(nfwd);
865  for (casadi_int d=0; d<nfwd; ++d) {
866  fsens[d].resize(n_out_);
867  }
868 
869  // Quick return if no directions
870  if (nfwd==0) return;
871 
872  // Check if seeds need to have dimensions corrected
873  casadi_int npar = 1;
874  for (auto&& r : fseed) {
875  if (!matching_arg(r, npar)) {
876  casadi_assert_dev(npar==1);
877  ad_forward(replace_fseed(fseed, npar), fsens);
878  return;
879  }
880  }
881 
882  // Check if there are any zero seeds
883  for (auto&& r : fseed) {
884  if (purgable(r)) {
885  // New argument without all-zero directions
886  std::vector<std::vector<MX> > fseed_purged, fsens_purged;
887  fseed_purged.reserve(nfwd);
888  std::vector<casadi_int> index_purged;
889  for (casadi_int d=0; d<nfwd; ++d) {
890  if (purgable(fseed[d])) {
891  for (casadi_int i=0; i<fsens[d].size(); ++i) {
892  fsens[d][i] = MX(size_out(i));
893  }
894  } else {
895  fseed_purged.push_back(fsens[d]);
896  index_purged.push_back(d);
897  }
898  }
899 
900  // Call recursively
901  ad_forward(fseed_purged, fsens_purged);
902 
903  // Fetch result
904  for (casadi_int d=0; d<fseed_purged.size(); ++d) {
905  fsens[index_purged[d]] = fsens_purged[d];
906  }
907  return;
908  }
909  }
910 
911  if (!enable_forward_) {
912  // Do the non-inlining call from FunctionInternal
913  // NOLINTNEXTLINE(bugprone-parent-virtual-call)
914  FunctionInternal::call_forward(in_, out_, fseed, fsens, false, false);
915  return;
916  }
917 
918  // Work vector, forward derivatives
919  std::vector<std::vector<MX> > dwork(workloc_.size()-1);
920  fill(dwork.begin(), dwork.end(), std::vector<MX>(nfwd));
921  if (verbose_) casadi_message("Allocated derivative work vector (forward mode)");
922 
923  // Split up fseed analogous to symbolic primitives
924  std::vector<std::vector<std::vector<MX>>> fseed_split(nfwd);
925  for (casadi_int d=0; d<nfwd; ++d) {
926  fseed_split[d].resize(fseed[d].size());
927  for (casadi_int i=0; i<fseed[d].size(); ++i) {
928  fseed_split[d][i] = in_[i].split_primitives(fseed[d][i]);
929  }
930  }
931 
932  // Allocate splited forward sensitivities
933  std::vector<std::vector<std::vector<MX>>> fsens_split(nfwd);
934  for (casadi_int d=0; d<nfwd; ++d) {
935  fsens_split[d].resize(out_.size());
936  for (casadi_int i=0; i<out_.size(); ++i) {
937  fsens_split[d][i].resize(out_[i].n_primitives());
938  }
939  }
940 
941  // Pointers to the arguments of the current operation
942  std::vector<std::vector<MX> > oseed, osens;
943  oseed.reserve(nfwd);
944  osens.reserve(nfwd);
945  std::vector<bool> skip(nfwd, false);
946 
947  // Loop over computational nodes in forward order
948  for (auto&& e : algorithm_) {
949  if (e.op == OP_INPUT) {
950  // Fetch forward seed
951  for (casadi_int d=0; d<nfwd; ++d) {
952  dwork[e.res.front()][d] =
953  project(fseed_split[d].at(e.data->ind()).at(e.data->segment()),
954  e.data.sparsity(), true);
955  }
956  } else if (e.op==OP_OUTPUT) {
957  // Collect forward sensitivity
958  for (casadi_int d=0; d<nfwd; ++d) {
959  fsens_split[d][e.data->ind()][e.data->segment()] = dwork[e.arg.front()][d];
960  }
961  } else if (e.op==OP_PARAMETER) {
962  // Fetch parameter
963  for (casadi_int d=0; d<nfwd; ++d) {
964  dwork[e.res.front()][d] = MX();
965  }
966  } else {
967  // Get seeds, ignoring all-zero directions
968  oseed.clear();
969  for (casadi_int d=0; d<nfwd; ++d) {
970  // Collect seeds, skipping directions with only zeros
971  std::vector<MX> seed(e.arg.size());
972  skip[d] = true; // All seeds are zero?
973  for (casadi_int i=0; i<e.arg.size(); ++i) {
974  casadi_int el = e.arg[i];
975  if (el<0 || dwork[el][d].is_empty(true)) {
976  seed[i] = MX(e.data->dep(i).size());
977  } else {
978  seed[i] = dwork[el][d];
979  }
980  if (skip[d] && !seed[i].is_zero()) skip[d] = false;
981  }
982  if (!skip[d]) oseed.push_back(seed);
983  }
984 
985  // Perform the operation
986  osens.resize(oseed.size());
987  if (!osens.empty()) {
988  fill(osens.begin(), osens.end(), std::vector<MX>(e.res.size()));
989  e.data.ad_forward(oseed, osens);
990  }
991 
992  // Store sensitivities
993  casadi_int d1=0;
994  for (casadi_int d=0; d<nfwd; ++d) {
995  for (casadi_int i=0; i<e.res.size(); ++i) {
996  casadi_int el = e.res[i];
997  if (el>=0) {
998  dwork[el][d] = skip[d] ? MX(e.data->sparsity(i).size()) : osens[d1][i];
999  }
1000  }
1001  if (!skip[d]) d1++;
1002  }
1003  }
1004  }
1005 
1006  // Get forward sensitivities
1007  for (casadi_int d=0; d<nfwd; ++d) {
1008  for (casadi_int i=0; i<out_.size(); ++i) {
1009  fsens[d][i] = out_[i].join_primitives(fsens_split[d][i]);
1010  }
1011  }
1012  } catch (std::exception& e) {
1013  CASADI_THROW_ERROR("ad_forward", e.what());
1014  }
1015  }
1016 
1017  void MXFunction::ad_reverse(const std::vector<std::vector<MX> >& aseed,
1018  std::vector<std::vector<MX> >& asens) const {
1019  if (verbose_) casadi_message(name_ + "::ad_reverse(" + str(aseed.size())+ ")");
1020  try {
1021 
1022  // Allocate results
1023  casadi_int nadj = aseed.size();
1024  asens.resize(nadj);
1025  for (casadi_int d=0; d<nadj; ++d) {
1026  asens[d].resize(n_in_);
1027  }
1028 
1029  // Quick return if no directions
1030  if (nadj==0) return;
1031 
1032  // Check if seeds need to have dimensions corrected
1033  casadi_int npar = 1;
1034  for (auto&& r : aseed) {
1035  if (!matching_res(r, npar)) {
1036  casadi_assert_dev(npar==1);
1037  ad_reverse(replace_aseed(aseed, npar), asens);
1038  return;
1039  }
1040  }
1041 
1042  // Check if there are any zero seeds
1043  for (auto&& r : aseed) {
1044  // If any direction can be skipped
1045  if (purgable(r)) {
1046  // New argument without all-zero directions
1047  std::vector<std::vector<MX> > aseed_purged, asens_purged;
1048  aseed_purged.reserve(nadj);
1049  std::vector<casadi_int> index_purged;
1050  for (casadi_int d=0; d<nadj; ++d) {
1051  if (purgable(aseed[d])) {
1052  for (casadi_int i=0; i<asens[d].size(); ++i) {
1053  asens[d][i] = MX(size_in(i));
1054  }
1055  } else {
1056  aseed_purged.push_back(asens[d]);
1057  index_purged.push_back(d);
1058  }
1059  }
1060 
1061  // Call recursively
1062  ad_reverse(aseed_purged, asens_purged);
1063 
1064  // Fetch result
1065  for (casadi_int d=0; d<aseed_purged.size(); ++d) {
1066  asens[index_purged[d]] = asens_purged[d];
1067  }
1068  return;
1069  }
1070  }
1071 
1072  if (!enable_reverse_) {
1073  std::vector<std::vector<MX> > v;
1074  // Do the non-inlining call from FunctionInternal
1075  // NOLINTNEXTLINE(bugprone-parent-virtual-call)
1076  FunctionInternal::call_reverse(in_, out_, aseed, v, false, false);
1077  for (casadi_int i=0; i<v.size(); ++i) {
1078  for (casadi_int j=0; j<v[i].size(); ++j) {
1079  if (!v[i][j].is_empty()) { // TODO(@jaeandersson): Hack
1080  if (asens[i][j].is_empty()) {
1081  asens[i][j] = v[i][j];
1082  } else {
1083  asens[i][j] += v[i][j];
1084  }
1085  }
1086  }
1087  }
1088  return;
1089  }
1090 
1091  // Split up aseed analogous to symbolic primitives
1092  std::vector<std::vector<std::vector<MX>>> aseed_split(nadj);
1093  for (casadi_int d=0; d<nadj; ++d) {
1094  aseed_split[d].resize(out_.size());
1095  for (casadi_int i=0; i<out_.size(); ++i) {
1096  aseed_split[d][i] = out_[i].split_primitives(aseed[d][i]);
1097  }
1098  }
1099 
1100  // Allocate splited adjoint sensitivities
1101  std::vector<std::vector<std::vector<MX>>> asens_split(nadj);
1102  for (casadi_int d=0; d<nadj; ++d) {
1103  asens_split[d].resize(in_.size());
1104  for (casadi_int i=0; i<in_.size(); ++i) {
1105  asens_split[d][i].resize(in_[i].n_primitives());
1106  }
1107  }
1108 
1109  // Pointers to the arguments of the current operation
1110  std::vector<std::vector<MX>> oseed, osens;
1111  oseed.reserve(nadj);
1112  osens.reserve(nadj);
1113  std::vector<bool> skip(nadj, false);
1114 
1115  // Work vector, adjoint derivatives
1116  std::vector<std::vector<MX> > dwork(workloc_.size()-1);
1117  fill(dwork.begin(), dwork.end(), std::vector<MX>(nadj));
1118 
1119  // Loop over computational nodes in reverse order
1120  for (auto it=algorithm_.rbegin(); it!=algorithm_.rend(); ++it) {
1121  if (it->op == OP_INPUT) {
1122  // Get the adjoint sensitivities
1123  for (casadi_int d=0; d<nadj; ++d) {
1124  asens_split[d].at(it->data->ind()).at(it->data->segment()) = dwork[it->res.front()][d];
1125  dwork[it->res.front()][d] = MX();
1126  }
1127  } else if (it->op==OP_OUTPUT) {
1128  // Pass the adjoint seeds
1129  for (casadi_int d=0; d<nadj; ++d) {
1130  MX a = project(aseed_split[d].at(it->data->ind()).at(it->data->segment()),
1131  it->data.dep().sparsity(), true);
1132  if (dwork[it->arg.front()][d].is_empty(true)) {
1133  dwork[it->arg.front()][d] = a;
1134  } else {
1135  dwork[it->arg.front()][d] += a;
1136  }
1137  }
1138  } else if (it->op==OP_PARAMETER) {
1139  // Clear adjoint seeds
1140  for (casadi_int d=0; d<nadj; ++d) {
1141  dwork[it->res.front()][d] = MX();
1142  }
1143  } else {
1144  // Collect and reset seeds
1145  oseed.clear();
1146  for (casadi_int d=0; d<nadj; ++d) {
1147  // Can the direction be skipped completely?
1148  skip[d] = true;
1149 
1150  // Seeds for direction d
1151  std::vector<MX> seed(it->res.size());
1152  for (casadi_int i=0; i<it->res.size(); ++i) {
1153  // Get and clear seed
1154  casadi_int el = it->res[i];
1155  if (el>=0) {
1156  seed[i] = dwork[el][d];
1157  dwork[el][d] = MX();
1158  } else {
1159  seed[i] = MX();
1160  }
1161 
1162  // If first time encountered, reset to zero of right dimension
1163  if (seed[i].is_empty(true)) seed[i] = MX(it->data->sparsity(i).size());
1164 
1165  // If nonzero seeds, keep direction
1166  if (skip[d] && !seed[i].is_zero()) skip[d] = false;
1167  }
1168  // Add to list of derivatives
1169  if (!skip[d]) oseed.push_back(seed);
1170  }
1171 
1172  // Get values of sensitivities before addition
1173  osens.resize(oseed.size());
1174  casadi_int d1=0;
1175  for (casadi_int d=0; d<nadj; ++d) {
1176  if (skip[d]) continue;
1177  osens[d1].resize(it->arg.size());
1178  for (casadi_int i=0; i<it->arg.size(); ++i) {
1179  // Pass seed and reset to avoid counting twice
1180  casadi_int el = it->arg[i];
1181  if (el>=0) {
1182  osens[d1][i] = dwork[el][d];
1183  dwork[el][d] = MX();
1184  } else {
1185  osens[d1][i] = MX();
1186  }
1187 
1188  // If first time encountered, reset to zero of right dimension
1189  if (osens[d1][i].is_empty(true)) osens[d1][i] = MX(it->data->dep(i).size());
1190  }
1191  d1++;
1192  }
1193 
1194  // Perform the operation
1195  if (!osens.empty()) {
1196  it->data.ad_reverse(oseed, osens);
1197  }
1198 
1199  // Store sensitivities
1200  d1=0;
1201  for (casadi_int d=0; d<nadj; ++d) {
1202  if (skip[d]) continue;
1203  for (casadi_int i=0; i<it->arg.size(); ++i) {
1204  casadi_int el = it->arg[i];
1205  if (el>=0) {
1206  if (dwork[el][d].is_empty(true)) {
1207  dwork[el][d] = osens[d1][i];
1208  } else {
1209  dwork[el][d] += osens[d1][i];
1210  }
1211  }
1212  }
1213  d1++;
1214  }
1215  }
1216  }
1217 
1218  // Get adjoint sensitivities
1219  for (casadi_int d=0; d<nadj; ++d) {
1220  for (casadi_int i=0; i<in_.size(); ++i) {
1221  asens[d][i] = in_[i].join_primitives(asens_split[d][i]);
1222  }
1223  }
1224  } catch (std::exception& e) {
1225  CASADI_THROW_ERROR("ad_reverse", e.what());
1226  }
1227  }
1228 
1229  int MXFunction::eval_sx(const SXElem** arg, SXElem** res,
1230  casadi_int* iw, SXElem* w, void* mem,
1231  bool always_inline, bool never_inline) const {
1232  always_inline = always_inline || always_inline_;
1233  never_inline = never_inline || never_inline_;
1234 
1235  // non-inlining call is implemented in the base-class
1236  if (!should_inline(true, always_inline, never_inline)) {
1237  return FunctionInternal::eval_sx(arg, res, iw, w, mem, false, true);
1238  }
1239 
1240  // Work vector and temporaries to hold pointers to operation input and outputs
1241  std::vector<const SXElem*> argp(sz_arg());
1242  std::vector<SXElem*> resp(sz_res());
1243 
1244  // Evaluate all of the nodes of the algorithm:
1245  // should only evaluate nodes that have not yet been calculated!
1246  for (auto&& a : algorithm_) {
1247  if (a.op==OP_INPUT) {
1248  // Pass an input
1249  SXElem *w1 = w+workloc_[a.res.front()];
1250  casadi_int nnz=a.data.nnz();
1251  casadi_int i=a.data->ind();
1252  casadi_int nz_offset=a.data->offset();
1253  if (arg[i]==nullptr) {
1254  std::fill(w1, w1+nnz, 0);
1255  } else {
1256  std::copy(arg[i]+nz_offset, arg[i]+nz_offset+nnz, w1);
1257  }
1258  } else if (a.op==OP_OUTPUT) {
1259  // Get the outputs
1260  SXElem *w1 = w+workloc_[a.arg.front()];
1261  casadi_int nnz=a.data.dep().nnz();
1262  casadi_int i=a.data->ind();
1263  casadi_int nz_offset=a.data->offset();
1264  if (res[i]) std::copy(w1, w1+nnz, res[i]+nz_offset);
1265  } else if (a.op==OP_PARAMETER) {
1266  continue; // FIXME
1267  } else {
1268  // Point pointers to the data corresponding to the element
1269  for (casadi_int i=0; i<a.arg.size(); ++i)
1270  argp[i] = a.arg[i]>=0 ? w+workloc_[a.arg[i]] : nullptr;
1271  for (casadi_int i=0; i<a.res.size(); ++i)
1272  resp[i] = a.res[i]>=0 ? w+workloc_[a.res[i]] : nullptr;
1273 
1274  // Evaluate
1275  if (a.data->eval_sx(get_ptr(argp), get_ptr(resp), iw, w)) return 1;
1276  }
1277  }
1278  return 0;
1279  }
1280 
1282 
1283  // Make sure that there are no free variables
1284  if (!free_vars_.empty()) {
1285  casadi_error("Code generation of '" + name_ + "' is not possible since variables "
1286  + str(free_vars_) + " are free.");
1287  }
1288 
1289  // Generate code for the embedded functions
1290  for (auto&& a : algorithm_) {
1291  a.data->add_dependency(g);
1292  }
1293  }
1294 
1297  std::set<void*> added;
1298  for (auto&& a : algorithm_) {
1299  a.data->codegen_incref(g, added);
1300  }
1301  }
1302 
1305  std::set<void*> added;
1306  for (auto&& a : algorithm_) {
1307  a.data->codegen_decref(g, added);
1308  }
1309  }
1310 
1312  // Temporary variables and vectors
1313  g.init_local("arg1", "arg+" + str(n_in_));
1314  g.init_local("res1", "res+" + str(n_out_));
1315 
1316  g.reserve_work(workloc_.size()-1);
1317 
1318  // Operation number (for printing)
1319  casadi_int k=0;
1320 
1321  // Names of operation argument and results
1322  std::vector<casadi_int> arg, res;
1323 
1324  // State of work vector: reference or not (value types)
1325  std::vector<bool> work_is_ref(workloc_.size()-1, false);
1326 
1327  // State of operation arguments and results: reference or not
1328  std::vector<bool> arg_is_ref, res_is_ref;
1329 
1330  // Collect for each work vector element if reference or value needed
1331  std::vector<bool> needs_reference(workloc_.size()-1, false);
1332  std::vector<bool> needs_value(workloc_.size()-1, false);
1333 
1334  // Codegen the algorithm
1335  for (auto&& e : algorithm_) {
1336  // Generate comment
1337  if (g.verbose) {
1338  g << "/* #" << k << ": " << print(e) << " */\n";
1339  }
1340 
1341  // Get the names of the operation arguments
1342  arg.resize(e.arg.size());
1343  arg_is_ref.resize(e.arg.size());
1344  for (casadi_int i=0; i<e.arg.size(); ++i) {
1345  casadi_int j=e.arg.at(i);
1346  if (j>=0 && workloc_.at(j)!=workloc_.at(j+1)) {
1347  arg.at(i) = j;
1348  arg_is_ref.at(i) = work_is_ref.at(j);
1349  } else {
1350  arg.at(i) = -1;
1351  arg_is_ref.at(i) = false;
1352  }
1353  }
1354 
1355  // Get the names of the operation results
1356  res.resize(e.res.size());
1357  for (casadi_int i=0; i<e.res.size(); ++i) {
1358  casadi_int j=e.res.at(i);
1359  if (j>=0 && workloc_.at(j)!=workloc_.at(j+1)) {
1360  res.at(i) = j;
1361  } else {
1362  res.at(i) = -1;
1363  }
1364  }
1365 
1366  res_is_ref.resize(e.res.size());
1367  // By default, don't assume references
1368  std::fill(res_is_ref.begin(), res_is_ref.end(), false);
1369 
1370  if (print_instructions_ && e.op!=OP_INPUT && e.op!=OP_OUTPUT) {
1371  print_arg(g, k, e, arg, arg_is_ref);
1372  }
1373 
1374  // Generate operation
1375  e.data->generate(g, arg, res, arg_is_ref, res_is_ref);
1376 
1377  for (casadi_int i=0; i<e.res.size(); ++i) {
1378  casadi_int j=e.res.at(i);
1379  if (j>=0 && workloc_.at(j)!=workloc_.at(j+1)) {
1380  work_is_ref.at(j) = res_is_ref.at(i);
1381  if (res_is_ref.at(i)) {
1382  needs_reference[j] = true;
1383  } else {
1384  needs_value[j] = true;
1385  }
1386  }
1387  }
1388 
1389  if (print_instructions_ && e.op!=OP_INPUT && e.op!=OP_OUTPUT) {
1390  print_res(g, k, e, res, res_is_ref);
1391  }
1392 
1393  k++;
1394 
1395  }
1396 
1397  // Declare scalar work vector elements as local variables
1398  for (casadi_int i=0; i<workloc_.size()-1; ++i) {
1399  casadi_int n=workloc_[i+1]-workloc_[i];
1400  if (n==0) continue;
1401  /* Could use local variables for small work vector elements here, e.g.:
1402  ...
1403  } else if (n<10) {
1404  g << "w" << i << "[" << n << "]";
1405  } else {
1406  ...
1407  */
1408  if (!g.codegen_scalars && n==1) {
1409  g.local("w" + g.format_padded(i), "casadi_real");
1410  } else {
1411  if (needs_value[i]) {
1412  g.local("w" + g.format_padded(i), "casadi_real", "*");
1413  g.init_local("w" + g.format_padded(i), "w+" + str(workloc_[i]));
1414  }
1415  if (needs_reference[i]) {
1416  g.local("wr" + g.format_padded(i), "const casadi_real", "*");
1417  }
1418  }
1419  }
1420  }
1421 
1422  void MXFunction::generate_lifted(Function& vdef_fcn, Function& vinit_fcn) const {
1423  std::vector<MX> swork(workloc_.size()-1);
1424 
1425  std::vector<MX> arg1, res1;
1426 
1427  // Get input primitives
1428  std::vector<std::vector<MX> > in_split(in_.size());
1429  for (casadi_int i=0; i<in_.size(); ++i) in_split[i] = in_[i].primitives();
1430 
1431  // Definition of intermediate variables
1432  std::vector<MX> y;
1433  std::vector<MX> g;
1434  std::vector<std::vector<MX> > f_G(out_.size());
1435  for (casadi_int i=0; i<out_.size(); ++i) f_G[i].resize(out_[i].n_primitives());
1436 
1437  // Initial guess for intermediate variables
1438  std::vector<MX> x_init;
1439 
1440  // Temporary std::stringstream
1441  std::stringstream ss;
1442 
1443  for (casadi_int algNo=0; algNo<2; ++algNo) {
1444  for (auto&& e : algorithm_) {
1445  switch (e.op) {
1446  case OP_LIFT:
1447  {
1448  MX& arg = swork[e.arg.at(0)];
1449  MX& arg_init = swork[e.arg.at(1)];
1450  MX& res = swork[e.res.front()];
1451  switch (algNo) {
1452  case 0:
1453  ss.str(std::string());
1454  ss << "y" << y.size();
1455  y.push_back(MX::sym(ss.str(), arg.sparsity()));
1456  g.push_back(arg);
1457  res = y.back();
1458  break;
1459  case 1:
1460  x_init.push_back(arg_init);
1461  res = arg_init;
1462  break;
1463  }
1464  break;
1465  }
1466  case OP_INPUT:
1467  swork[e.res.front()] = in_split.at(e.data->ind()).at(e.data->segment());
1468  break;
1469  case OP_PARAMETER:
1470  swork[e.res.front()] = e.data;
1471  break;
1472  case OP_OUTPUT:
1473  if (algNo==0) {
1474  f_G.at(e.data->ind()).at(e.data->segment()) = swork[e.arg.front()];
1475  }
1476  break;
1477  default:
1478  {
1479  // Arguments of the operation
1480  arg1.resize(e.arg.size());
1481  for (casadi_int i=0; i<arg1.size(); ++i) {
1482  casadi_int el = e.arg[i]; // index of the argument
1483  arg1[i] = el<0 ? MX(e.data->dep(i).size()) : swork[el];
1484  }
1485 
1486  // Perform the operation
1487  res1.resize(e.res.size());
1488  e.data->eval_mx(arg1, res1);
1489 
1490  // Get the result
1491  for (casadi_int i=0; i<res1.size(); ++i) {
1492  casadi_int el = e.res[i]; // index of the output
1493  if (el>=0) swork[el] = res1[i];
1494  }
1495  }
1496  }
1497  }
1498  }
1499 
1500  // Definition of intermediate variables
1501  std::vector<MX> f_in = in_;
1502  f_in.insert(f_in.end(), y.begin(), y.end());
1503  std::vector<MX> f_out;
1504  for (casadi_int i=0; i<out_.size(); ++i) f_out.push_back(out_[i].join_primitives(f_G[i]));
1505  f_out.insert(f_out.end(), g.begin(), g.end());
1506  vdef_fcn = Function("lifting_variable_definition", f_in, f_out);
1507 
1508  // Initial guess of intermediate variables
1509  f_in = in_;
1510  f_out = x_init;
1511  vinit_fcn = Function("lifting_variable_guess", f_in, f_out);
1512  }
1513 
1514  const MX MXFunction::mx_in(casadi_int ind) const {
1515  return in_.at(ind);
1516  }
1517 
1518  const std::vector<MX> MXFunction::mx_in() const {
1519  return in_;
1520  }
1521 
1522  bool MXFunction::is_a(const std::string& type, bool recursive) const {
1523  return type=="MXFunction"
1524  || (recursive && XFunction<MXFunction,
1525  MX, MXNode>::is_a(type, recursive));
1526  }
1527 
1528  void MXFunction::substitute_inplace(std::vector<MX>& vdef, std::vector<MX>& ex) const {
1529  std::vector<MX> work(workloc_.size()-1);
1530  std::vector<MX> oarg, ores;
1531 
1532  // Output segments
1533  std::vector<std::vector<MX>> out_split(out_.size());
1534  for (casadi_int i = 0; i < out_split.size(); ++i) out_split[i].resize(out_[i].n_primitives());
1535 
1536  // Evaluate algorithm
1537  for (auto it=algorithm_.begin(); it!=algorithm_.end(); ++it) {
1538  switch (it->op) {
1539  case OP_INPUT:
1540  casadi_assert(it->data->segment()==0, "Not implemented");
1541  work.at(it->res.front())
1542  = out_.at(it->data->ind()).join_primitives(out_split.at(it->data->ind()));
1543  break;
1544  case OP_PARAMETER:
1545  case OP_CONST:
1546  work.at(it->res.front()) = it->data;
1547  break;
1548  case OP_OUTPUT:
1549  out_split.at(it->data->ind()).at(it->data->segment()) = work.at(it->arg.front());
1550  break;
1551  default:
1552  {
1553  // Arguments of the operation
1554  oarg.resize(it->arg.size());
1555  for (casadi_int i=0; i<oarg.size(); ++i) {
1556  casadi_int el = it->arg[i];
1557  oarg[i] = el<0 ? MX(it->data->dep(i).size()) : work.at(el);
1558  }
1559 
1560  // Perform the operation
1561  ores.resize(it->res.size());
1562  it->data->eval_mx(oarg, ores);
1563 
1564  // Get the result
1565  for (casadi_int i=0; i<ores.size(); ++i) {
1566  casadi_int el = it->res[i];
1567  if (el>=0) work.at(el) = ores[i];
1568  }
1569  }
1570  }
1571  }
1572  // Join primitives
1573  for (size_t k = 0; k < out_split.size(); ++k) {
1574  MX a = out_.at(k).join_primitives(out_split.at(k));
1575  if (k < vdef.size()) {
1576  vdef.at(k) = a;
1577  } else {
1578  ex.at(k - vdef.size()) = a;
1579  }
1580  }
1581  }
1582 
1583  bool MXFunction::should_inline(bool with_sx, bool always_inline, bool never_inline) const {
1584  // If inlining has been specified
1585  casadi_assert(!(always_inline && never_inline),
1586  "Inconsistent options for " + definition());
1587  casadi_assert(!(never_inline && has_free()),
1588  "Must inline " + definition());
1589  if (always_inline) return true;
1590  if (never_inline) return false;
1591  // Functions with free variables must be inlined
1592  if (has_free()) return true;
1593  // Default inlining only when called with sx
1594  return with_sx;
1595  }
1596 
1597  void MXFunction::export_code_body(const std::string& lang,
1598  std::ostream &ss, const Dict& options) const {
1599 
1600  // Default values for options
1601  casadi_int indent_level = 0;
1602 
1603  // Read options
1604  for (auto&& op : options) {
1605  if (op.first=="indent_level") {
1606  indent_level = op.second;
1607  } else {
1608  casadi_error("Unknown option '" + op.first + "'.");
1609  }
1610  }
1611 
1612  // Construct indent string
1613  std::string indent;
1614  for (casadi_int i=0;i<indent_level;++i) {
1615  indent += " ";
1616  }
1617 
1618  Function f = shared_from_this<Function>();
1619 
1620  // Loop over algorithm
1621  for (casadi_int k=0;k<f.n_instructions();++k) {
1622  // Get operation
1623  casadi_int op = static_cast<casadi_int>(f.instruction_id(k));
1624  // Get MX node
1625  MX x = f.instruction_MX(k);
1626  // Get input positions into workvector
1627  std::vector<casadi_int> o = f.instruction_output(k);
1628  // Get output positions into workvector
1629  std::vector<casadi_int> i = f.instruction_input(k);
1630 
1631  switch (op) {
1632  case OP_INPUT:
1633  ss << indent << "w" << o[0] << " = varargin{" << i[0]+1 << "};" << std::endl;
1634  break;
1635  case OP_OUTPUT:
1636  {
1637  Dict info = x.info();
1638  casadi_int segment = info["segment"];
1639  x.dep(0).sparsity().export_code("matlab", ss,
1640  {{"name", "sp_in"}, {"indent_level", indent_level}, {"as_matrix", true}});
1641  ss << indent << "argout_" << o[0] << "{" << (1+segment) << "} = ";
1642  ss << "w" << i[0] << "(sp_in==1);" << std::endl;
1643  }
1644  break;
1645  case OP_CONST:
1646  {
1647  DM v = static_cast<DM>(x);
1648  Dict opts;
1649  opts["name"] = "m";
1650  opts["indent_level"] = indent_level;
1651  v.export_code("matlab", ss, opts);
1652  ss << indent << "w" << o[0] << " = m;" << std::endl;
1653  }
1654  break;
1655  case OP_SQ:
1656  ss << indent << "w" << o[0] << " = " << "w" << i[0] << ".^2;" << std::endl;
1657  break;
1658  case OP_MTIMES:
1659  ss << indent << "w" << o[0] << " = ";
1660  ss << "w" << i[1] << "*w" << i[2] << "+w" << i[0] << ";" << std::endl;
1661  break;
1662  case OP_MUL:
1663  {
1664  std::string prefix = (x.dep(0).is_scalar() || x.dep(1).is_scalar()) ? "" : ".";
1665  ss << indent << "w" << o[0] << " = " << "w" << i[0] << prefix << "*w" << i[1] << ";";
1666  ss << std::endl;
1667  }
1668  break;
1669  case OP_TWICE:
1670  ss << indent << "w" << o[0] << " = 2*w" << i[0] << ";" << std::endl;
1671  break;
1672  case OP_INV:
1673  ss << indent << "w" << o[0] << " = 1./w" << i[0] << ";" << std::endl;
1674  break;
1675  case OP_DOT:
1676  ss << indent << "w" << o[0] << " = dot(w" << i[0] << ",w" << i[1]<< ");" << std::endl;
1677  break;
1678  case OP_BILIN:
1679  ss << indent << "w" << o[0] << " = w" << i[1] << ".'*w" << i[0]<< "*w" << i[2] << ";";
1680  ss << std::endl;
1681  break;
1682  case OP_RANK1:
1683  ss << indent << "w" << o[0] << " = w" << i[0] << "+";
1684  ss << "w" << i[1] << "*w" << i[2] << "*w" << i[3] << ".';";
1685  ss << std::endl;
1686  break;
1687  case OP_FABS:
1688  ss << indent << "w" << o[0] << " = abs(w" << i[0] << ");" << std::endl;
1689  break;
1690  case OP_DETERMINANT:
1691  ss << indent << "w" << o[0] << " = det(w" << i[0] << ");" << std::endl;
1692  break;
1693  case OP_INVERSE:
1694  ss << indent << "w" << o[0] << " = inv(w" << i[0] << ");";
1695  ss << "w" << o[0] << "(w" << o[0] << "==0) = 1e-200;" << std::endl;
1696  break;
1697  case OP_SOLVE:
1698  {
1699  bool tr = x.info()["tr"];
1700  if (tr) {
1701  ss << indent << "w" << o[0] << " = ((w" << i[1] << ".')\\w" << i[0] << ").';";
1702  ss << std::endl;
1703  } else {
1704  ss << indent << "w" << o[0] << " = w" << i[1] << "\\w" << i[0] << ";" << std::endl;
1705  }
1706  ss << "w" << o[0] << "(w" << o[0] << "==0) = 1e-200;" << std::endl;
1707  }
1708  break;
1709  case OP_DIV:
1710  {
1711  std::string prefix = (x.dep(0).is_scalar() || x.dep(1).is_scalar()) ? "" : ".";
1712  ss << indent << "w" << o[0] << " = " << "w" << i[0] << prefix << "/w" << i[1] << ";";
1713  ss << std::endl;
1714  }
1715  break;
1716  case OP_POW:
1717  case OP_CONSTPOW:
1718  ss << indent << "w" << o[0] << " = " << "w" << i[0] << ".^w" << i[1] << ";" << std::endl;
1719  break;
1720  case OP_TRANSPOSE:
1721  ss << indent << "w" << o[0] << " = " << "w" << i[0] << ".';" << std::endl;
1722  break;
1723  case OP_HORZCAT:
1724  case OP_VERTCAT:
1725  {
1726  ss << indent << "w" << o[0] << " = [";
1727  for (casadi_int e : i) {
1728  ss << "w" << e << (op==OP_HORZCAT ? " " : ";");
1729  }
1730  ss << "];" << std::endl;
1731  }
1732  break;
1733  case OP_DIAGCAT:
1734  {
1735  for (casadi_int k=0;k<i.size();++k) {
1736  x.dep(k).sparsity().export_code("matlab", ss,
1737  {{"name", "sp_in" + str(k)}, {"indent_level", indent_level}, {"as_matrix", true}});
1738  }
1739  ss << indent << "w" << o[0] << " = [";
1740  for (casadi_int k=0;k<i.size();++k) {
1741  ss << "w" << i[k] << "(sp_in" << k << "==1);";
1742  }
1743  ss << "];" << std::endl;
1744  Dict opts;
1745  opts["name"] = "sp";
1746  opts["indent_level"] = indent_level;
1747  opts["as_matrix"] = false;
1748  x.sparsity().export_code("matlab", ss, opts);
1749  ss << indent << "w" << o[0] << " = ";
1750  ss << "sparse(sp_i, sp_j, w" << o[0] << ", sp_m, sp_n);" << std::endl;
1751  }
1752  break;
1753  case OP_HORZSPLIT:
1754  case OP_VERTSPLIT:
1755  {
1756  Dict info = x.info();
1757  std::vector<casadi_int> offset = info["offset"];
1758  casadi::Function output = info["output"];
1759  std::vector<Sparsity> sp;
1760  for (casadi_int i=0;i<output.n_out();i++)
1761  sp.push_back(output.sparsity_out(i));
1762  for (casadi_int k=0;k<o.size();++k) {
1763  if (o[k]==-1) continue;
1764  x.dep(0).sparsity().export_code("matlab", ss,
1765  {{"name", "sp_in"}, {"indent_level", indent_level}, {"as_matrix", true}});
1766  ss << indent << "tmp = w" << i[0]<< "(sp_in==1);" << std::endl;
1767  Dict opts;
1768  opts["name"] = "sp";
1769  opts["indent_level"] = indent_level;
1770  opts["as_matrix"] = false;
1771  sp[k].export_code("matlab", ss, opts);
1772  ss << indent << "w" << o[k] << " = sparse(sp_i, sp_j, ";
1773  ss << "tmp(" << offset[k]+1 << ":" << offset[k+1] << "), sp_m, sp_n);" << std::endl;
1774  }
1775  }
1776  break;
1777  case OP_GETNONZEROS:
1778  case OP_SETNONZEROS:
1779  {
1780  Dict info = x.info();
1781 
1782  std::string nonzeros;
1783  if (info.find("nz")!=info.end()) {
1784  nonzeros = "1+" + str(info["nz"]);
1785  } else if (info.find("slice")!=info.end()) {
1786  Dict s = info["slice"];
1787  casadi_int start = s["start"];
1788  casadi_int step = s["step"];
1789  casadi_int stop = s["stop"];
1790  nonzeros = str(start+1) + ":" + str(step) + ":" + str(stop);
1791  nonzeros = "nonzeros(" + nonzeros + ")";
1792  } else {
1793  Dict inner = info["inner"];
1794  Dict outer = info["outer"];
1795  casadi_int inner_start = inner["start"];
1796  casadi_int inner_step = inner["step"];
1797  casadi_int inner_stop = inner["stop"];
1798  casadi_int outer_start = outer["start"];
1799  casadi_int outer_step = outer["step"];
1800  casadi_int outer_stop = outer["stop"];
1801  std::string inner_slice = "(" + str(inner_start) + ":" +
1802  str(inner_step) + ":" + str(inner_stop-1)+")";
1803  std::string outer_slice = "(" + str(outer_start+1) + ":" +
1804  str(outer_step) + ":" + str(outer_stop)+")";
1805  casadi_int N = range(outer_start, outer_stop, outer_step).size();
1806  casadi_int M = range(inner_start, inner_stop, inner_step).size();
1807  nonzeros = "repmat("+ inner_slice +"', 1, " + str(N) + ")+" +
1808  "repmat("+ outer_slice +", " + str(M) + ", 1)";
1809  nonzeros = "nonzeros(" + nonzeros + ")";
1810  }
1811 
1812  Dict opts;
1813  opts["name"] = "sp";
1814  opts["indent_level"] = indent_level;
1815  opts["as_matrix"] = false;
1816  x.sparsity().export_code("matlab", ss, opts);
1817 
1818  if (op==OP_GETNONZEROS) {
1819  x.dep(0).sparsity().export_code("matlab", ss,
1820  {{"name", "sp_in"}, {"indent_level", indent_level}, {"as_matrix", true}});
1821  //ss << indent << "w" << i[0] << "" << std::endl;
1822  //ss << indent << "size(w" << i[0] << ")" << std::endl;
1823  ss << indent << "in_flat = w" << i[0] << "(sp_in==1);" << std::endl;
1824  //ss << indent << "in_flat" << std::endl;
1825  //ss << indent << "size(in_flat)" << std::endl;
1826  ss << indent << "w" << o[0] << " = in_flat(" << nonzeros << ");" << std::endl;
1827  } else {
1828  x.dep(0).sparsity().export_code("matlab", ss,
1829  {{"name", "sp_in0"}, {"indent_level", indent_level}, {"as_matrix", true}});
1830  x.dep(1).sparsity().export_code("matlab", ss,
1831  {{"name", "sp_in1"}, {"indent_level", indent_level}, {"as_matrix", true}});
1832  ss << indent << "in_flat = w" << i[1] << "(sp_in1==1);" << std::endl;
1833  ss << indent << "w" << o[0] << " = w" << i[0] << "(sp_in0==1);" << std::endl;
1834  ss << indent << "w" << o[0] << "(" << nonzeros << ") = ";
1835  if (info["add"]) ss << "w" << o[0] << "(" << nonzeros << ") + ";
1836  ss << "in_flat;";
1837  }
1838  ss << indent << "w" << o[0] << " = ";
1839  ss << "sparse(sp_i, sp_j, w" << o[0] << ", sp_m, sp_n);" << std::endl;
1840  }
1841  break;
1842  case OP_PROJECT:
1843  {
1844  Dict opts;
1845  opts["name"] = "sp";
1846  opts["indent_level"] = indent_level;
1847  x.sparsity().export_code("matlab", ss, opts);
1848  ss << indent << "w" << o[0] << " = ";
1849  ss << "sparse(sp_i, sp_j, w" << i[0] << "(sp==1), sp_m, sp_n);" << std::endl;
1850  }
1851  break;
1852  case OP_NORM1:
1853  ss << indent << "w" << o[0] << " = norm(w" << i[0] << ", 1);" << std::endl;
1854  break;
1855  case OP_NORM2:
1856  ss << indent << "w" << o[0] << " = norm(w" << i[0] << ", 2);" << std::endl;
1857  break;
1858  case OP_NORMF:
1859  ss << indent << "w" << o[0] << " = norm(w" << i[0] << ", 'fro');" << std::endl;
1860  break;
1861  case OP_NORMINF:
1862  ss << indent << "w" << o[0] << " = norm(w" << i[0] << ", inf);" << std::endl;
1863  break;
1864  case OP_MMIN:
1865  ss << indent << "w" << o[0] << " = min(w" << i[0] << ");" << std::endl;
1866  break;
1867  case OP_MMAX:
1868  ss << indent << "w" << o[0] << " = max(w" << i[0] << ");" << std::endl;
1869  break;
1870  case OP_NOT:
1871  ss << indent << "w" << o[0] << " = ~" << "w" << i[0] << ";" << std::endl;
1872  break;
1873  case OP_OR:
1874  ss << indent << "w" << o[0] << " = w" << i[0] << " | w" << i[1] << ";" << std::endl;
1875  break;
1876  case OP_AND:
1877  ss << indent << "w" << o[0] << " = w" << i[0] << " & w" << i[1] << ";" << std::endl;
1878  break;
1879  case OP_NE:
1880  ss << indent << "w" << o[0] << " = w" << i[0] << " ~= w" << i[1] << ";" << std::endl;
1881  break;
1882  case OP_IF_ELSE_ZERO:
1883  ss << indent << "w" << o[0] << " = ";
1884  ss << "if_else_zero_gen(w" << i[0] << ", w" << i[1] << ");" << std::endl;
1885  break;
1886  case OP_RESHAPE:
1887  {
1888  x.dep(0).sparsity().export_code("matlab", ss,
1889  {{"name", "sp_in"}, {"indent_level", indent_level}, {"as_matrix", true}});
1890  x.sparsity().export_code("matlab", ss,
1891  {{"name", "sp_out"}, {"indent_level", indent_level}, {"as_matrix", false}});
1892  ss << indent << "w" << o[0] << " = sparse(sp_out_i, sp_out_j, ";
1893  ss << "w" << i[0] << "(sp_in==1), sp_out_m, sp_out_n);" << std::endl;
1894  }
1895  break;
1896  default:
1897  if (x.is_binary()) {
1898  ss << indent << "w" << o[0] << " = " << casadi::casadi_math<double>::print(op,
1899  "w"+std::to_string(i[0]), "w"+std::to_string(i[1])) << ";" << std::endl;
1900  } else if (x.is_unary()) {
1901  ss << indent << "w" << o[0] << " = " << casadi::casadi_math<double>::print(op,
1902  "w"+std::to_string(i[0])) << ";" << std::endl;
1903  } else {
1904  ss << "unknown" + x.class_name() << std::endl;
1905  }
1906  }
1907  }
1908  }
1909 
1910  Dict MXFunction::get_stats(void* mem) const {
1911  Dict stats = XFunction::get_stats(mem);
1912 
1913  Function dep;
1914  for (auto&& e : algorithm_) {
1915  if (e.op==OP_CALL) {
1916  Function d = e.data.which_function();
1917  if (d.is_a("Conic", true) || d.is_a("Nlpsol")) {
1918  if (!dep.is_null()) return stats;
1919  dep = d;
1920  }
1921  }
1922  }
1923  if (dep.is_null()) return stats;
1924  return dep.stats(1);
1925  }
1926 
1929 
1930  s.version("MXFunction", 2);
1931  s.pack("MXFunction::n_instr", algorithm_.size());
1932 
1933  // Loop over algorithm
1934  for (const auto& e : algorithm_) {
1935  s.pack("MXFunction::alg::data", e.data);
1936  s.pack("MXFunction::alg::arg", e.arg);
1937  s.pack("MXFunction::alg::res", e.res);
1938  }
1939 
1940  s.pack("MXFunction::workloc", workloc_);
1941  s.pack("MXFunction::free_vars", free_vars_);
1942  s.pack("MXFunction::default_in", default_in_);
1943  s.pack("MXFunction::live_variables", live_variables_);
1944  s.pack("MXFunction::print_instructions", print_instructions_);
1945 
1947  }
1948 
1949 
1951  int version = s.version("MXFunction", 1, 2);
1952  size_t n_instructions;
1953  s.unpack("MXFunction::n_instr", n_instructions);
1954  algorithm_.resize(n_instructions);
1955  for (casadi_int k=0;k<n_instructions;++k) {
1956  AlgEl& e = algorithm_[k];
1957  s.unpack("MXFunction::alg::data", e.data);
1958  e.op = e.data.op();
1959  s.unpack("MXFunction::alg::arg", e.arg);
1960  s.unpack("MXFunction::alg::res", e.res);
1961  }
1962 
1963  s.unpack("MXFunction::workloc", workloc_);
1964  s.unpack("MXFunction::free_vars", free_vars_);
1965  s.unpack("MXFunction::default_in", default_in_);
1966  s.unpack("MXFunction::live_variables", live_variables_);
1967  print_instructions_ = false;
1968  if (version >= 2) s.unpack("MXFunction::print_instructions", print_instructions_);
1969 
1971  }
1972 
1974  return new MXFunction(s);
1975  }
1976 
1977  void MXFunction::find(std::map<FunctionInternal*, std::pair<Function, size_t> >& all_fun,
1978  casadi_int max_depth) const {
1979  // Call to base class
1980  FunctionInternal::find(all_fun, max_depth);
1981  for (auto&& e : algorithm_) {
1982  if (e.op == OP_CALL) add_embedded(all_fun, e.data.which_function(), max_depth);
1983  }
1984  }
1985 
1986  void MXFunction::change_option(const std::string& option_name,
1987  const GenericType& option_value) {
1988  if (option_name == "print_instructions") {
1989  print_instructions_ = option_value;
1990  } else {
1991  // Option not found - continue to base classes
1992  XFunction<MXFunction, MX, MXNode>::change_option(option_name, option_value);
1993  }
1994  }
1995 
1996  std::vector<MX> MXFunction::order(const std::vector<MX>& expr) {
1997 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
1998  std::lock_guard<std::mutex> lock(MX::get_mutex_temp());
1999 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
2000  // Stack used to sort the computational graph
2001  std::stack<MXNode*> s;
2002 
2003  // All nodes
2004  std::vector<MXNode*> nodes;
2005 
2006  // Add the list of nodes
2007  for (casadi_int ind=0; ind<expr.size(); ++ind) {
2008  // Loop over primitives of each output
2009  std::vector<MX> prim = expr[ind].primitives();
2010  for (casadi_int p=0; p<prim.size(); ++p) {
2011  // Get the nodes using a depth first search
2012  s.push(prim[p].get());
2014  }
2015  }
2016 
2017  // Clear temporary markers
2018  for (casadi_int i=0; i<nodes.size(); ++i) {
2019  nodes[i]->temp = 0;
2020  }
2021 
2022  std::vector<MX> ret(nodes.size());
2023  for (casadi_int i=0; i<nodes.size(); ++i) {
2024  ret[i].own(nodes[i]);
2025  }
2026 
2027  return ret;
2028  }
2029 
2030 } // 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.
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 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.
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.
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.
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 Sparsity & sparsity_out(casadi_int ind) const
Get sparsity of a given output.
Definition: function.cpp:1183
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
casadi_int n_out() const
Get the number of function outputs.
Definition: function.cpp:975
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.
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 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.
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)
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.
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:90
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: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: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
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