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 
89  static void sort_depth_first(std::stack<NodeType*>& s, std::vector<NodeType*>& nodes);
90 
94  std::vector<MatType> jac(const Dict& opts) const;
95 
99  bool is_a(const std::string& type, bool recursive) const override {
100  return type=="xfunction" || (recursive && FunctionInternal::is_a(type, recursive));
101  }
102 
103  // Factory
104  Function factory(const std::string& name,
105  const std::vector<std::string>& s_in,
106  const std::vector<std::string>& s_out,
107  const Function::AuxOut& aux,
108  const Dict& opts) const override;
109 
116  std::vector<bool> which_depends(const std::string& s_in,
117  const std::vector<std::string>& s_out,
118  casadi_int order, bool tr=false) const override;
119 
121 
124  bool has_forward(casadi_int nfwd) const override { return true;}
125  Function get_forward(casadi_int nfwd, const std::string& name,
126  const std::vector<std::string>& inames,
127  const std::vector<std::string>& onames,
128  const Dict& opts) const override;
130 
132 
135  bool has_reverse(casadi_int nadj) const override { return true;}
136  Function get_reverse(casadi_int nadj, const std::string& name,
137  const std::vector<std::string>& inames,
138  const std::vector<std::string>& onames,
139  const Dict& opts) const override;
141 
143 
146  bool has_jacobian() const override { return true;}
147  Function get_jacobian(const std::string& name,
148  const std::vector<std::string>& inames,
149  const std::vector<std::string>& onames,
150  const Dict& opts) const override;
152 
156  Function slice(const std::string& name, const std::vector<casadi_int>& order_in,
157  const std::vector<casadi_int>& order_out, const Dict& opts) const override;
158 
162  void codegen_declarations(CodeGenerator& g) const override = 0;
163 
167  void codegen_body(CodeGenerator& g) const override = 0;
168 
172  void export_code(const std::string& lang,
173  std::ostream &stream, const Dict& options) const override;
174 
178  virtual void export_code_body(const std::string& lang,
179  std::ostream &stream, const Dict& options) const = 0;
180 
184  bool has_codegen() const override { return true;}
185 
189  virtual bool isInput(const std::vector<MatType>& arg) const;
190 
192  virtual bool should_inline(bool always_inline, bool never_inline) const = 0;
193 
197  void call_forward(const std::vector<MatType>& arg,
198  const std::vector<MatType>& res,
199  const std::vector<std::vector<MatType> >& fseed,
200  std::vector<std::vector<MatType> >& fsens,
201  bool always_inline, bool never_inline) const override;
202 
206  void call_reverse(const std::vector<MatType>& arg,
207  const std::vector<MatType>& res,
208  const std::vector<std::vector<MatType> >& aseed,
209  std::vector<std::vector<MatType> >& asens,
210  bool always_inline, bool never_inline) const override;
211 
213 
216  size_t get_n_in() override { return in_.size(); }
217  size_t get_n_out() override { return out_.size(); }
219 
221 
224  Sparsity get_sparsity_in(casadi_int i) override { return in_.at(i).sparsity();}
225  Sparsity get_sparsity_out(casadi_int i) override { return out_.at(i).sparsity();}
227 
231  explicit XFunction(DeserializingStream& s);
235  void serialize_body(SerializingStream &s) const override;
236 
244  void delayed_serialize_members(SerializingStream &s) const;
245  void delayed_deserialize_members(DeserializingStream &s);
247 
248  // Data members (all public)
249 
253  std::vector<MatType> in_;
254 
258  std::vector<MatType> out_;
259  };
260 
261  // Template implementations
262 
263  template<typename DerivedType, typename MatType, typename NodeType>
264  XFunction<DerivedType, MatType, NodeType>::
265  XFunction(const std::string& name,
266  const std::vector<MatType>& ex_in,
267  const std::vector<MatType>& ex_out,
268  const std::vector<std::string>& name_in,
269  const std::vector<std::string>& name_out)
270  : FunctionInternal(name), in_(ex_in), out_(ex_out) {
271  // Names of inputs
272  if (!name_in.empty()) {
273  casadi_assert(ex_in.size()==name_in.size(),
274  "Mismatching number of input names");
275  name_in_ = name_in;
276  }
277  // Names of outputs
278  if (!name_out.empty()) {
279  casadi_assert(ex_out.size()==name_out.size(),
280  "Mismatching number of output names");
281  name_out_ = name_out;
282  }
283  }
284 
285  template<typename DerivedType, typename MatType, typename NodeType>
286  XFunction<DerivedType, MatType, NodeType>::
287  XFunction(DeserializingStream& s) : FunctionInternal(s) {
288  s.version("XFunction", 1);
289  s.unpack("XFunction::in", in_);
290  // 'out' member needs to be delayed
291  }
292 
293  template<typename DerivedType, typename MatType, typename NodeType>
294  void XFunction<DerivedType, MatType, NodeType>::
295  delayed_deserialize_members(DeserializingStream& s) {
296  s.unpack("XFunction::out", out_);
297  }
298 
299  template<typename DerivedType, typename MatType, typename NodeType>
300  void XFunction<DerivedType, MatType, NodeType>::
301  delayed_serialize_members(SerializingStream& s) const {
302  s.pack("XFunction::out", out_);
303  }
304 
305  template<typename DerivedType, typename MatType, typename NodeType>
306  void XFunction<DerivedType, MatType, NodeType>::
307  serialize_body(SerializingStream& s) const {
308  FunctionInternal::serialize_body(s);
309  s.version("XFunction", 1);
310  s.pack("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>::init(const Dict& opts) {
316  // Call the init function of the base class
317  FunctionInternal::init(opts);
318 
319  bool allow_duplicate_io_names = false;
320  // Read options
321  for (auto&& op : opts) {
322  if (op.first=="allow_duplicate_io_names") {
323  allow_duplicate_io_names = op.second;
324  }
325  }
326 
327  if (verbose_) casadi_message(name_ + "::init");
328  // Make sure that inputs are symbolic
329  for (casadi_int i=0; i<n_in_; ++i) {
330  if (in_.at(i).nnz()>0 && !in_.at(i).is_valid_input()) {
331  casadi_error("For " + this->name_ + ": Xfunction input arguments must be purely symbolic."
332  "\nArgument " + str(i) + "(" + name_in_[i] + ") is not symbolic.");
333  }
334  }
335  // Check for duplicate entries among the input expressions
336  bool has_duplicates = false;
337  for (auto&& i : in_) {
338  if (i.has_duplicates()) {
339  has_duplicates = true;
340  break;
341  }
342  }
343  // Reset temporaries
344  for (auto&& i : in_) i.reset_input();
345  // Generate error
346  if (has_duplicates) {
347  std::stringstream s;
348  s << "The input expressions are not independent:\n";
349  for (casadi_int iind=0; iind<in_.size(); ++iind) {
350  s << iind << ": " << in_[iind] << "\n";
351  }
352  casadi_error(s.str());
353  }
354 
355  if (!allow_duplicate_io_names) {
356  // Collect hashes for all inputs and outputs
357  std::hash<std::string> hasher;
358  std::vector<size_t> iohash;
359  iohash.reserve(name_in_.size() + name_out_.size());
360  for (const std::string& s : name_in_) iohash.push_back(hasher(s));
361  for (const std::string& s : name_out_) iohash.push_back(hasher(s));
362  std::sort(iohash.begin(), iohash.end());
363  // Look for duplicates
364  size_t prev = -1;
365  for (size_t h : iohash) {
366  if (h == prev) {
367  // Hash duplicate found, collect strings
368  std::vector<std::string> io_names;
369  io_names.reserve(iohash.size());
370  for (const std::string& s : name_in_) io_names.push_back(s);
371  for (const std::string& s : name_out_) io_names.push_back(s);
372  std::sort(io_names.begin(), io_names.end());
373  // Look for duplicates
374  std::string prev;
375  for (std::string h : io_names) {
376  if (h == prev) casadi_error("Duplicate IO name: " + h + ". "
377  "To ignore this error, set 'allow_duplicate_io_names' option.");
378  prev = h;
379  }
380  }
381  prev = h;
382  }
383  }
384  }
385 
386  template<typename DerivedType, typename MatType, typename NodeType>
387  void XFunction<DerivedType, MatType, NodeType>::sort_depth_first(
388  std::stack<NodeType*>& s, std::vector<NodeType*>& nodes) {
389  while (!s.empty()) {
390  // Get the topmost element
391  NodeType* t = s.top();
392  // If the last element on the stack has not yet been added
393  if (t && t->temp>=0) {
394  // Get the index of the next dependency
395  casadi_int next_dep = t->temp++;
396  // If there is any dependency which has not yet been added
397  if (next_dep < t->n_dep()) {
398  // Add dependency to stack
399  s.push(static_cast<NodeType*>(t->dep(next_dep).get()));
400  } else {
401  // if no dependencies need to be added, we can add the node to the algorithm
402  nodes.push_back(t);
403  // Mark the node as found
404  t->temp = -1;
405  // Remove from stack
406  s.pop();
407  }
408  } else {
409  // If the last element on the stack has already been added
410  s.pop();
411  }
412  }
413  }
414 
415  template<typename DerivedType, typename MatType, typename NodeType>
416  std::vector<MatType> XFunction<DerivedType, MatType, NodeType>
417  ::jac(const Dict& opts) const {
418  try {
419  // Read options
420  bool compact = false;
421  bool symmetric = false;
422  bool allow_forward = true;
423  bool allow_reverse = true;
424  for (auto&& op : opts) {
425  if (op.first=="compact") {
426  compact = op.second;
427  } else if (op.first=="symmetric") {
428  symmetric = op.second;
429  } else if (op.first=="allow_forward") {
430  allow_forward = op.second;
431  } else if (op.first=="allow_reverse") {
432  allow_reverse = op.second;
433  } else if (op.first=="verbose") {
434  continue;
435  } else {
436  casadi_error("No such Jacobian option: " + std::string(op.first));
437  }
438  }
439 
440  // Return object
441  std::vector<MatType> ret(n_in_ * n_out_);
442 
443  // Quick return if trivially empty
444  if (nnz_in() == 0 || nnz_out() == 0) {
445  for (casadi_int i = 0; i < n_out_; ++i) {
446  for (casadi_int j = 0; j < n_in_; ++j) {
447  if (compact) {
448  ret[i * n_in_ + j] = MatType(nnz_out(i), nnz_in(j));
449  } else {
450  ret[i * n_in_ + j] = MatType(numel_out(i), numel_in(j));
451  }
452  }
453  }
454  return ret;
455  }
456 
457  // FIXME(@jaeandersson)
458  casadi_int iind = 0, oind = 0;
459  casadi_assert(n_in_ == 1, "Not implemented");
460  casadi_assert(n_out_ == 1, "Not implemented");
461 
462  // Create return object
463  ret.at(0) = MatType::zeros(jac_sparsity(0, 0, false, symmetric).T());
464  if (verbose_) casadi_message("Allocated return value");
465 
466  // Quick return if empty
467  if (ret.at(0).nnz()==0) {
468  ret.at(0) = ret.at(0).T();
469  return ret;
470  }
471 
472  // Get a bidirectional partition
473  Sparsity D1, D2;
474  get_partition(iind, oind, D1, D2, true, symmetric, allow_forward, allow_reverse);
475  if (verbose_) casadi_message("Graph coloring completed");
476 
477  // Get the number of forward and adjoint sweeps
478  casadi_int nfdir = D1.is_null() ? 0 : D1.size2();
479  casadi_int nadir = D2.is_null() ? 0 : D2.size2();
480 
481  // Number of derivative directions supported by the function
482  casadi_int max_nfdir = max_num_dir_;
483  casadi_int max_nadir = max_num_dir_;
484 
485  // Current forward and adjoint direction
486  casadi_int offset_nfdir = 0, offset_nadir = 0;
487 
488  // Evaluation result (known)
489  std::vector<MatType> res(out_);
490 
491  // Forward and adjoint seeds and sensitivities
492  std::vector<std::vector<MatType> > fseed, aseed, fsens, asens;
493 
494  // Get the sparsity of the Jacobian block
495  Sparsity jsp = jac_sparsity(0, 0, true, symmetric).T();
496  const casadi_int* jsp_colind = jsp.colind();
497  const casadi_int* jsp_row = jsp.row();
498 
499  // Input sparsity
500  std::vector<casadi_int> input_col = sparsity_in_.at(iind).get_col();
501  const casadi_int* input_row = sparsity_in_.at(iind).row();
502 
503  // Output sparsity
504  std::vector<casadi_int> output_col = sparsity_out_.at(oind).get_col();
505  const casadi_int* output_row = sparsity_out_.at(oind).row();
506 
507  // Get transposes and mappings for jacobian sparsity pattern if we are using forward mode
508  if (verbose_) casadi_message("jac transposes and mapping");
509  std::vector<casadi_int> mapping;
510  Sparsity jsp_trans;
511  if (nfdir>0) {
512  jsp_trans = jsp.transpose(mapping);
513  }
514 
515  // The nonzeros of the sensitivity matrix
516  std::vector<casadi_int> nzmap, nzmap2;
517 
518  // Additions to the jacobian matrix
519  std::vector<casadi_int> adds, adds2;
520 
521  // Temporary vector
522  std::vector<casadi_int> tmp;
523 
524  // Progress
525  casadi_int progress = -10;
526 
527  // Number of sweeps
528  casadi_int nsweep_fwd = nfdir/max_nfdir; // Number of sweeps needed for the forward mode
529  if (nfdir%max_nfdir>0) nsweep_fwd++;
530  casadi_int nsweep_adj = nadir/max_nadir; // Number of sweeps needed for the adjoint mode
531  if (nadir%max_nadir>0) nsweep_adj++;
532  casadi_int nsweep = std::max(nsweep_fwd, nsweep_adj);
533  if (verbose_) {
534  casadi_message(str(nsweep) + " sweeps needed for " + str(nfdir) + " forward and "
535  + str(nadir) + " reverse directions");
536  }
537 
538  // Sparsity of the seeds
539  std::vector<casadi_int> seed_col, seed_row;
540 
541  // Evaluate until everything has been determined
542  for (casadi_int s=0; s<nsweep; ++s) {
543  // Print progress
544  if (verbose_) {
545  casadi_int progress_new = (s*100)/nsweep;
546  // Print when entering a new decade
547  if (progress_new / 10 > progress / 10) {
548  progress = progress_new;
549  casadi_message(str(progress) + " %");
550  }
551  }
552 
553  // Number of forward and adjoint directions in the current "batch"
554  casadi_int nfdir_batch = std::min(nfdir - offset_nfdir, max_nfdir);
555  casadi_int nadir_batch = std::min(nadir - offset_nadir, max_nadir);
556 
557  // Forward seeds
558  fseed.resize(nfdir_batch);
559  for (casadi_int d=0; d<nfdir_batch; ++d) {
560  // Nonzeros of the seed matrix
561  seed_col.clear();
562  seed_row.clear();
563 
564  // For all the directions
565  for (casadi_int el = D1.colind(offset_nfdir+d); el<D1.colind(offset_nfdir+d+1); ++el) {
566 
567  // Get the direction
568  casadi_int c = D1.row(el);
569 
570  // Give a seed in the direction
571  seed_col.push_back(input_col[c]);
572  seed_row.push_back(input_row[c]);
573  }
574 
575  // initialize to zero
576  fseed[d].resize(n_in_);
577  for (casadi_int ind=0; ind<fseed[d].size(); ++ind) {
578  casadi_int nrow = size1_in(ind), ncol = size2_in(ind); // Input dimensions
579  if (ind==iind) {
580  fseed[d][ind] = MatType::ones(Sparsity::triplet(nrow, ncol, seed_row, seed_col));
581  } else {
582  fseed[d][ind] = MatType(nrow, ncol);
583  }
584  }
585  }
586 
587  // Adjoint seeds
588  aseed.resize(nadir_batch);
589  for (casadi_int d=0; d<nadir_batch; ++d) {
590  // Nonzeros of the seed matrix
591  seed_col.clear();
592  seed_row.clear();
593 
594  // For all the directions
595  for (casadi_int el = D2.colind(offset_nadir+d); el<D2.colind(offset_nadir+d+1); ++el) {
596 
597  // Get the direction
598  casadi_int c = D2.row(el);
599 
600  // Give a seed in the direction
601  seed_col.push_back(output_col[c]);
602  seed_row.push_back(output_row[c]);
603  }
604 
605  //initialize to zero
606  aseed[d].resize(n_out_);
607  for (casadi_int ind=0; ind<aseed[d].size(); ++ind) {
608  casadi_int nrow = size1_out(ind), ncol = size2_out(ind); // Output dimensions
609  if (ind==oind) {
610  aseed[d][ind] = MatType::ones(Sparsity::triplet(nrow, ncol, seed_row, seed_col));
611  } else {
612  aseed[d][ind] = MatType(nrow, ncol);
613  }
614  }
615  }
616 
617  // Forward sensitivities
618  fsens.resize(nfdir_batch);
619  for (casadi_int d=0; d<nfdir_batch; ++d) {
620  // initialize to zero
621  fsens[d].resize(n_out_);
622  for (casadi_int oind=0; oind<fsens[d].size(); ++oind) {
623  fsens[d][oind] = MatType::zeros(sparsity_out_.at(oind));
624  }
625  }
626 
627  // Adjoint sensitivities
628  asens.resize(nadir_batch);
629  for (casadi_int d=0; d<nadir_batch; ++d) {
630  // initialize to zero
631  asens[d].resize(n_in_);
632  for (casadi_int ind=0; ind<asens[d].size(); ++ind) {
633  asens[d][ind] = MatType::zeros(sparsity_in_.at(ind));
634  }
635  }
636 
637  // Evaluate symbolically
638  if (!fseed.empty()) {
639  casadi_assert_dev(aseed.empty());
640  if (verbose_) casadi_message("Calling 'ad_forward'");
641  static_cast<const DerivedType*>(this)->ad_forward(fseed, fsens);
642  if (verbose_) casadi_message("Back from 'ad_forward'");
643  } else if (!aseed.empty()) {
644  casadi_assert_dev(fseed.empty());
645  if (verbose_) casadi_message("Calling 'ad_reverse'");
646  static_cast<const DerivedType*>(this)->ad_reverse(aseed, asens);
647  if (verbose_) casadi_message("Back from 'ad_reverse'");
648  }
649 
650  // Carry out the forward sweeps
651  for (casadi_int d=0; d<nfdir_batch; ++d) {
652  // Skip if nothing to add
653  if (fsens[d][oind].nnz()==0) {
654  continue;
655  }
656 
657  // If symmetric, see how many times each output appears
658  if (symmetric) {
659  // Initialize to zero
660  tmp.resize(nnz_out(oind));
661  std::fill(tmp.begin(), tmp.end(), 0);
662 
663  // "Multiply" Jacobian sparsity by seed vector
664  for (casadi_int el = D1.colind(offset_nfdir+d); el<D1.colind(offset_nfdir+d+1); ++el) {
665 
666  // Get the input nonzero
667  casadi_int c = D1.row(el);
668 
669  // Propagate dependencies
670  for (casadi_int el_jsp=jsp_colind[c]; el_jsp<jsp_colind[c+1]; ++el_jsp) {
671  tmp[jsp_row[el_jsp]]++;
672  }
673  }
674  }
675 
676  // Locate the nonzeros of the forward sensitivity matrix
677  sparsity_out_.at(oind).find(nzmap);
678  fsens[d][oind].sparsity().get_nz(nzmap);
679 
680  if (symmetric) {
681  sparsity_in_.at(iind).find(nzmap2);
682  fsens[d][oind].sparsity().get_nz(nzmap2);
683  }
684 
685  // Assignments to the Jacobian
686  adds.resize(fsens[d][oind].nnz());
687  std::fill(adds.begin(), adds.end(), -1);
688  if (symmetric) {
689  adds2.resize(adds.size());
690  std::fill(adds2.begin(), adds2.end(), -1);
691  }
692 
693  // For all the input nonzeros treated in the sweep
694  for (casadi_int el = D1.colind(offset_nfdir+d); el<D1.colind(offset_nfdir+d+1); ++el) {
695 
696  // Get the input nonzero
697  casadi_int c = D1.row(el);
698  //casadi_int f2_out;
699  //if (symmetric) {
700  // f2_out = nzmap2[c];
701  //}
702 
703  // Loop over the output nonzeros corresponding to this input nonzero
704  for (casadi_int el_out = jsp_trans.colind(c); el_out<jsp_trans.colind(c+1); ++el_out) {
705 
706  // Get the output nonzero
707  casadi_int r_out = jsp_trans.row(el_out);
708 
709  // Get the forward sensitivity nonzero
710  casadi_int f_out = nzmap[r_out];
711  if (f_out<0) continue; // Skip if structurally zero
712 
713  // The nonzero of the Jacobian now treated
714  casadi_int elJ = mapping[el_out];
715 
716  if (symmetric) {
717  if (tmp[r_out]==1) {
718  adds[f_out] = el_out;
719  adds2[f_out] = elJ;
720  }
721  } else {
722  // Get the output seed
723  adds[f_out] = elJ;
724  }
725  }
726  }
727 
728  // Get entries in fsens[d][oind] with nonnegative indices
729  tmp.resize(adds.size());
730  casadi_int sz = 0;
731  for (casadi_int i=0; i<adds.size(); ++i) {
732  if (adds[i]>=0) {
733  adds[sz] = adds[i];
734  tmp[sz++] = i;
735  }
736  }
737  adds.resize(sz);
738  tmp.resize(sz);
739 
740  // Add contribution to the Jacobian
741  ret.at(0).nz(adds) = fsens[d][oind].nz(tmp);
742 
743  if (symmetric) {
744  // Get entries in fsens[d][oind] with nonnegative indices
745  tmp.resize(adds2.size());
746  sz = 0;
747  for (casadi_int i=0; i<adds2.size(); ++i) {
748  if (adds2[i]>=0) {
749  adds2[sz] = adds2[i];
750  tmp[sz++] = i;
751  }
752  }
753  adds2.resize(sz);
754  tmp.resize(sz);
755 
756  // Add contribution to the Jacobian
757  ret.at(0).nz(adds2) = fsens[d][oind].nz(tmp);
758  }
759  }
760 
761  // Add elements to the Jacobian matrix
762  for (casadi_int d=0; d<nadir_batch; ++d) {
763  // Skip if nothing to add
764  if (asens[d][iind].nnz()==0) {
765  continue;
766  }
767 
768  // Locate the nonzeros of the adjoint sensitivity matrix
769  sparsity_in_.at(iind).find(nzmap);
770  asens[d][iind].sparsity().get_nz(nzmap);
771 
772  // For all the output nonzeros treated in the sweep
773  for (casadi_int el = D2.colind(offset_nadir+d); el<D2.colind(offset_nadir+d+1); ++el) {
774 
775  // Get the output nonzero
776  casadi_int r = D2.row(el);
777 
778  // Loop over the input nonzeros that influences this output nonzero
779  for (casadi_int elJ = jsp.colind(r); elJ<jsp.colind(r+1); ++elJ) {
780 
781  // Get the input nonzero
782  casadi_int inz = jsp.row(elJ);
783 
784  // Get the corresponding adjoint sensitivity nonzero
785  casadi_int anz = nzmap[inz];
786  if (anz<0) continue;
787 
788  // Get the input seed
789  ret.at(0).nz(elJ) = asens[d][iind].nz(anz);
790  }
791  }
792  }
793 
794  // Update direction offsets
795  offset_nfdir += nfdir_batch;
796  offset_nadir += nadir_batch;
797  }
798 
799  // Return
800  for (MatType& Jb : ret) Jb = Jb.T();
801  return ret;
802 
803  } catch (std::exception& e) {
804  CASADI_THROW_ERROR("jac", e.what());
805  }
806  }
807 
808  template<typename DerivedType, typename MatType, typename NodeType>
809  Function XFunction<DerivedType, MatType, NodeType>
810  ::get_forward(casadi_int nfwd, const std::string& name,
811  const std::vector<std::string>& inames,
812  const std::vector<std::string>& onames,
813  const Dict& opts) const {
814  try {
815  // Seeds
816  std::vector<std::vector<MatType> > fseed = fwd_seed<MatType>(nfwd), fsens;
817 
818  // Evaluate symbolically
819  static_cast<const DerivedType*>(this)->ad_forward(fseed, fsens);
820  casadi_assert_dev(fsens.size()==fseed.size());
821 
822  // All inputs of the return function
823  std::vector<MatType> ret_in(inames.size());
824  std::copy(in_.begin(), in_.end(), ret_in.begin());
825  for (casadi_int i=0; i<n_out_; ++i) {
826  ret_in.at(n_in_+i) = MatType::sym(inames[n_in_+i], Sparsity(out_.at(i).size()));
827  }
828  std::vector<MatType> v(nfwd);
829  for (casadi_int i=0; i<n_in_; ++i) {
830  for (casadi_int d=0; d<nfwd; ++d) v[d] = fseed[d][i];
831  ret_in.at(n_in_ + n_out_ + i) = horzcat(v);
832  }
833 
834  // All outputs of the return function
835  std::vector<MatType> ret_out(onames.size());
836  for (casadi_int i=0; i<n_out_; ++i) {
837  if (is_diff_out_[i]) {
838  // Concatenate sensitivities, correct sparsity pattern if needed
839  for (casadi_int d=0; d<nfwd; ++d) v[d] = fsens[d][i];
840  ret_out.at(i) = ensure_stacked(horzcat(v), sparsity_out(i), nfwd);
841  } else {
842  // Output is non-differentable
843  ret_out.at(i) = MatType(size1_out(i), size2_out(i) * nfwd);
844  }
845  }
846 
847  Dict options = opts;
848  if (opts.find("is_diff_in")==opts.end())
849  options["is_diff_in"] = join(is_diff_in_, is_diff_out_, is_diff_in_);
850  if (opts.find("is_diff_out")==opts.end())
851  options["is_diff_out"] = is_diff_out_;
852  options["allow_duplicate_io_names"] = true;
853  // Assemble function and return
854  return Function(name, ret_in, ret_out, inames, onames, options);
855  } catch (std::exception& e) {
856  CASADI_THROW_ERROR("get_forward", e.what());
857  }
858  }
859 
860  template<typename DerivedType, typename MatType, typename NodeType>
861  Function XFunction<DerivedType, MatType, NodeType>
862  ::get_reverse(casadi_int nadj, const std::string& name,
863  const std::vector<std::string>& inames,
864  const std::vector<std::string>& onames,
865  const Dict& opts) const {
866  try {
867  // Seeds
868  std::vector<std::vector<MatType> > aseed = symbolicAdjSeed(nadj, out_), asens;
869 
870  // Evaluate symbolically
871  static_cast<const DerivedType*>(this)->ad_reverse(aseed, asens);
872 
873  // All inputs of the return function
874  std::vector<MatType> ret_in(inames.size());
875  std::copy(in_.begin(), in_.end(), ret_in.begin());
876  for (casadi_int i=0; i<n_out_; ++i) {
877  ret_in.at(n_in_ + i) = MatType::sym(inames[n_in_+i], Sparsity(out_.at(i).size()));
878  }
879  std::vector<MatType> v(nadj);
880  for (casadi_int i=0; i<n_out_; ++i) {
881  for (casadi_int d=0; d<nadj; ++d) v[d] = aseed[d][i];
882  ret_in.at(n_in_ + n_out_ + i) = horzcat(v);
883  }
884 
885  // All outputs of the return function
886  std::vector<MatType> ret_out(onames.size());
887  for (casadi_int i=0; i<n_in_; ++i) {
888  if (is_diff_in_[i]) {
889  // Concatenate sensitivities, correct sparsity pattern if needed
890  for (casadi_int d=0; d<nadj; ++d) v[d] = asens[d][i];
891  ret_out.at(i) = ensure_stacked(horzcat(v), sparsity_in(i), nadj);
892  } else {
893  // Input is non-differentable
894  ret_out.at(i) = MatType(size1_in(i), size2_in(i) * nadj);
895  }
896  }
897 
898  Dict options = opts;
899  if (opts.find("is_diff_in")==opts.end())
900  options["is_diff_in"] = join(is_diff_in_, is_diff_out_, is_diff_out_);
901  if (opts.find("is_diff_out")==opts.end())
902  options["is_diff_out"] = is_diff_in_;
903 
904  options["allow_duplicate_io_names"] = true;
905  // Assemble function and return
906  return Function(name, ret_in, ret_out, inames, onames, options);
907  } catch (std::exception& e) {
908  CASADI_THROW_ERROR("get_reverse", e.what());
909  }
910  }
911 
912  template<typename DerivedType, typename MatType, typename NodeType>
913  Function XFunction<DerivedType, MatType, NodeType>
914  ::get_jacobian(const std::string& name,
915  const std::vector<std::string>& inames,
916  const std::vector<std::string>& onames,
917  const Dict& opts) const {
918  try {
919  Dict tmp_options = generate_options("tmp");
920  tmp_options["allow_free"] = true;
921  tmp_options["allow_duplicate_io_names"] = true;
922  // Temporary single-input, single-output function FIXME(@jaeandersson)
923  Function tmp("flattened_" + name, {veccat(in_)}, {veccat(out_)}, tmp_options);
924 
925  // Expression for the extended Jacobian
926  MatType J = tmp.get<DerivedType>()->jac(Dict()).at(0);
927 
928  // Split up extended Jacobian
929  std::vector<casadi_int> r_offset = {0}, c_offset = {0};
930  for (auto& e : out_) r_offset.push_back(r_offset.back() + e.numel());
931  for (auto& e : in_) c_offset.push_back(c_offset.back() + e.numel());
932  auto Jblocks = MatType::blocksplit(J, r_offset, c_offset);
933 
934  // Collect all outputs
935  std::vector<MatType> ret_out;
936  ret_out.reserve(onames.size());
937  for (casadi_int i = 0; i < n_out_; ++i) {
938  for (casadi_int j = 0; j < n_in_; ++j) {
939  MatType b = Jblocks.at(i).at(j);
940  if (!is_diff_out_.at(i) || !is_diff_in_.at(j)) {
941  b = MatType(b.size());
942  }
943  ret_out.push_back(b);
944  }
945  }
946 
947  // All inputs of the return function
948  std::vector<MatType> ret_in(inames.size());
949  std::copy(in_.begin(), in_.end(), ret_in.begin());
950  for (casadi_int i=0; i<n_out_; ++i) {
951  ret_in.at(n_in_+i) = MatType::sym(inames[n_in_+i], Sparsity(out_.at(i).size()));
952  }
953 
954  Dict options = opts;
955  options["allow_free"] = true;
956  options["allow_duplicate_io_names"] = true;
957 
958  // Assemble function and return
959  return Function(name, ret_in, ret_out, inames, onames, options);
960  } catch (std::exception& e) {
961  CASADI_THROW_ERROR("get_jacobian", e.what());
962  }
963  }
964 
965  template<typename DerivedType, typename MatType, typename NodeType>
966  Function XFunction<DerivedType, MatType, NodeType>
967  ::slice(const std::string& name, const std::vector<casadi_int>& order_in,
968  const std::vector<casadi_int>& order_out, const Dict& opts) const {
969  // Return expressions
970  std::vector<MatType> ret_in, ret_out;
971  std::vector<std::string> ret_in_name, ret_out_name;
972 
973  // Reorder inputs
974  for (casadi_int k : order_in) {
975  ret_in.push_back(in_.at(k));
976  ret_in_name.push_back(name_in_.at(k));
977  }
978 
979  // Reorder outputs
980  for (casadi_int k : order_out) {
981  ret_out.push_back(out_.at(k));
982  ret_out_name.push_back(name_out_.at(k));
983  }
984 
985  // Assemble function
986  return Function(name, ret_in, ret_out,
987  ret_in_name, ret_out_name, opts);
988  }
989 
990  template<typename DerivedType, typename MatType, typename NodeType>
991  void XFunction<DerivedType, MatType, NodeType>
992  ::export_code(const std::string& lang, std::ostream &stream, const Dict& options) const {
993 
994  casadi_assert(!has_free(), "export_code needs a Function without free variables");
995 
996  casadi_assert(lang=="matlab", "Only matlab language supported for now.");
997 
998  // start function
999  stream << "function [varargout] = " << name_ << "(varargin)" << std::endl;
1000 
1001  // Allocate space for output argument (segments)
1002  for (casadi_int i=0;i<n_out_;++i) {
1003  stream << " argout_" << i << " = cell(" << nnz_out(i) << ",1);" << std::endl;
1004  }
1005 
1006  Dict opts;
1007  opts["indent_level"] = 1;
1008  export_code_body(lang, stream, opts);
1009 
1010  // Process the outputs
1011  for (casadi_int i=0;i<n_out_;++i) {
1012  const Sparsity& out = sparsity_out_.at(i);
1013  if (out.is_dense()) {
1014  // Special case if dense
1015  stream << " varargout{" << i+1 << "} = reshape(vertcat(argout_" << i << "{:}), ";
1016  stream << out.size1() << ", " << out.size2() << ");" << std::endl;
1017  } else {
1018  // For sparse outputs, export sparsity and call 'sparse'
1019  Dict opts;
1020  opts["name"] = "sp";
1021  opts["indent_level"] = 1;
1022  opts["as_matrix"] = false;
1023  out.export_code("matlab", stream, opts);
1024  stream << " varargout{" << i+1 << "} = ";
1025  stream << "sparse(sp_i, sp_j, vertcat(argout_" << i << "{:}), sp_m, sp_n);" << std::endl;
1026  }
1027  }
1028 
1029  // end function
1030  stream << "end" << std::endl;
1031  stream << "function y=nonzeros_gen(x)" << std::endl;
1032  stream << " if isa(x,'casadi.SX') || isa(x,'casadi.MX') || isa(x,'casadi.DM')" << std::endl;
1033  stream << " y = x{:};" << std::endl;
1034  stream << " elseif isa(x,'sdpvar')" << std::endl;
1035  stream << " b = getbase(x);" << std::endl;
1036  stream << " f = find(sum(b~=0,2));" << std::endl;
1037  stream << " y = sdpvar(length(f),1,[],getvariables(x),b(f,:));" << std::endl;
1038  stream << " else" << std::endl;
1039  stream << " y = nonzeros(x);" << std::endl;
1040  stream << " end" << std::endl;
1041  stream << "end" << std::endl;
1042  stream << "function y=if_else_zero_gen(c,e)" << std::endl;
1043  stream << " if isa(c+e,'casadi.SX') || isa(c+e,'casadi.MX') "
1044  "|| isa(c+e,'casadi.DM')" << std::endl;
1045  stream << " y = if_else(c, e, 0);" << std::endl;
1046  stream << " else" << std::endl;
1047  stream << " if c" << std::endl;
1048  stream << " y = x;" << std::endl;
1049  stream << " else" << std::endl;
1050  stream << " y = 0;" << std::endl;
1051  stream << " end" << std::endl;
1052  stream << " end" << std::endl;
1053  stream << "end" << std::endl;
1054 
1055 
1056  }
1057 
1058  template<typename DerivedType, typename MatType, typename NodeType>
1059  bool XFunction<DerivedType, MatType, NodeType>
1060  ::isInput(const std::vector<MatType>& arg) const {
1061  // Check if arguments matches the input expressions, in which case
1062  // the output is known to be the output expressions
1063  const casadi_int checking_depth = 2;
1064  for (casadi_int i=0; i<arg.size(); ++i) {
1065  if (!is_equal(arg[i], in_[i], checking_depth)) {
1066  return false;
1067  }
1068  }
1069  return true;
1070  }
1071 
1072  template<typename DerivedType, typename MatType, typename NodeType>
1073  void XFunction<DerivedType, MatType, NodeType>::
1074  call_forward(const std::vector<MatType>& arg,
1075  const std::vector<MatType>& res,
1076  const std::vector<std::vector<MatType> >& fseed,
1077  std::vector<std::vector<MatType> >& fsens,
1078  bool always_inline, bool never_inline) const {
1079  casadi_assert(!(always_inline && never_inline), "Inconsistent options");
1080  if (!should_inline(always_inline, never_inline)) {
1081  // The non-inlining version is implemented in the base class
1082  return FunctionInternal::call_forward(arg, res, fseed, fsens,
1083  always_inline, never_inline);
1084  }
1085 
1086  // Quick return if no seeds
1087  if (fseed.empty()) {
1088  fsens.clear();
1089  return;
1090  }
1091 
1092  // Call inlining
1093  if (isInput(arg)) {
1094  // Argument agrees with in_, call ad_forward directly
1095  static_cast<const DerivedType*>(this)->ad_forward(fseed, fsens);
1096  } else {
1097  // Need to create a temporary function
1098  Function f("tmp_call_forward", arg, res);
1099  static_cast<DerivedType *>(f.get())->ad_forward(fseed, fsens);
1100  }
1101  }
1102 
1103  template<typename DerivedType, typename MatType, typename NodeType>
1104  void XFunction<DerivedType, MatType, NodeType>::
1105  call_reverse(const std::vector<MatType>& arg,
1106  const std::vector<MatType>& res,
1107  const std::vector<std::vector<MatType> >& aseed,
1108  std::vector<std::vector<MatType> >& asens,
1109  bool always_inline, bool never_inline) const {
1110  casadi_assert(!(always_inline && never_inline), "Inconsistent options");
1111  if (!should_inline(always_inline, never_inline)) {
1112  // The non-inlining version is implemented in the base class
1113  return FunctionInternal::call_reverse(arg, res, aseed, asens,
1114  always_inline, never_inline);
1115  }
1116 
1117  // Quick return if no seeds
1118  if (aseed.empty()) {
1119  asens.clear();
1120  return;
1121  }
1122 
1123  // Call inlining
1124  if (isInput(arg)) {
1125  // Argument agrees with in_, call ad_reverse directly
1126  static_cast<const DerivedType*>(this)->ad_reverse(aseed, asens);
1127  } else {
1128  // Need to create a temporary function
1129  Function f("tmp_call_reverse", arg, res);
1130  static_cast<DerivedType *>(f.get())->ad_reverse(aseed, asens);
1131  }
1132  }
1133 
1134  template<typename DerivedType, typename MatType, typename NodeType>
1135  Function XFunction<DerivedType, MatType, NodeType>::
1136  factory(const std::string& name,
1137  const std::vector<std::string>& s_in,
1138  const std::vector<std::string>& s_out,
1139  const Function::AuxOut& aux,
1140  const Dict& opts) const {
1141 
1142  Dict g_ops = generate_options("clone");
1143  Dict f_options;
1144  f_options["helper_options"] = g_ops;
1145  f_options["final_options"] = g_ops;
1146  update_dict(f_options, opts, true);
1147 
1148  Dict final_options;
1149  extract_from_dict_inplace(f_options, "final_options", final_options);
1150  final_options["allow_duplicate_io_names"] = true;
1151 
1152  // Create an expression factory
1153  Factory<MatType> f;
1154  for (casadi_int i=0; i<in_.size(); ++i) f.add_input(name_in_[i], in_[i], is_diff_in_[i]);
1155  for (casadi_int i=0; i<out_.size(); ++i) f.add_output(name_out_[i], out_[i], is_diff_out_[i]);
1156  f.add_dual(aux);
1157 
1158  // Specify input expressions to be calculated
1159  std::vector<std::string> ret_iname;
1160  for (const std::string& s : s_in) {
1161  try {
1162  ret_iname.push_back(f.request_input(s));
1163  } catch (CasadiException& ex) {
1164  casadi_error("Cannot process factory input \"" + s + "\":" + ex.what());
1165  }
1166  }
1167 
1168  // Specify output expressions to be calculated
1169  std::vector<std::string> ret_oname;
1170  for (const std::string& s : s_out) {
1171  try {
1172  ret_oname.push_back(f.request_output(s));
1173  } catch (CasadiException& ex) {
1174  casadi_error("Cannot process factory output \"" + s + "\":" + ex.what());
1175  }
1176  }
1177 
1178  // Calculate expressions
1179  f.calculate(f_options);
1180 
1181  // Get input expressions
1182  std::vector<MatType> ret_in;
1183  ret_in.reserve(s_in.size());
1184  for (const std::string& s : s_in) ret_in.push_back(f.get_input(s));
1185 
1186  // Get output expressions
1187  std::vector<MatType> ret_out;
1188  ret_out.reserve(s_out.size());
1189  for (const std::string& s : s_out) ret_out.push_back(f.get_output(s));
1190 
1191  // Create function and return
1192  Dict final_options_allow_free = final_options;
1193  final_options_allow_free["allow_free"] = true;
1194  final_options_allow_free["allow_duplicate_io_names"] = true;
1195  Function ret(name, ret_in, ret_out, ret_iname, ret_oname, final_options_allow_free);
1196  if (ret.has_free()) {
1197  // Substitute free variables with zeros
1198  // We assume that the free variables are caused by false positive dependencies
1199  std::vector<MatType> free_in = MatType::get_free(ret);
1200  std::vector<MatType> free_sub = free_in;
1201  for (auto&& e : free_sub) e = MatType::zeros(e.sparsity());
1202  ret_out = substitute(ret_out, free_in, free_sub);
1203  ret = Function(name, ret_in, ret_out, ret_iname, ret_oname, final_options);
1204  }
1205  return ret;
1206  }
1207 
1208  template<typename DerivedType, typename MatType, typename NodeType>
1209  std::vector<bool> XFunction<DerivedType, MatType, NodeType>::
1210  which_depends(const std::string& s_in, const std::vector<std::string>& s_out,
1211  casadi_int order, bool tr) const {
1212 
1213  // Input arguments
1214  auto it = std::find(name_in_.begin(), name_in_.end(), s_in);
1215  casadi_assert_dev(it!=name_in_.end());
1216  MatType arg = in_.at(it-name_in_.begin());
1217 
1218  // Output arguments
1219  std::vector<MatType> res;
1220  for (auto&& s : s_out) {
1221  it = std::find(name_out_.begin(), name_out_.end(), s);
1222  casadi_assert_dev(it!=name_out_.end());
1223  res.push_back(out_.at(it-name_out_.begin()));
1224  }
1225 
1226  // Extract variables entering nonlinearly
1227  return MatType::which_depends(veccat(res), arg, order, tr);
1228  }
1229 
1230  template<typename MatType>
1231  Sparsity _jacobian_sparsity(const MatType &expr, const MatType &var) {
1232  Dict opts{{"max_io", 0}, {"allow_free", true}};
1233  Function f = Function("tmp_jacobian_sparsity", {var}, {expr}, opts);
1234  return f.jac_sparsity(0, 0, false);
1235  }
1236 
1237  template<typename MatType>
1238  std::vector<bool> _which_depends(const MatType &expr, const MatType &var,
1239  casadi_int order, bool tr) {
1240  // Short-circuit
1241  if (expr.is_empty() || var.is_empty()) {
1242  return std::vector<bool>(tr? expr.numel() : var.numel(), false);
1243  }
1244 
1245  MatType e = expr;
1246 
1247  // Create a function for calculating a forward-mode derivative
1248  casadi_assert(order==1 || order==2,
1249  "which_depends: order argument must be 1 or 2, got " + str(order) + " instead.");
1250 
1251  MatType v = MatType::sym("v", var.sparsity());
1252  for (casadi_int i=1;i<order;++i) {
1253  e = jtimes(e, var, v);
1254  }
1255 
1256  Dict opts{{"max_io", 0}, {"allow_free", true}};
1257  Function f = Function("tmp_which_depends", {var}, {e}, opts);
1258  // Propagate sparsities backwards seeding all outputs
1259  std::vector<bvec_t> seed(tr? f.nnz_in(0) : f.nnz_out(0), 1);
1260  std::vector<bvec_t> sens(tr? f.nnz_out(0) : f.nnz_in(0), 0);
1261 
1262  if (tr)
1263  f({get_ptr(seed)}, {get_ptr(sens)});
1264  else
1265  f.rev({get_ptr(sens)}, {get_ptr(seed)});
1266  // Temporaries for evaluation
1267  std::vector<bool> ret(sens.size());
1268  std::copy(sens.begin(), sens.end(), ret.begin());
1269 
1270  // Project the result back on the original sparsity
1271  if (tr && e.sparsity()!=expr.sparsity()) {
1272  // std::vector<bool> is not accessible as bool*
1273  // bool -> casadi_int
1274  std::vector<casadi_int> source(sens.size());
1275  std::copy(ret.begin(), ret.end(), source.begin());
1276  std::vector<casadi_int> target(expr.nnz());
1277 
1278  // project
1279  std::vector<casadi_int> scratch(expr.size1());
1280  casadi_project(get_ptr(source), e.sparsity(), get_ptr(target), expr.sparsity(),
1281  get_ptr(scratch));
1282 
1283  // casadi_int -> bool
1284  ret.resize(expr.nnz());
1285  std::copy(target.begin(), target.end(), ret.begin());
1286  }
1287 
1288  return ret;
1289  }
1290 
1291 } // namespace casadi
1293 #undef CASADI_THROW_ERROR
1294 
1295 #endif // CASADI_X_FUNCTION_HPP
std::map< std::string, std::vector< std::string > > AuxOut
Definition: function.hpp:395
The casadi namespace.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.