x_function.hpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 
26 #ifndef CASADI_X_FUNCTION_HPP
27 #define CASADI_X_FUNCTION_HPP
28 
29 #include <stack>
30 #include "function_internal.hpp"
31 #include "factory.hpp"
32 #include "serializing_stream.hpp"
33 
34 // To reuse variables we need to be able to sort by sparsity pattern
35 #include <unordered_map>
36 #define SPARSITY_MAP std::unordered_map
37 
38 // Throw informative error message
39 #define CASADI_THROW_ERROR(FNAME, WHAT) \
40 throw CasadiException("Error in XFunction::" FNAME " for '" + this->name_ + "' "\
41  "[" + this->class_name() + "] at " + CASADI_WHERE + ":\n"\
42  + std::string(WHAT));
43 
45 
46 namespace casadi {
47 
56  template<typename DerivedType, typename MatType, typename NodeType>
57  class CASADI_EXPORT XFunction : public FunctionInternal {
58  public:
59 
63  XFunction(const std::string& name,
64  const std::vector<MatType>& ex_in,
65  const std::vector<MatType>& ex_out,
66  const std::vector<std::string>& name_in,
67  const std::vector<std::string>& name_out);
68 
72  ~XFunction() override {
73  }
74 
78  void init(const Dict& opts) override;
79 
82  bool has_spfwd() const override { return true;}
83  bool has_sprev() const override { return true;}
85 
93  static void sort_depth_first(std::stack<NodeType*>& s, std::vector<NodeType*>& nodes);
94 
98  std::vector<MatType> jac(const Dict& opts) const;
99 
103  bool is_a(const std::string& type, bool recursive) const override {
104  return type=="xfunction" || (recursive && FunctionInternal::is_a(type, recursive));
105  }
106 
107  // Factory
108  Function factory(const std::string& name,
109  const std::vector<std::string>& s_in,
110  const std::vector<std::string>& s_out,
111  const Function::AuxOut& aux,
112  const Dict& opts) const override;
113 
120  std::vector<bool> which_depends(const std::string& s_in,
121  const std::vector<std::string>& s_out,
122  casadi_int order, bool tr=false) const override;
123 
125 
128  bool has_forward(casadi_int nfwd) const override { return true;}
129  Function get_forward(casadi_int nfwd, const std::string& name,
130  const std::vector<std::string>& inames,
131  const std::vector<std::string>& onames,
132  const Dict& opts) const override;
134 
136 
139  bool has_reverse(casadi_int nadj) const override { return true;}
140  Function get_reverse(casadi_int nadj, const std::string& name,
141  const std::vector<std::string>& inames,
142  const std::vector<std::string>& onames,
143  const Dict& opts) const override;
145 
147 
150  bool has_jacobian() const override { return true;}
151  Function get_jacobian(const std::string& name,
152  const std::vector<std::string>& inames,
153  const std::vector<std::string>& onames,
154  const Dict& opts) const override;
156 
160  Function slice(const std::string& name, const std::vector<casadi_int>& order_in,
161  const std::vector<casadi_int>& order_out, const Dict& opts) const override;
162 
166  Function simplify_passes(
167  const std::vector<std::pair<std::string, casadi_int> >& tasks) const override;
168 
175  void apply_simplify_passes(
176  const std::vector<std::pair<std::string, casadi_int> >& tasks,
177  std::vector<MatType>& new_in,
178  std::vector<MatType>& new_out) const;
179 
183  void codegen_declarations(CodeGenerator& g) const override = 0;
184 
188  void codegen_body(CodeGenerator& g) const override = 0;
189 
193  void export_code(const std::string& lang,
194  std::ostream &stream, const Dict& options) const override;
195 
199  virtual void export_code_body(const std::string& lang,
200  std::ostream &stream, const Dict& options) const = 0;
201 
205  bool has_codegen() const override { return true;}
206 
210  virtual bool isInput(const std::vector<MatType>& arg) const;
211 
213  virtual bool should_inline(bool with_sx, bool always_inline, bool never_inline) const = 0;
214 
218  void call_forward(const std::vector<MatType>& arg,
219  const std::vector<MatType>& res,
220  const std::vector<std::vector<MatType> >& fseed,
221  std::vector<std::vector<MatType> >& fsens,
222  bool always_inline, bool never_inline) const override;
223 
227  void call_reverse(const std::vector<MatType>& arg,
228  const std::vector<MatType>& res,
229  const std::vector<std::vector<MatType> >& aseed,
230  std::vector<std::vector<MatType> >& asens,
231  bool always_inline, bool never_inline) const override;
232 
234 
237  size_t get_n_in() override { return in_.size(); }
238  size_t get_n_out() override { return out_.size(); }
240 
242 
245  Sparsity get_sparsity_in(casadi_int i) override { return in_.at(i).sparsity();}
246  Sparsity get_sparsity_out(casadi_int i) override { return out_.at(i).sparsity();}
248 
252  explicit XFunction(DeserializingStream& s);
256  void serialize_body(SerializingStream &s) const override;
257 
265  void delayed_serialize_members(SerializingStream &s) const;
266  void delayed_deserialize_members(DeserializingStream &s);
268 
269  // Data members (all public)
270 
274  std::vector<MatType> in_;
275 
279  std::vector<MatType> out_;
280  };
281 
282  // Template implementations
283 
284  template<typename DerivedType, typename MatType, typename NodeType>
285  XFunction<DerivedType, MatType, NodeType>::
286  XFunction(const std::string& name,
287  const std::vector<MatType>& ex_in,
288  const std::vector<MatType>& ex_out,
289  const std::vector<std::string>& name_in,
290  const std::vector<std::string>& name_out)
291  : FunctionInternal(name), in_(ex_in), out_(ex_out) {
292  // Names of inputs
293  if (!name_in.empty()) {
294  casadi_assert(ex_in.size()==name_in.size(),
295  "Mismatching number of input names");
296  name_in_ = name_in;
297  }
298  // Names of outputs
299  if (!name_out.empty()) {
300  casadi_assert(ex_out.size()==name_out.size(),
301  "Mismatching number of output names");
302  name_out_ = name_out;
303  }
304  }
305 
306  template<typename DerivedType, typename MatType, typename NodeType>
307  XFunction<DerivedType, MatType, NodeType>::
308  XFunction(DeserializingStream& s) : FunctionInternal(s) {
309  s.version("XFunction", 1);
310  s.unpack("XFunction::in", in_);
311  // 'out' member needs to be delayed
312  }
313 
314  template<typename DerivedType, typename MatType, typename NodeType>
315  void XFunction<DerivedType, MatType, NodeType>::
316  delayed_deserialize_members(DeserializingStream& s) {
317  s.unpack("XFunction::out", out_);
318  }
319 
320  template<typename DerivedType, typename MatType, typename NodeType>
321  void XFunction<DerivedType, MatType, NodeType>::
322  delayed_serialize_members(SerializingStream& s) const {
323  s.pack("XFunction::out", out_);
324  }
325 
326  template<typename DerivedType, typename MatType, typename NodeType>
327  void XFunction<DerivedType, MatType, NodeType>::
328  serialize_body(SerializingStream& s) const {
329  FunctionInternal::serialize_body(s);
330  s.version("XFunction", 1);
331  s.pack("XFunction::in", in_);
332  // 'out' member needs to be delayed
333  }
334 
335  template<typename DerivedType, typename MatType, typename NodeType>
336  void XFunction<DerivedType, MatType, NodeType>::init(const Dict& opts) {
337  // Call the init function of the base class
338  FunctionInternal::init(opts);
339 
340  bool allow_duplicate_io_names = false;
341  // Read options
342  for (auto&& op : opts) {
343  if (op.first=="allow_duplicate_io_names") {
344  allow_duplicate_io_names = op.second;
345  }
346  }
347 
348  if (verbose_) casadi_message(name_ + "::init");
349  // Make sure that inputs are symbolic
350  for (casadi_int i=0; i<n_in_; ++i) {
351  if (!in_.at(i).is_valid_input()) {
352  casadi_error("For " + this->name_ + ": Xfunction input arguments must be purely symbolic."
353  "\nArgument " + str(i) + "(" + name_in_[i] + ") is not symbolic.");
354  }
355  }
356 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
357  std::lock_guard<std::mutex> lock(MatType::get_mutex_temp());
358 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
359 
360  // Check for duplicate entries among the input expressions
361  bool has_duplicates = false;
362  for (auto&& i : in_) {
363  if (i.has_duplicates()) {
364  has_duplicates = true;
365  break;
366  }
367  }
368  // Reset temporaries
369  for (auto&& i : in_) i.reset_input();
370  // Generate error
371  if (has_duplicates) {
372  std::stringstream s;
373  s << "The input expressions are not independent:\n";
374  for (casadi_int iind=0; iind<in_.size(); ++iind) {
375  s << iind << ": " << in_[iind] << "\n";
376  }
377  casadi_error(s.str());
378  }
379 
380  if (!allow_duplicate_io_names) {
381  // Collect hashes for all inputs and outputs
382  std::hash<std::string> hasher;
383  std::vector<size_t> iohash;
384  iohash.reserve(name_in_.size() + name_out_.size());
385  for (const std::string& s : name_in_) iohash.push_back(hasher(s));
386  for (const std::string& s : name_out_) iohash.push_back(hasher(s));
387  std::sort(iohash.begin(), iohash.end());
388  // Look for duplicates
389  size_t prev = -1;
390  for (size_t h : iohash) {
391  if (h == prev) {
392  // Hash duplicate found, collect strings
393  std::vector<std::string> io_names;
394  io_names.reserve(iohash.size());
395  for (const std::string& s : name_in_) io_names.push_back(s);
396  for (const std::string& s : name_out_) io_names.push_back(s);
397  std::sort(io_names.begin(), io_names.end());
398  // Look for duplicates
399  std::string prev;
400  for (std::string h : io_names) {
401  if (h == prev) casadi_error("Duplicate IO name: " + h + ". "
402  "To ignore this error, set 'allow_duplicate_io_names' option.");
403  prev = h;
404  }
405  }
406  prev = h;
407  }
408  }
409  }
410 
411  template<typename DerivedType, typename MatType, typename NodeType>
412  void XFunction<DerivedType, MatType, NodeType>::sort_depth_first(
413  std::stack<NodeType*>& s, std::vector<NodeType*>& nodes) {
414  while (!s.empty()) {
415  // Get the topmost element
416  NodeType* t = s.top();
417  // If the last element on the stack has not yet been added
418  if (t && t->temp>=0) {
419  // Get the index of the next dependency
420  casadi_int next_dep = t->temp++;
421  // If there is any dependency which has not yet been added
422  if (next_dep < t->n_dep()) {
423  // Add dependency to stack
424  s.push(static_cast<NodeType*>(t->dep(next_dep).get()));
425  } else {
426  // if no dependencies need to be added, we can add the node to the algorithm
427  nodes.push_back(t);
428  // Mark the node as found
429  t->temp = -1;
430  // Remove from stack
431  s.pop();
432  }
433  } else {
434  // If the last element on the stack has already been added
435  s.pop();
436  }
437  }
438  }
439 
440  template<typename DerivedType, typename MatType, typename NodeType>
441  std::vector<MatType> XFunction<DerivedType, MatType, NodeType>
442  ::jac(const Dict& opts) const {
443  try {
444  // Read options
445  bool compact = false;
446  bool symmetric = false;
447  bool allow_forward = true;
448  bool allow_reverse = true;
449  for (auto&& op : opts) {
450  if (op.first=="compact") {
451  compact = op.second;
452  } else if (op.first=="symmetric") {
453  symmetric = op.second;
454  } else if (op.first=="allow_forward") {
455  allow_forward = op.second;
456  } else if (op.first=="allow_reverse") {
457  allow_reverse = op.second;
458  } else if (op.first=="verbose") {
459  continue;
460  } else {
461  casadi_error("No such Jacobian option: " + std::string(op.first));
462  }
463  }
464 
465  // Return object
466  std::vector<MatType> ret(n_in_ * n_out_);
467 
468  // Quick return if trivially empty
469  if (nnz_in() == 0 || nnz_out() == 0) {
470  for (casadi_int i = 0; i < n_out_; ++i) {
471  for (casadi_int j = 0; j < n_in_; ++j) {
472  if (compact) {
473  ret[i * n_in_ + j] = MatType(nnz_out(i), nnz_in(j));
474  } else {
475  ret[i * n_in_ + j] = MatType(numel_out(i), numel_in(j));
476  }
477  }
478  }
479  return ret;
480  }
481 
482  // FIXME(@jaeandersson)
483  casadi_int iind = 0, oind = 0;
484  casadi_assert(n_in_>=1 && is_diff_in_[0], "Not implemented");
485  casadi_assert(n_in_ == 1 || !any(vector_tail(is_diff_in_)), "Not implemented");
486  casadi_assert(n_out_ == 1, "Not implemented");
487 
488  // Create return object
489  ret.at(0) = MatType::zeros(jac_sparsity(0, 0, false, symmetric).T());
490  if (verbose_) casadi_message("Allocated return value");
491 
492  // Quick return if empty
493  if (ret.at(0).nnz()==0) {
494  ret.at(0) = ret.at(0).T();
495  return ret;
496  }
497 
498  // Get a bidirectional partition
499  Sparsity D1, D2;
500  get_partition(iind, oind, D1, D2, true, symmetric, allow_forward, allow_reverse);
501  if (verbose_) casadi_message("Graph coloring completed");
502 
503  // Get the number of forward and adjoint sweeps
504  casadi_int nfdir = D1.is_null() ? 0 : D1.size2();
505  casadi_int nadir = D2.is_null() ? 0 : D2.size2();
506 
507  // Number of derivative directions supported by the function
508  casadi_int max_nfdir = max_num_dir_;
509  casadi_int max_nadir = max_num_dir_;
510 
511  // Current forward and adjoint direction
512  casadi_int offset_nfdir = 0, offset_nadir = 0;
513 
514  // Evaluation result (known)
515  std::vector<MatType> res(out_);
516 
517  // Forward and adjoint seeds and sensitivities
518  std::vector<std::vector<MatType> > fseed, aseed, fsens, asens;
519 
520  // Get the sparsity of the Jacobian block
521  Sparsity jsp = jac_sparsity(0, 0, true, symmetric).T();
522  const casadi_int* jsp_colind = jsp.colind();
523  const casadi_int* jsp_row = jsp.row();
524 
525  // Input sparsity
526  std::vector<casadi_int> input_col = sparsity_in_.at(iind).get_col();
527  const casadi_int* input_row = sparsity_in_.at(iind).row();
528 
529  // Output sparsity
530  std::vector<casadi_int> output_col = sparsity_out_.at(oind).get_col();
531  const casadi_int* output_row = sparsity_out_.at(oind).row();
532 
533  // Get transposes and mappings for jacobian sparsity pattern if we are using forward mode
534  if (verbose_) casadi_message("jac transposes and mapping");
535  std::vector<casadi_int> mapping;
536  Sparsity jsp_trans;
537  if (nfdir>0) {
538  jsp_trans = jsp.transpose(mapping);
539  }
540 
541  // The nonzeros of the sensitivity matrix
542  std::vector<casadi_int> nzmap, nzmap2;
543 
544  // Additions to the jacobian matrix
545  std::vector<casadi_int> adds, adds2;
546 
547  // Temporary vector
548  std::vector<casadi_int> tmp;
549 
550  // Progress
551  casadi_int progress = -10;
552 
553  // Number of sweeps
554  casadi_int nsweep_fwd = nfdir/max_nfdir; // Number of sweeps needed for the forward mode
555  if (nfdir%max_nfdir>0) nsweep_fwd++;
556  casadi_int nsweep_adj = nadir/max_nadir; // Number of sweeps needed for the adjoint mode
557  if (nadir%max_nadir>0) nsweep_adj++;
558  casadi_int nsweep = std::max(nsweep_fwd, nsweep_adj);
559  if (verbose_) {
560  casadi_message(str(nsweep) + " sweeps needed for " + str(nfdir) + " forward and "
561  + str(nadir) + " reverse directions");
562  }
563 
564  // Sparsity of the seeds
565  std::vector<casadi_int> seed_col, seed_row;
566 
567  // Evaluate until everything has been determined
568  for (casadi_int s=0; s<nsweep; ++s) {
569  // Print progress
570  if (verbose_) {
571  casadi_int progress_new = (s*100)/nsweep;
572  // Print when entering a new decade
573  if (progress_new / 10 > progress / 10) {
574  progress = progress_new;
575  casadi_message(str(progress) + " %");
576  }
577  }
578 
579  // Number of forward and adjoint directions in the current "batch"
580  casadi_int nfdir_batch = std::min(nfdir - offset_nfdir, max_nfdir);
581  casadi_int nadir_batch = std::min(nadir - offset_nadir, max_nadir);
582 
583  // Forward seeds
584  fseed.resize(nfdir_batch);
585  for (casadi_int d=0; d<nfdir_batch; ++d) {
586  // Nonzeros of the seed matrix
587  seed_col.clear();
588  seed_row.clear();
589 
590  // For all the directions
591  for (casadi_int el = D1.colind(offset_nfdir+d); el<D1.colind(offset_nfdir+d+1); ++el) {
592 
593  // Get the direction
594  casadi_int c = D1.row(el);
595 
596  // Give a seed in the direction
597  seed_col.push_back(input_col[c]);
598  seed_row.push_back(input_row[c]);
599  }
600 
601  // initialize to zero
602  fseed[d].resize(n_in_);
603  for (casadi_int ind=0; ind<fseed[d].size(); ++ind) {
604  casadi_int nrow = size1_in(ind), ncol = size2_in(ind); // Input dimensions
605  if (ind==iind) {
606  fseed[d][ind] = MatType::ones(Sparsity::triplet(nrow, ncol, seed_row, seed_col));
607  } else {
608  fseed[d][ind] = MatType(nrow, ncol);
609  }
610  }
611  }
612 
613  // Adjoint seeds
614  aseed.resize(nadir_batch);
615  for (casadi_int d=0; d<nadir_batch; ++d) {
616  // Nonzeros of the seed matrix
617  seed_col.clear();
618  seed_row.clear();
619 
620  // For all the directions
621  for (casadi_int el = D2.colind(offset_nadir+d); el<D2.colind(offset_nadir+d+1); ++el) {
622 
623  // Get the direction
624  casadi_int c = D2.row(el);
625 
626  // Give a seed in the direction
627  seed_col.push_back(output_col[c]);
628  seed_row.push_back(output_row[c]);
629  }
630 
631  //initialize to zero
632  aseed[d].resize(n_out_);
633  for (casadi_int ind=0; ind<aseed[d].size(); ++ind) {
634  casadi_int nrow = size1_out(ind), ncol = size2_out(ind); // Output dimensions
635  if (ind==oind) {
636  aseed[d][ind] = MatType::ones(Sparsity::triplet(nrow, ncol, seed_row, seed_col));
637  } else {
638  aseed[d][ind] = MatType(nrow, ncol);
639  }
640  }
641  }
642 
643  // Forward sensitivities
644  fsens.resize(nfdir_batch);
645  for (casadi_int d=0; d<nfdir_batch; ++d) {
646  // initialize to zero
647  fsens[d].resize(n_out_);
648  for (casadi_int oind=0; oind<fsens[d].size(); ++oind) {
649  fsens[d][oind] = MatType::zeros(sparsity_out_.at(oind));
650  }
651  }
652 
653  // Adjoint sensitivities
654  asens.resize(nadir_batch);
655  for (casadi_int d=0; d<nadir_batch; ++d) {
656  // initialize to zero
657  asens[d].resize(n_in_);
658  for (casadi_int ind=0; ind<asens[d].size(); ++ind) {
659  asens[d][ind] = MatType::zeros(sparsity_in_.at(ind));
660  }
661  }
662 
663  // Evaluate symbolically
664  if (!fseed.empty()) {
665  casadi_assert_dev(aseed.empty());
666  if (verbose_) casadi_message("Calling 'ad_forward'");
667  static_cast<const DerivedType*>(this)->ad_forward(fseed, fsens);
668  if (verbose_) casadi_message("Back from 'ad_forward'");
669  } else if (!aseed.empty()) {
670  casadi_assert_dev(fseed.empty());
671  if (verbose_) casadi_message("Calling 'ad_reverse'");
672  static_cast<const DerivedType*>(this)->ad_reverse(aseed, asens);
673  if (verbose_) casadi_message("Back from 'ad_reverse'");
674  }
675 
676  // Carry out the forward sweeps
677  for (casadi_int d=0; d<nfdir_batch; ++d) {
678  // Skip if nothing to add
679  if (fsens[d][oind].nnz()==0) {
680  continue;
681  }
682 
683  // If symmetric, see how many times each output appears
684  if (symmetric) {
685  // Initialize to zero
686  tmp.resize(nnz_out(oind));
687  std::fill(tmp.begin(), tmp.end(), 0);
688 
689  // "Multiply" Jacobian sparsity by seed vector
690  for (casadi_int el = D1.colind(offset_nfdir+d); el<D1.colind(offset_nfdir+d+1); ++el) {
691 
692  // Get the input nonzero
693  casadi_int c = D1.row(el);
694 
695  // Propagate dependencies
696  for (casadi_int el_jsp=jsp_colind[c]; el_jsp<jsp_colind[c+1]; ++el_jsp) {
697  tmp[jsp_row[el_jsp]]++;
698  }
699  }
700  }
701 
702  // Locate the nonzeros of the forward sensitivity matrix
703  sparsity_out_.at(oind).find(nzmap);
704  fsens[d][oind].sparsity().get_nz(nzmap);
705 
706  if (symmetric) {
707  sparsity_in_.at(iind).find(nzmap2);
708  fsens[d][oind].sparsity().get_nz(nzmap2);
709  }
710 
711  // Assignments to the Jacobian
712  adds.resize(fsens[d][oind].nnz());
713  std::fill(adds.begin(), adds.end(), -1);
714  if (symmetric) {
715  adds2.resize(adds.size());
716  std::fill(adds2.begin(), adds2.end(), -1);
717  }
718 
719  // For all the input nonzeros treated in the sweep
720  for (casadi_int el = D1.colind(offset_nfdir+d); el<D1.colind(offset_nfdir+d+1); ++el) {
721 
722  // Get the input nonzero
723  casadi_int c = D1.row(el);
724  //casadi_int f2_out;
725  //if (symmetric) {
726  // f2_out = nzmap2[c];
727  //}
728 
729  // Loop over the output nonzeros corresponding to this input nonzero
730  for (casadi_int el_out = jsp_trans.colind(c); el_out<jsp_trans.colind(c+1); ++el_out) {
731 
732  // Get the output nonzero
733  casadi_int r_out = jsp_trans.row(el_out);
734 
735  // Get the forward sensitivity nonzero
736  casadi_int f_out = nzmap[r_out];
737  if (f_out<0) continue; // Skip if structurally zero
738 
739  // The nonzero of the Jacobian now treated
740  casadi_int elJ = mapping[el_out];
741 
742  if (symmetric) {
743  if (tmp[r_out]==1) {
744  adds[f_out] = el_out;
745  adds2[f_out] = elJ;
746  }
747  } else {
748  // Get the output seed
749  adds[f_out] = elJ;
750  }
751  }
752  }
753 
754  // Get entries in fsens[d][oind] with nonnegative indices
755  tmp.resize(adds.size());
756  casadi_int sz = 0;
757  for (casadi_int i=0; i<adds.size(); ++i) {
758  if (adds[i]>=0) {
759  adds[sz] = adds[i];
760  tmp[sz++] = i;
761  }
762  }
763  adds.resize(sz);
764  tmp.resize(sz);
765 
766  // Add contribution to the Jacobian
767  ret.at(0).nz(adds) = fsens[d][oind].nz(tmp);
768 
769  if (symmetric) {
770  // Get entries in fsens[d][oind] with nonnegative indices
771  tmp.resize(adds2.size());
772  sz = 0;
773  for (casadi_int i=0; i<adds2.size(); ++i) {
774  if (adds2[i]>=0) {
775  adds2[sz] = adds2[i];
776  tmp[sz++] = i;
777  }
778  }
779  adds2.resize(sz);
780  tmp.resize(sz);
781 
782  // Add contribution to the Jacobian
783  ret.at(0).nz(adds2) = fsens[d][oind].nz(tmp);
784  }
785  }
786 
787  // Add elements to the Jacobian matrix
788  for (casadi_int d=0; d<nadir_batch; ++d) {
789  // Skip if nothing to add
790  if (asens[d][iind].nnz()==0) {
791  continue;
792  }
793 
794  // Locate the nonzeros of the adjoint sensitivity matrix
795  sparsity_in_.at(iind).find(nzmap);
796  asens[d][iind].sparsity().get_nz(nzmap);
797 
798  // Collect the (Jacobian nonzero, sensitivity nonzero) assignment pairs
799  adds.clear();
800  tmp.clear();
801 
802  // For all the output nonzeros treated in the sweep
803  for (casadi_int el = D2.colind(offset_nadir+d); el<D2.colind(offset_nadir+d+1); ++el) {
804 
805  // Get the output nonzero
806  casadi_int r = D2.row(el);
807 
808  // Loop over the input nonzeros that influences this output nonzero
809  for (casadi_int elJ = jsp.colind(r); elJ<jsp.colind(r+1); ++elJ) {
810 
811  // Get the input nonzero
812  casadi_int inz = jsp.row(elJ);
813 
814  // Get the corresponding adjoint sensitivity nonzero
815  casadi_int anz = nzmap[inz];
816  if (anz<0) continue;
817 
818  // Queue the assignment
819  adds.push_back(elJ);
820  tmp.push_back(anz);
821  }
822  }
823 
824  // Add contribution to the Jacobian in a single batched assignment
825  ret.at(0).nz(adds) = asens[d][iind].nz(tmp);
826  }
827 
828  // Update direction offsets
829  offset_nfdir += nfdir_batch;
830  offset_nadir += nadir_batch;
831  }
832 
833  // Return
834  for (MatType& Jb : ret) Jb = Jb.T();
835  return ret;
836 
837  } catch (std::exception& e) {
838  CASADI_THROW_ERROR("jac", e.what());
839  }
840  }
841 
842  template<typename DerivedType, typename MatType, typename NodeType>
843  Function XFunction<DerivedType, MatType, NodeType>
844  ::get_forward(casadi_int nfwd, const std::string& name,
845  const std::vector<std::string>& inames,
846  const std::vector<std::string>& onames,
847  const Dict& opts) const {
848  try {
849  // Seeds
850  std::vector<std::vector<MatType> > fseed = fwd_seed<MatType>(nfwd), fsens;
851 
852  // Evaluate symbolically
853  static_cast<const DerivedType*>(this)->ad_forward(fseed, fsens);
854  casadi_assert_dev(fsens.size()==fseed.size());
855 
856  // All inputs of the return function
857  std::vector<MatType> ret_in(inames.size());
858  std::copy(in_.begin(), in_.end(), ret_in.begin());
859  for (casadi_int i=0; i<n_out_; ++i) {
860  ret_in.at(n_in_+i) = MatType::sym(inames[n_in_+i], Sparsity(out_.at(i).size()));
861  }
862  std::vector<MatType> v(nfwd);
863  for (casadi_int i=0; i<n_in_; ++i) {
864  for (casadi_int d=0; d<nfwd; ++d) v[d] = fseed[d][i];
865  ret_in.at(n_in_ + n_out_ + i) = horzcat(v);
866  }
867 
868  // All outputs of the return function
869  std::vector<MatType> ret_out(onames.size());
870  for (casadi_int i=0; i<n_out_; ++i) {
871  if (is_diff_out_[i]) {
872  // Concatenate sensitivities, correct sparsity pattern if needed
873  for (casadi_int d=0; d<nfwd; ++d) v[d] = fsens[d][i];
874  ret_out.at(i) = ensure_stacked(horzcat(v), sparsity_out(i), nfwd);
875  } else {
876  // Output is non-differentable
877  ret_out.at(i) = MatType(size1_out(i), size2_out(i) * nfwd);
878  }
879  }
880 
881  Dict options = opts;
882  options["allow_duplicate_io_names"] = true;
883  // Assemble function and return
884  return Function(name, ret_in, ret_out, inames, onames, options);
885  } catch (std::exception& e) {
886  CASADI_THROW_ERROR("get_forward", e.what());
887  }
888  }
889 
890  template<typename DerivedType, typename MatType, typename NodeType>
891  Function XFunction<DerivedType, MatType, NodeType>
892  ::get_reverse(casadi_int nadj, const std::string& name,
893  const std::vector<std::string>& inames,
894  const std::vector<std::string>& onames,
895  const Dict& opts) const {
896  try {
897  // Seeds
898  std::vector<std::vector<MatType> > aseed = symbolicAdjSeed(nadj, out_), asens;
899 
900  // Evaluate symbolically
901  static_cast<const DerivedType*>(this)->ad_reverse(aseed, asens);
902 
903  // All inputs of the return function
904  std::vector<MatType> ret_in(inames.size());
905  std::copy(in_.begin(), in_.end(), ret_in.begin());
906  for (casadi_int i=0; i<n_out_; ++i) {
907  ret_in.at(n_in_ + i) = MatType::sym(inames[n_in_+i], Sparsity(out_.at(i).size()));
908  }
909  std::vector<MatType> v(nadj);
910  for (casadi_int i=0; i<n_out_; ++i) {
911  for (casadi_int d=0; d<nadj; ++d) v[d] = aseed[d][i];
912  ret_in.at(n_in_ + n_out_ + i) = horzcat(v);
913  }
914 
915  // All outputs of the return function
916  std::vector<MatType> ret_out(onames.size());
917  for (casadi_int i=0; i<n_in_; ++i) {
918  if (is_diff_in_[i]) {
919  // Concatenate sensitivities, correct sparsity pattern if needed
920  for (casadi_int d=0; d<nadj; ++d) v[d] = asens[d][i];
921  ret_out.at(i) = ensure_stacked(horzcat(v), sparsity_in(i), nadj);
922  } else {
923  // Input is non-differentable
924  ret_out.at(i) = MatType(size1_in(i), size2_in(i) * nadj);
925  }
926  }
927 
928  Dict options = opts;
929  options["allow_duplicate_io_names"] = true;
930  // Assemble function and return
931  return Function(name, ret_in, ret_out, inames, onames, options);
932  } catch (std::exception& e) {
933  CASADI_THROW_ERROR("get_reverse", e.what());
934  }
935  }
936 
937  template<typename DerivedType, typename MatType, typename NodeType>
938  Function XFunction<DerivedType, MatType, NodeType>
939  ::get_jacobian(const std::string& name,
940  const std::vector<std::string>& inames,
941  const std::vector<std::string>& onames,
942  const Dict& opts) const {
943  try {
944  // Select only differentiable inputs and outputs
945  std::vector<MatType> diff_in = vector_select(in_, is_diff_in_);
946  std::vector<MatType> diff_out = vector_select(out_, is_diff_out_);
947 
948  std::vector<MatType> non_diff_in = vector_select(in_, is_diff_in_, true);
949 
950  // Create flattened function with only differentiable inputs/outputs
951  Dict tmp_options = generate_options("tmp");
952  tmp_options["allow_free"] = true;
953  tmp_options["allow_duplicate_io_names"] = true;
954  std::vector<bool> tmp_is_diff = {true};
955  if (!non_diff_in.empty()) tmp_is_diff.push_back(false);
956 
957  tmp_options["is_diff_in"] = tmp_is_diff;
958  std::vector<MatType> tmp_args = {veccat(diff_in)};
959  if (!non_diff_in.empty()) tmp_args.push_back(veccat(non_diff_in));
960 
961  Function tmp("flattened_" + name_, tmp_args, {veccat(diff_out)}, tmp_options);
962  // Expression for the Jacobian of differentiable inputs/outputs only
963  MatType J = tmp.get<DerivedType>()->jac(Dict()).at(0);
964 
965  // Split up Jacobian into blocks (only for differentiable inputs/outputs)
966  std::vector<casadi_int> r_offset = {0}, c_offset = {0};
967  for (auto& e : diff_out) r_offset.push_back(r_offset.back() + e.numel());
968  for (auto& e : diff_in) c_offset.push_back(c_offset.back() + e.numel());
969  auto Jblocks = MatType::blocksplit(J, r_offset, c_offset);
970 
971  // Assemble full Jacobian output, inserting zeros for non-differentiable blocks
972  std::vector<MatType> ret_out;
973  ret_out.reserve(onames.size());
974  casadi_int diff_i = 0;
975  for (casadi_int i=0; i<n_out_; ++i) {
976  casadi_int diff_j = 0;
977  for (casadi_int j=0; j<n_in_; ++j) {
978  if (is_diff_out_.at(i) && is_diff_in_.at(j)) {
979  ret_out.push_back(Jblocks.at(diff_i).at(diff_j));
980  diff_j++;
981  } else {
982  ret_out.push_back(MatType(out_.at(i).numel(), in_.at(j).numel()));
983  }
984  }
985  if (is_diff_out_.at(i)) diff_i++;
986  }
987 
988  // All inputs of the return function
989  std::vector<MatType> ret_in(inames.size());
990  std::copy(in_.begin(), in_.end(), ret_in.begin());
991  for (casadi_int i=0; i<n_out_; ++i) {
992  ret_in.at(n_in_+i) = MatType::sym(inames[n_in_+i], Sparsity(out_.at(i).size()));
993  }
994 
995  Dict options = opts;
996  options["allow_free"] = true;
997  options["allow_duplicate_io_names"] = true;
998 
999  if (opts.find("is_diff_in")==opts.end()) {
1000  std::vector<bool> is_diff_in = join(is_diff_in_, is_diff_out_);
1001  options["is_diff_in"] = is_diff_in;
1002  }
1003 
1004  if (opts.find("is_diff_out")==opts.end()) {
1005  std::vector<bool> is_diff_out;
1006  for (casadi_int i=0; i<n_out_; ++i) {
1007  for (casadi_int j=0; j<n_in_; ++j) {
1008  is_diff_out.push_back(is_diff_in_[j] && is_diff_out_[i]);
1009  }
1010  }
1011  options["is_diff_out"] = is_diff_out;
1012  }
1013 
1014  // Assemble function and return
1015  return Function(name, ret_in, ret_out, inames, onames, options);
1016  } catch (std::exception& e) {
1017  CASADI_THROW_ERROR("get_jacobian", e.what());
1018  }
1019  }
1020 
1021  template<typename DerivedType, typename MatType, typename NodeType>
1022  Function XFunction<DerivedType, MatType, NodeType>
1023  ::slice(const std::string& name, const std::vector<casadi_int>& order_in,
1024  const std::vector<casadi_int>& order_out, const Dict& opts) const {
1025  // Return expressions
1026  std::vector<MatType> ret_in, ret_out;
1027  std::vector<std::string> ret_in_name, ret_out_name;
1028 
1029  // Reorder inputs
1030  for (casadi_int k : order_in) {
1031  ret_in.push_back(in_.at(k));
1032  ret_in_name.push_back(name_in_.at(k));
1033  }
1034 
1035  // Reorder outputs
1036  for (casadi_int k : order_out) {
1037  ret_out.push_back(out_.at(k));
1038  ret_out_name.push_back(name_out_.at(k));
1039  }
1040 
1041  // Assemble function
1042  return Function(name, ret_in, ret_out,
1043  ret_in_name, ret_out_name, opts);
1044  }
1045 
1046  template<typename DerivedType, typename MatType, typename NodeType>
1047  void XFunction<DerivedType, MatType, NodeType>
1048  ::apply_simplify_passes(
1049  const std::vector<std::pair<std::string, casadi_int> >& tasks,
1050  std::vector<MatType>& new_in,
1051  std::vector<MatType>& new_out) const {
1052  for (const auto& tc : tasks) {
1053  const std::string& task = tc.first;
1054  // count>0: run exactly that many times; count==0: run until a fixed point
1055  casadi_int count = tc.second;
1056  casadi_assert(count>=0,
1057  "simplify task '" + task + "': run count must be >= 0 (0 = until fixed point)");
1058  casadi_int prev_nodes = -1;
1059  casadi_int max_iter = count==0 ? 100 : count;
1060  for (casadi_int it=0; it<max_iter; ++it) {
1061  if (task=="empty_inputs") {
1062  // What symbols occur in the outputs?
1063  std::vector<MatType> syms = MatType::symvar(veccat(new_out));
1064  // Loop over inputs
1065  for (MatType& e : new_in) {
1066  // If current input symbols do not occur in outputs
1067  if (!contains_any(syms, MatType::symvar(e))) {
1068  // Replace input by an empty matrix
1069  e = MatType(e.size());
1070  }
1071  }
1072  } else if (task=="combine_terms") {
1073  MatType::simplify_combine_terms(new_in, new_out);
1074  } else if (task=="cse") {
1075  new_out = MatType::cse(new_out);
1076  } else if (task=="ref_count") {
1077  MatType::simplify_ref_count(new_in, new_out);
1078  } else if (task=="const_folding") {
1079  MatType::simplify_const_folding(new_in, new_out);
1080  } else {
1081  casadi_error("No such simplify task: '" + task + "'.\n");
1082  }
1083  if (count!=0) continue;
1084  // Stop once the graph size stops shrinking
1085  casadi_int nodes = MatType::n_nodes(veccat(new_out));
1086  if (prev_nodes != -1 && nodes >= prev_nodes) break;
1087  prev_nodes = nodes;
1088  }
1089  }
1090  }
1091 
1092  template<typename DerivedType, typename MatType, typename NodeType>
1093  Function XFunction<DerivedType, MatType, NodeType>
1094  ::simplify_passes(
1095  const std::vector<std::pair<std::string, casadi_int> >& tasks) const {
1096  std::vector<MatType> new_in = in_;
1097  std::vector<MatType> new_out = out_;
1098  Dict final_options = generate_options("clone");
1099  final_options["allow_duplicate_io_names"] = true;
1100  final_options["allow_free"] = true;
1101  apply_simplify_passes(tasks, new_in, new_out);
1102  return Function(name_, new_in, new_out, name_in_, name_out_, final_options);
1103  }
1104 
1105  template<typename DerivedType, typename MatType, typename NodeType>
1106  void XFunction<DerivedType, MatType, NodeType>
1107  ::export_code(const std::string& lang, std::ostream &stream, const Dict& options) const {
1108 
1109  casadi_assert(!has_free(), "export_code needs a Function without free variables");
1110 
1111  casadi_assert(lang=="matlab", "Only matlab language supported for now.");
1112 
1113  // start function
1114  stream << "function [varargout] = " << name_ << "(varargin)" << std::endl;
1115 
1116  // Allocate space for output argument (segments)
1117  for (casadi_int i=0;i<n_out_;++i) {
1118  stream << " argout_" << i << " = cell(" << nnz_out(i) << ",1);" << std::endl;
1119  }
1120 
1121  Dict opts;
1122  opts["indent_level"] = 1;
1123  export_code_body(lang, stream, opts);
1124 
1125  // Process the outputs
1126  for (casadi_int i=0;i<n_out_;++i) {
1127  const Sparsity& out = sparsity_out_.at(i);
1128  if (out.is_dense()) {
1129  // Special case if dense
1130  stream << " varargout{" << i+1 << "} = reshape(vertcat(argout_" << i << "{:}), ";
1131  stream << out.size1() << ", " << out.size2() << ");" << std::endl;
1132  } else {
1133  // For sparse outputs, export sparsity and call 'sparse'
1134  Dict opts;
1135  opts["name"] = "sp";
1136  opts["indent_level"] = 1;
1137  opts["as_matrix"] = false;
1138  out.export_code("matlab", stream, opts);
1139  stream << " varargout{" << i+1 << "} = ";
1140  stream << "sparse(sp_i, sp_j, vertcat(argout_" << i << "{:}), sp_m, sp_n);" << std::endl;
1141  }
1142  }
1143 
1144  // end function
1145  stream << "end" << std::endl;
1146  stream << "function y=nonzeros_gen(x)" << std::endl;
1147  stream << " if isa(x,'casadi.SX') || isa(x,'casadi.MX') || isa(x,'casadi.DM')" << std::endl;
1148  stream << " y = x{:};" << std::endl;
1149  stream << " elseif isa(x,'sdpvar')" << std::endl;
1150  stream << " b = getbase(x);" << std::endl;
1151  stream << " f = find(sum(b~=0,2));" << std::endl;
1152  stream << " y = sdpvar(length(f),1,[],getvariables(x),b(f,:));" << std::endl;
1153  stream << " else" << std::endl;
1154  stream << " y = nonzeros(x);" << std::endl;
1155  stream << " end" << std::endl;
1156  stream << "end" << std::endl;
1157  stream << "function y=if_else_zero_gen(c,e)" << std::endl;
1158  stream << " if isa(c+e,'casadi.SX') || isa(c+e,'casadi.MX') "
1159  "|| isa(c+e,'casadi.DM')" << std::endl;
1160  stream << " y = if_else(c, e, 0);" << std::endl;
1161  stream << " else" << std::endl;
1162  stream << " if c" << std::endl;
1163  stream << " y = x;" << std::endl;
1164  stream << " else" << std::endl;
1165  stream << " y = 0;" << std::endl;
1166  stream << " end" << std::endl;
1167  stream << " end" << std::endl;
1168  stream << "end" << std::endl;
1169 
1170 
1171  }
1172 
1173  template<typename DerivedType, typename MatType, typename NodeType>
1174  bool XFunction<DerivedType, MatType, NodeType>
1175  ::isInput(const std::vector<MatType>& arg) const {
1176  // Check if arguments matches the input expressions, in which case
1177  // the output is known to be the output expressions
1178  const casadi_int checking_depth = 2;
1179  for (casadi_int i=0; i<arg.size(); ++i) {
1180  if (!is_equal(arg[i], in_[i], checking_depth)) {
1181  return false;
1182  }
1183  }
1184  return true;
1185  }
1186 
1187  template<typename DerivedType, typename MatType, typename NodeType>
1188  void XFunction<DerivedType, MatType, NodeType>::
1189  call_forward(const std::vector<MatType>& arg,
1190  const std::vector<MatType>& res,
1191  const std::vector<std::vector<MatType> >& fseed,
1192  std::vector<std::vector<MatType> >& fsens,
1193  bool always_inline, bool never_inline) const {
1194  casadi_assert(!(always_inline && never_inline), "Inconsistent options");
1195  if (!should_inline(MatType::type_name()=="SX", always_inline, never_inline)) {
1196  // The non-inlining version is implemented in the base class
1197  return FunctionInternal::call_forward(arg, res, fseed, fsens,
1198  always_inline, never_inline);
1199  }
1200 
1201  // Quick return if no seeds
1202  if (fseed.empty()) {
1203  fsens.clear();
1204  return;
1205  }
1206 
1207  // Call inlining
1208  if (isInput(arg)) {
1209  // Argument agrees with in_, call ad_forward directly
1210  static_cast<const DerivedType*>(this)->ad_forward(fseed, fsens);
1211  } else {
1212  // Need to create a temporary function
1213  Function f("tmp_call_forward", arg, res);
1214  static_cast<DerivedType *>(f.get())->ad_forward(fseed, fsens);
1215  }
1216  }
1217 
1218  template<typename DerivedType, typename MatType, typename NodeType>
1219  void XFunction<DerivedType, MatType, NodeType>::
1220  call_reverse(const std::vector<MatType>& arg,
1221  const std::vector<MatType>& res,
1222  const std::vector<std::vector<MatType> >& aseed,
1223  std::vector<std::vector<MatType> >& asens,
1224  bool always_inline, bool never_inline) const {
1225  casadi_assert(!(always_inline && never_inline), "Inconsistent options");
1226  if (!should_inline(MatType::type_name()=="SX", always_inline, never_inline)) {
1227  // The non-inlining version is implemented in the base class
1228  return FunctionInternal::call_reverse(arg, res, aseed, asens,
1229  always_inline, never_inline);
1230  }
1231 
1232  // Quick return if no seeds
1233  if (aseed.empty()) {
1234  asens.clear();
1235  return;
1236  }
1237 
1238  // Call inlining
1239  if (isInput(arg)) {
1240  // Argument agrees with in_, call ad_reverse directly
1241  static_cast<const DerivedType*>(this)->ad_reverse(aseed, asens);
1242  } else {
1243  // Need to create a temporary function
1244  Function f("tmp_call_reverse", arg, res);
1245  static_cast<DerivedType *>(f.get())->ad_reverse(aseed, asens);
1246  }
1247  }
1248 
1249  template<typename DerivedType, typename MatType, typename NodeType>
1250  Function XFunction<DerivedType, MatType, NodeType>::
1251  factory(const std::string& name,
1252  const std::vector<std::string>& s_in,
1253  const std::vector<std::string>& s_out,
1254  const Function::AuxOut& aux,
1255  const Dict& opts) const {
1256 
1257  Dict g_ops = generate_options("tmp");
1258  Dict f_options;
1259  f_options["helper_options"] = g_ops;
1260  f_options["final_options"] = g_ops;
1261  update_dict(f_options, opts, true);
1262 
1263  Dict final_options;
1264  extract_from_dict_inplace(f_options, "final_options", final_options);
1265  final_options["allow_duplicate_io_names"] = true;
1266 
1267  // Create an expression factory
1268  Factory<MatType> f;
1269  for (casadi_int i=0; i<in_.size(); ++i) f.add_input(name_in_[i], in_[i], is_diff_in_[i]);
1270  for (casadi_int i=0; i<out_.size(); ++i) f.add_output(name_out_[i], out_[i], is_diff_out_[i]);
1271  f.add_dual(aux);
1272 
1273  // Specify input expressions to be calculated
1274  std::vector<std::string> ret_iname;
1275  for (const std::string& s : s_in) {
1276  try {
1277  ret_iname.push_back(f.request_input(s));
1278  } catch (CasadiException& ex) {
1279  casadi_error("Cannot process factory input \"" + s + "\":" + ex.what());
1280  }
1281  }
1282 
1283  // Specify output expressions to be calculated
1284  std::vector<std::string> ret_oname;
1285  for (const std::string& s : s_out) {
1286  try {
1287  ret_oname.push_back(f.request_output(s));
1288  } catch (CasadiException& ex) {
1289  casadi_error("Cannot process factory output \"" + s + "\":" + ex.what());
1290  }
1291  }
1292 
1293  // Calculate expressions
1294  f.calculate(f_options);
1295 
1296  // Get input expressions
1297  std::vector<MatType> ret_in;
1298  ret_in.reserve(s_in.size());
1299  for (const std::string& s : s_in) ret_in.push_back(f.get_input(s));
1300 
1301  // Get output expressions
1302  std::vector<MatType> ret_out;
1303  ret_out.reserve(s_out.size());
1304  for (const std::string& s : s_out) ret_out.push_back(f.get_output(s));
1305 
1306  // Create function and return
1307  Dict final_options_allow_free = final_options;
1308  final_options_allow_free["allow_free"] = true;
1309  final_options_allow_free["allow_duplicate_io_names"] = true;
1310  Function ret(name, ret_in, ret_out, ret_iname, ret_oname, final_options_allow_free);
1311  if (ret.has_free()) {
1312  // Substitute free variables with zeros
1313  // We assume that the free variables are caused by false positive dependencies
1314  std::vector<MatType> free_in = MatType::get_free(ret);
1315  std::vector<MatType> free_sub = free_in;
1316  for (auto&& e : free_sub) e = MatType::zeros(e.sparsity());
1317  ret_out = substitute(ret_out, free_in, free_sub);
1318  ret = Function(name, ret_in, ret_out, ret_iname, ret_oname, final_options);
1319  }
1320  return ret;
1321  }
1322 
1323  template<typename DerivedType, typename MatType, typename NodeType>
1324  std::vector<bool> XFunction<DerivedType, MatType, NodeType>::
1325  which_depends(const std::string& s_in, const std::vector<std::string>& s_out,
1326  casadi_int order, bool tr) const {
1327 
1328  // Input arguments
1329  auto it = std::find(name_in_.begin(), name_in_.end(), s_in);
1330  casadi_assert_dev(it!=name_in_.end());
1331  MatType arg = in_.at(it-name_in_.begin());
1332 
1333  // Output arguments
1334  std::vector<MatType> res;
1335  for (auto&& s : s_out) {
1336  it = std::find(name_out_.begin(), name_out_.end(), s);
1337  casadi_assert_dev(it!=name_out_.end());
1338  res.push_back(out_.at(it-name_out_.begin()));
1339  }
1340 
1341  // Extract variables entering nonlinearly
1342  return MatType::which_depends(veccat(res), arg, order, tr);
1343  }
1344 
1345  template<typename MatType>
1346  Sparsity _jacobian_sparsity(const MatType &expr, const MatType &var) {
1347  Dict opts{{"max_io", 0}, {"allow_free", true}};
1348  Function f = Function("tmp_jacobian_sparsity", {var}, {expr}, opts);
1349  return f.jac_sparsity(0, 0, false);
1350  }
1351 
1352  template<typename MatType>
1353  std::vector<bool> _which_depends(const MatType &expr, const MatType &var,
1354  casadi_int order, bool tr) {
1355  // Short-circuit
1356  if (expr.is_empty() || var.is_empty()) {
1357  return std::vector<bool>(tr? expr.numel() : var.numel(), false);
1358  }
1359 
1360  MatType e = expr;
1361 
1362  // Create a function for calculating a forward-mode derivative
1363  casadi_assert(order==1 || order==2,
1364  "which_depends: order argument must be 1 or 2, got " + str(order) + " instead.");
1365 
1366  MatType v = MatType::sym("v", var.sparsity());
1367  for (casadi_int i=1;i<order;++i) {
1368  e = jtimes(e, var, v);
1369  }
1370 
1371  Dict opts{{"max_io", 0}, {"allow_free", true}};
1372  Function f = Function("tmp_which_depends", {var}, {e}, opts);
1373  // Propagate sparsities backwards seeding all outputs
1374  std::vector<bvec_t> seed(tr? f.nnz_in(0) : f.nnz_out(0), 1);
1375  std::vector<bvec_t> sens(tr? f.nnz_out(0) : f.nnz_in(0), 0);
1376 
1377  if (tr)
1378  f({get_ptr(seed)}, {get_ptr(sens)});
1379  else
1380  f.rev({get_ptr(sens)}, {get_ptr(seed)});
1381  // Temporaries for evaluation
1382  std::vector<bool> ret(sens.size());
1383  std::copy(sens.begin(), sens.end(), ret.begin());
1384 
1385  // Project the result back on the original sparsity
1386  if (tr && e.sparsity()!=expr.sparsity()) {
1387  // std::vector<bool> is not accessible as bool*
1388  // bool -> casadi_int
1389  std::vector<casadi_int> source(sens.size());
1390  std::copy(ret.begin(), ret.end(), source.begin());
1391  std::vector<casadi_int> target(expr.nnz());
1392 
1393  // project
1394  std::vector<casadi_int> scratch(expr.size1());
1395  casadi_project(get_ptr(source), e.sparsity(), get_ptr(target), expr.sparsity(),
1396  get_ptr(scratch));
1397 
1398  // casadi_int -> bool
1399  ret.resize(expr.nnz());
1400  std::copy(target.begin(), target.end(), ret.begin());
1401  }
1402 
1403  return ret;
1404  }
1405 
1406 } // namespace casadi
1408 #undef CASADI_THROW_ERROR
1409 
1410 #endif // CASADI_X_FUNCTION_HPP
std::map< std::string, std::vector< std::string > > AuxOut
Definition: function.hpp:447
The casadi namespace.
Definition: archiver.hpp:32
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.