26 #ifndef CASADI_X_FUNCTION_HPP
27 #define CASADI_X_FUNCTION_HPP
30 #include "function_internal.hpp"
31 #include "factory.hpp"
32 #include "serializing_stream.hpp"
35 #include <unordered_map>
36 #define SPARSITY_MAP std::unordered_map
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"\
56 template<
typename DerivedType,
typename MatType,
typename NodeType>
57 class CASADI_EXPORT XFunction :
public FunctionInternal {
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);
72 ~XFunction()
override {
78 void init(
const Dict& opts)
override;
82 bool has_spfwd()
const override {
return true;}
83 bool has_sprev()
const override {
return true;}
93 static void sort_depth_first(std::stack<NodeType*>& s, std::vector<NodeType*>& nodes);
98 std::vector<MatType> jac(
const Dict& opts)
const;
103 bool is_a(
const std::string& type,
bool recursive)
const override {
104 return type==
"xfunction" || (recursive && FunctionInternal::is_a(type, recursive));
108 Function factory(
const std::string& name,
109 const std::vector<std::string>& s_in,
110 const std::vector<std::string>& s_out,
112 const Dict& opts)
const override;
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;
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;
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;
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;
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;
166 void codegen_declarations(CodeGenerator& g)
const override = 0;
171 void codegen_body(CodeGenerator& g)
const override = 0;
176 void export_code(
const std::string& lang,
177 std::ostream &stream,
const Dict& options)
const override;
182 virtual void export_code_body(
const std::string& lang,
183 std::ostream &stream,
const Dict& options)
const = 0;
188 bool has_codegen()
const override {
return true;}
193 virtual bool isInput(
const std::vector<MatType>& arg)
const;
196 virtual bool should_inline(
bool with_sx,
bool always_inline,
bool never_inline)
const = 0;
201 void call_forward(
const std::vector<MatType>& arg,
202 const std::vector<MatType>& res,
203 const std::vector<std::vector<MatType> >& fseed,
204 std::vector<std::vector<MatType> >& fsens,
205 bool always_inline,
bool never_inline)
const override;
210 void call_reverse(
const std::vector<MatType>& arg,
211 const std::vector<MatType>& res,
212 const std::vector<std::vector<MatType> >& aseed,
213 std::vector<std::vector<MatType> >& asens,
214 bool always_inline,
bool never_inline)
const override;
220 size_t get_n_in()
override {
return in_.size(); }
221 size_t get_n_out()
override {
return out_.size(); }
228 Sparsity get_sparsity_in(casadi_int i)
override {
return in_.at(i).sparsity();}
229 Sparsity get_sparsity_out(casadi_int i)
override {
return out_.at(i).sparsity();}
235 explicit XFunction(DeserializingStream& s);
239 void serialize_body(SerializingStream &s)
const override;
248 void delayed_serialize_members(SerializingStream &s)
const;
249 void delayed_deserialize_members(DeserializingStream &s);
257 std::vector<MatType> in_;
262 std::vector<MatType> out_;
267 template<
typename DerivedType,
typename MatType,
typename NodeType>
268 XFunction<DerivedType, MatType, NodeType>::
269 XFunction(
const std::string& name,
270 const std::vector<MatType>& ex_in,
271 const std::vector<MatType>& ex_out,
272 const std::vector<std::string>& name_in,
273 const std::vector<std::string>& name_out)
274 : FunctionInternal(name), in_(ex_in), out_(ex_out) {
276 if (!name_in.empty()) {
277 casadi_assert(ex_in.size()==name_in.size(),
278 "Mismatching number of input names");
282 if (!name_out.empty()) {
283 casadi_assert(ex_out.size()==name_out.size(),
284 "Mismatching number of output names");
285 name_out_ = name_out;
289 template<
typename DerivedType,
typename MatType,
typename NodeType>
290 XFunction<DerivedType, MatType, NodeType>::
291 XFunction(DeserializingStream& s) : FunctionInternal(s) {
292 s.version(
"XFunction", 1);
293 s.unpack(
"XFunction::in", in_);
297 template<
typename DerivedType,
typename MatType,
typename NodeType>
298 void XFunction<DerivedType, MatType, NodeType>::
299 delayed_deserialize_members(DeserializingStream& s) {
300 s.unpack(
"XFunction::out", out_);
303 template<
typename DerivedType,
typename MatType,
typename NodeType>
304 void XFunction<DerivedType, MatType, NodeType>::
305 delayed_serialize_members(SerializingStream& s)
const {
306 s.pack(
"XFunction::out", out_);
309 template<
typename DerivedType,
typename MatType,
typename NodeType>
310 void XFunction<DerivedType, MatType, NodeType>::
311 serialize_body(SerializingStream& s)
const {
312 FunctionInternal::serialize_body(s);
313 s.version(
"XFunction", 1);
314 s.pack(
"XFunction::in", in_);
318 template<
typename DerivedType,
typename MatType,
typename NodeType>
319 void XFunction<DerivedType, MatType, NodeType>::init(
const Dict& opts) {
321 FunctionInternal::init(opts);
323 bool allow_duplicate_io_names =
false;
325 for (
auto&& op : opts) {
326 if (op.first==
"allow_duplicate_io_names") {
327 allow_duplicate_io_names = op.second;
331 if (verbose_) casadi_message(name_ +
"::init");
333 for (casadi_int i=0; i<n_in_; ++i) {
334 if (in_.at(i).nnz()>0 && !in_.at(i).is_valid_input()) {
335 casadi_error(
"For " + this->name_ +
": Xfunction input arguments must be purely symbolic."
336 "\nArgument " + str(i) +
"(" + name_in_[i] +
") is not symbolic.");
339 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
340 std::lock_guard<std::mutex> lock(MatType::get_mutex_temp());
344 bool has_duplicates =
false;
345 for (
auto&& i : in_) {
346 if (i.has_duplicates()) {
347 has_duplicates =
true;
352 for (
auto&& i : in_) i.reset_input();
354 if (has_duplicates) {
356 s <<
"The input expressions are not independent:\n";
357 for (casadi_int iind=0; iind<in_.size(); ++iind) {
358 s << iind <<
": " << in_[iind] <<
"\n";
360 casadi_error(s.str());
363 if (!allow_duplicate_io_names) {
365 std::hash<std::string> hasher;
366 std::vector<size_t> iohash;
367 iohash.reserve(name_in_.size() + name_out_.size());
368 for (
const std::string& s : name_in_) iohash.push_back(hasher(s));
369 for (
const std::string& s : name_out_) iohash.push_back(hasher(s));
370 std::sort(iohash.begin(), iohash.end());
373 for (
size_t h : iohash) {
376 std::vector<std::string> io_names;
377 io_names.reserve(iohash.size());
378 for (
const std::string& s : name_in_) io_names.push_back(s);
379 for (
const std::string& s : name_out_) io_names.push_back(s);
380 std::sort(io_names.begin(), io_names.end());
383 for (std::string h : io_names) {
384 if (h == prev) casadi_error(
"Duplicate IO name: " + h +
". "
385 "To ignore this error, set 'allow_duplicate_io_names' option.");
394 template<
typename DerivedType,
typename MatType,
typename NodeType>
395 void XFunction<DerivedType, MatType, NodeType>::sort_depth_first(
396 std::stack<NodeType*>& s, std::vector<NodeType*>& nodes) {
399 NodeType* t = s.top();
401 if (t && t->temp>=0) {
403 casadi_int next_dep = t->temp++;
405 if (next_dep < t->n_dep()) {
407 s.push(
static_cast<NodeType*
>(t->dep(next_dep).get()));
423 template<
typename DerivedType,
typename MatType,
typename NodeType>
424 std::vector<MatType> XFunction<DerivedType, MatType, NodeType>
425 ::jac(
const Dict& opts)
const {
428 bool compact =
false;
429 bool symmetric =
false;
430 bool allow_forward =
true;
431 bool allow_reverse =
true;
432 for (
auto&& op : opts) {
433 if (op.first==
"compact") {
435 }
else if (op.first==
"symmetric") {
436 symmetric = op.second;
437 }
else if (op.first==
"allow_forward") {
438 allow_forward = op.second;
439 }
else if (op.first==
"allow_reverse") {
440 allow_reverse = op.second;
441 }
else if (op.first==
"verbose") {
444 casadi_error(
"No such Jacobian option: " + std::string(op.first));
449 std::vector<MatType> ret(n_in_ * n_out_);
452 if (nnz_in() == 0 || nnz_out() == 0) {
453 for (casadi_int i = 0; i < n_out_; ++i) {
454 for (casadi_int j = 0; j < n_in_; ++j) {
456 ret[i * n_in_ + j] = MatType(nnz_out(i), nnz_in(j));
458 ret[i * n_in_ + j] = MatType(numel_out(i), numel_in(j));
466 casadi_int iind = 0, oind = 0;
467 casadi_assert(n_in_ == 1,
"Not implemented");
468 casadi_assert(n_out_ == 1,
"Not implemented");
471 ret.at(0) = MatType::zeros(jac_sparsity(0, 0,
false, symmetric).
T());
472 if (verbose_) casadi_message(
"Allocated return value");
475 if (ret.at(0).nnz()==0) {
476 ret.at(0) = ret.at(0).T();
482 get_partition(iind, oind, D1, D2,
true, symmetric, allow_forward, allow_reverse);
483 if (verbose_) casadi_message(
"Graph coloring completed");
486 casadi_int nfdir = D1.is_null() ? 0 : D1.size2();
487 casadi_int nadir = D2.is_null() ? 0 : D2.size2();
490 casadi_int max_nfdir = max_num_dir_;
491 casadi_int max_nadir = max_num_dir_;
494 casadi_int offset_nfdir = 0, offset_nadir = 0;
497 std::vector<MatType> res(out_);
500 std::vector<std::vector<MatType> > fseed, aseed, fsens, asens;
503 Sparsity jsp = jac_sparsity(0, 0,
true, symmetric).T();
504 const casadi_int* jsp_colind = jsp.colind();
505 const casadi_int* jsp_row = jsp.row();
508 std::vector<casadi_int> input_col = sparsity_in_.at(iind).get_col();
509 const casadi_int* input_row = sparsity_in_.at(iind).row();
512 std::vector<casadi_int> output_col = sparsity_out_.at(oind).get_col();
513 const casadi_int* output_row = sparsity_out_.at(oind).row();
516 if (verbose_) casadi_message(
"jac transposes and mapping");
517 std::vector<casadi_int> mapping;
520 jsp_trans = jsp.transpose(mapping);
524 std::vector<casadi_int> nzmap, nzmap2;
527 std::vector<casadi_int> adds, adds2;
530 std::vector<casadi_int> tmp;
533 casadi_int progress = -10;
536 casadi_int nsweep_fwd = nfdir/max_nfdir;
537 if (nfdir%max_nfdir>0) nsweep_fwd++;
538 casadi_int nsweep_adj = nadir/max_nadir;
539 if (nadir%max_nadir>0) nsweep_adj++;
540 casadi_int nsweep = std::max(nsweep_fwd, nsweep_adj);
542 casadi_message(str(nsweep) +
" sweeps needed for " + str(nfdir) +
" forward and "
543 + str(nadir) +
" reverse directions");
547 std::vector<casadi_int> seed_col, seed_row;
550 for (casadi_int s=0; s<nsweep; ++s) {
553 casadi_int progress_new = (s*100)/nsweep;
555 if (progress_new / 10 > progress / 10) {
556 progress = progress_new;
557 casadi_message(str(progress) +
" %");
562 casadi_int nfdir_batch = std::min(nfdir - offset_nfdir, max_nfdir);
563 casadi_int nadir_batch = std::min(nadir - offset_nadir, max_nadir);
566 fseed.resize(nfdir_batch);
567 for (casadi_int d=0; d<nfdir_batch; ++d) {
573 for (casadi_int el = D1.colind(offset_nfdir+d); el<D1.colind(offset_nfdir+d+1); ++el) {
576 casadi_int c = D1.row(el);
579 seed_col.push_back(input_col[c]);
580 seed_row.push_back(input_row[c]);
584 fseed[d].resize(n_in_);
585 for (casadi_int ind=0; ind<fseed[d].size(); ++ind) {
586 casadi_int nrow = size1_in(ind), ncol = size2_in(ind);
588 fseed[d][ind] = MatType::ones(Sparsity::triplet(nrow, ncol, seed_row, seed_col));
590 fseed[d][ind] = MatType(nrow, ncol);
596 aseed.resize(nadir_batch);
597 for (casadi_int d=0; d<nadir_batch; ++d) {
603 for (casadi_int el = D2.colind(offset_nadir+d); el<D2.colind(offset_nadir+d+1); ++el) {
606 casadi_int c = D2.row(el);
609 seed_col.push_back(output_col[c]);
610 seed_row.push_back(output_row[c]);
614 aseed[d].resize(n_out_);
615 for (casadi_int ind=0; ind<aseed[d].size(); ++ind) {
616 casadi_int nrow = size1_out(ind), ncol = size2_out(ind);
618 aseed[d][ind] = MatType::ones(Sparsity::triplet(nrow, ncol, seed_row, seed_col));
620 aseed[d][ind] = MatType(nrow, ncol);
626 fsens.resize(nfdir_batch);
627 for (casadi_int d=0; d<nfdir_batch; ++d) {
629 fsens[d].resize(n_out_);
630 for (casadi_int oind=0; oind<fsens[d].size(); ++oind) {
631 fsens[d][oind] = MatType::zeros(sparsity_out_.at(oind));
636 asens.resize(nadir_batch);
637 for (casadi_int d=0; d<nadir_batch; ++d) {
639 asens[d].resize(n_in_);
640 for (casadi_int ind=0; ind<asens[d].size(); ++ind) {
641 asens[d][ind] = MatType::zeros(sparsity_in_.at(ind));
646 if (!fseed.empty()) {
647 casadi_assert_dev(aseed.empty());
648 if (verbose_) casadi_message(
"Calling 'ad_forward'");
649 static_cast<const DerivedType*
>(
this)->ad_forward(fseed, fsens);
650 if (verbose_) casadi_message(
"Back from 'ad_forward'");
651 }
else if (!aseed.empty()) {
652 casadi_assert_dev(fseed.empty());
653 if (verbose_) casadi_message(
"Calling 'ad_reverse'");
654 static_cast<const DerivedType*
>(
this)->ad_reverse(aseed, asens);
655 if (verbose_) casadi_message(
"Back from 'ad_reverse'");
659 for (casadi_int d=0; d<nfdir_batch; ++d) {
661 if (fsens[d][oind].nnz()==0) {
668 tmp.resize(nnz_out(oind));
669 std::fill(tmp.begin(), tmp.end(), 0);
672 for (casadi_int el = D1.colind(offset_nfdir+d); el<D1.colind(offset_nfdir+d+1); ++el) {
675 casadi_int c = D1.row(el);
678 for (casadi_int el_jsp=jsp_colind[c]; el_jsp<jsp_colind[c+1]; ++el_jsp) {
679 tmp[jsp_row[el_jsp]]++;
685 sparsity_out_.at(oind).find(nzmap);
686 fsens[d][oind].sparsity().get_nz(nzmap);
689 sparsity_in_.at(iind).find(nzmap2);
690 fsens[d][oind].sparsity().get_nz(nzmap2);
694 adds.resize(fsens[d][oind].nnz());
695 std::fill(adds.begin(), adds.end(), -1);
697 adds2.resize(adds.size());
698 std::fill(adds2.begin(), adds2.end(), -1);
702 for (casadi_int el = D1.colind(offset_nfdir+d); el<D1.colind(offset_nfdir+d+1); ++el) {
705 casadi_int c = D1.row(el);
712 for (casadi_int el_out = jsp_trans.colind(c); el_out<jsp_trans.colind(c+1); ++el_out) {
715 casadi_int r_out = jsp_trans.row(el_out);
718 casadi_int f_out = nzmap[r_out];
719 if (f_out<0)
continue;
722 casadi_int elJ = mapping[el_out];
726 adds[f_out] = el_out;
737 tmp.resize(adds.size());
739 for (casadi_int i=0; i<adds.size(); ++i) {
749 ret.at(0).nz(adds) = fsens[d][oind].nz(tmp);
753 tmp.resize(adds2.size());
755 for (casadi_int i=0; i<adds2.size(); ++i) {
757 adds2[sz] = adds2[i];
765 ret.at(0).nz(adds2) = fsens[d][oind].nz(tmp);
770 for (casadi_int d=0; d<nadir_batch; ++d) {
772 if (asens[d][iind].nnz()==0) {
777 sparsity_in_.at(iind).find(nzmap);
778 asens[d][iind].sparsity().get_nz(nzmap);
781 for (casadi_int el = D2.colind(offset_nadir+d); el<D2.colind(offset_nadir+d+1); ++el) {
784 casadi_int r = D2.row(el);
787 for (casadi_int elJ = jsp.colind(r); elJ<jsp.colind(r+1); ++elJ) {
790 casadi_int inz = jsp.row(elJ);
793 casadi_int anz = nzmap[inz];
797 ret.at(0).nz(elJ) = asens[d][iind].nz(anz);
803 offset_nfdir += nfdir_batch;
804 offset_nadir += nadir_batch;
808 for (MatType& Jb : ret) Jb = Jb.T();
811 }
catch (std::exception& e) {
812 CASADI_THROW_ERROR(
"jac", e.what());
816 template<
typename DerivedType,
typename MatType,
typename NodeType>
817 Function XFunction<DerivedType, MatType, NodeType>
818 ::get_forward(casadi_int nfwd,
const std::string& name,
819 const std::vector<std::string>& inames,
820 const std::vector<std::string>& onames,
821 const Dict& opts)
const {
824 std::vector<std::vector<MatType> > fseed = fwd_seed<MatType>(nfwd), fsens;
827 static_cast<const DerivedType*
>(
this)->ad_forward(fseed, fsens);
828 casadi_assert_dev(fsens.size()==fseed.size());
831 std::vector<MatType> ret_in(inames.size());
832 std::copy(in_.begin(), in_.end(), ret_in.begin());
833 for (casadi_int i=0; i<n_out_; ++i) {
834 ret_in.at(n_in_+i) = MatType::sym(inames[n_in_+i], Sparsity(out_.at(i).size()));
836 std::vector<MatType> v(nfwd);
837 for (casadi_int i=0; i<n_in_; ++i) {
838 for (casadi_int d=0; d<nfwd; ++d) v[d] = fseed[d][i];
839 ret_in.at(n_in_ + n_out_ + i) = horzcat(v);
843 std::vector<MatType> ret_out(onames.size());
844 for (casadi_int i=0; i<n_out_; ++i) {
845 if (is_diff_out_[i]) {
847 for (casadi_int d=0; d<nfwd; ++d) v[d] = fsens[d][i];
848 ret_out.at(i) = ensure_stacked(horzcat(v), sparsity_out(i), nfwd);
851 ret_out.at(i) = MatType(size1_out(i), size2_out(i) * nfwd);
856 if (opts.find(
"is_diff_in")==opts.end())
857 options[
"is_diff_in"] = join(is_diff_in_, is_diff_out_, is_diff_in_);
858 if (opts.find(
"is_diff_out")==opts.end())
859 options[
"is_diff_out"] = is_diff_out_;
860 options[
"allow_duplicate_io_names"] =
true;
862 return Function(name, ret_in, ret_out, inames, onames, options);
863 }
catch (std::exception& e) {
864 CASADI_THROW_ERROR(
"get_forward", e.what());
868 template<
typename DerivedType,
typename MatType,
typename NodeType>
869 Function XFunction<DerivedType, MatType, NodeType>
870 ::get_reverse(casadi_int nadj,
const std::string& name,
871 const std::vector<std::string>& inames,
872 const std::vector<std::string>& onames,
873 const Dict& opts)
const {
876 std::vector<std::vector<MatType> > aseed = symbolicAdjSeed(nadj, out_), asens;
879 static_cast<const DerivedType*
>(
this)->ad_reverse(aseed, asens);
882 std::vector<MatType> ret_in(inames.size());
883 std::copy(in_.begin(), in_.end(), ret_in.begin());
884 for (casadi_int i=0; i<n_out_; ++i) {
885 ret_in.at(n_in_ + i) = MatType::sym(inames[n_in_+i], Sparsity(out_.at(i).size()));
887 std::vector<MatType> v(nadj);
888 for (casadi_int i=0; i<n_out_; ++i) {
889 for (casadi_int d=0; d<nadj; ++d) v[d] = aseed[d][i];
890 ret_in.at(n_in_ + n_out_ + i) = horzcat(v);
894 std::vector<MatType> ret_out(onames.size());
895 for (casadi_int i=0; i<n_in_; ++i) {
896 if (is_diff_in_[i]) {
898 for (casadi_int d=0; d<nadj; ++d) v[d] = asens[d][i];
899 ret_out.at(i) = ensure_stacked(horzcat(v), sparsity_in(i), nadj);
902 ret_out.at(i) = MatType(size1_in(i), size2_in(i) * nadj);
907 if (opts.find(
"is_diff_in")==opts.end())
908 options[
"is_diff_in"] = join(is_diff_in_, is_diff_out_, is_diff_out_);
909 if (opts.find(
"is_diff_out")==opts.end())
910 options[
"is_diff_out"] = is_diff_in_;
912 options[
"allow_duplicate_io_names"] =
true;
914 return Function(name, ret_in, ret_out, inames, onames, options);
915 }
catch (std::exception& e) {
916 CASADI_THROW_ERROR(
"get_reverse", e.what());
920 template<
typename DerivedType,
typename MatType,
typename NodeType>
921 Function XFunction<DerivedType, MatType, NodeType>
922 ::get_jacobian(
const std::string& name,
923 const std::vector<std::string>& inames,
924 const std::vector<std::string>& onames,
925 const Dict& opts)
const {
927 Dict tmp_options = generate_options(
"tmp");
928 tmp_options[
"allow_free"] =
true;
929 tmp_options[
"allow_duplicate_io_names"] =
true;
931 Function tmp(
"flattened_" + name, {veccat(in_)}, {veccat(out_)}, tmp_options);
934 MatType J = tmp.get<DerivedType>()->jac(
Dict()).at(0);
937 std::vector<casadi_int> r_offset = {0}, c_offset = {0};
938 for (
auto& e : out_) r_offset.push_back(r_offset.back() + e.numel());
939 for (
auto& e : in_) c_offset.push_back(c_offset.back() + e.numel());
940 auto Jblocks = MatType::blocksplit(J, r_offset, c_offset);
943 std::vector<MatType> ret_out;
944 ret_out.reserve(onames.size());
945 for (casadi_int i = 0; i < n_out_; ++i) {
946 for (casadi_int j = 0; j < n_in_; ++j) {
947 MatType b = Jblocks.at(i).at(j);
948 if (!is_diff_out_.at(i) || !is_diff_in_.at(j)) {
949 b = MatType(b.size());
951 ret_out.push_back(b);
956 std::vector<MatType> ret_in(inames.size());
957 std::copy(in_.begin(), in_.end(), ret_in.begin());
958 for (casadi_int i=0; i<n_out_; ++i) {
959 ret_in.at(n_in_+i) = MatType::sym(inames[n_in_+i], Sparsity(out_.at(i).size()));
963 options[
"allow_free"] =
true;
964 options[
"allow_duplicate_io_names"] =
true;
967 return Function(name, ret_in, ret_out, inames, onames, options);
968 }
catch (std::exception& e) {
969 CASADI_THROW_ERROR(
"get_jacobian", e.what());
973 template<
typename DerivedType,
typename MatType,
typename NodeType>
974 Function XFunction<DerivedType, MatType, NodeType>
975 ::slice(
const std::string& name,
const std::vector<casadi_int>& order_in,
976 const std::vector<casadi_int>& order_out,
const Dict& opts)
const {
978 std::vector<MatType> ret_in, ret_out;
979 std::vector<std::string> ret_in_name, ret_out_name;
982 for (casadi_int k : order_in) {
983 ret_in.push_back(in_.at(k));
984 ret_in_name.push_back(name_in_.at(k));
988 for (casadi_int k : order_out) {
989 ret_out.push_back(out_.at(k));
990 ret_out_name.push_back(name_out_.at(k));
994 return Function(name, ret_in, ret_out,
995 ret_in_name, ret_out_name, opts);
998 template<
typename DerivedType,
typename MatType,
typename NodeType>
999 void XFunction<DerivedType, MatType, NodeType>
1000 ::export_code(
const std::string& lang, std::ostream &stream,
const Dict& options)
const {
1002 casadi_assert(!has_free(),
"export_code needs a Function without free variables");
1004 casadi_assert(lang==
"matlab",
"Only matlab language supported for now.");
1007 stream <<
"function [varargout] = " << name_ <<
"(varargin)" << std::endl;
1010 for (casadi_int i=0;i<n_out_;++i) {
1011 stream <<
" argout_" << i <<
" = cell(" << nnz_out(i) <<
",1);" << std::endl;
1015 opts[
"indent_level"] = 1;
1016 export_code_body(lang, stream, opts);
1019 for (casadi_int i=0;i<n_out_;++i) {
1020 const Sparsity& out = sparsity_out_.at(i);
1021 if (out.is_dense()) {
1023 stream <<
" varargout{" << i+1 <<
"} = reshape(vertcat(argout_" << i <<
"{:}), ";
1024 stream << out.size1() <<
", " << out.size2() <<
");" << std::endl;
1028 opts[
"name"] =
"sp";
1029 opts[
"indent_level"] = 1;
1030 opts[
"as_matrix"] =
false;
1031 out.export_code(
"matlab", stream, opts);
1032 stream <<
" varargout{" << i+1 <<
"} = ";
1033 stream <<
"sparse(sp_i, sp_j, vertcat(argout_" << i <<
"{:}), sp_m, sp_n);" << std::endl;
1038 stream <<
"end" << std::endl;
1039 stream <<
"function y=nonzeros_gen(x)" << std::endl;
1040 stream <<
" if isa(x,'casadi.SX') || isa(x,'casadi.MX') || isa(x,'casadi.DM')" << std::endl;
1041 stream <<
" y = x{:};" << std::endl;
1042 stream <<
" elseif isa(x,'sdpvar')" << std::endl;
1043 stream <<
" b = getbase(x);" << std::endl;
1044 stream <<
" f = find(sum(b~=0,2));" << std::endl;
1045 stream <<
" y = sdpvar(length(f),1,[],getvariables(x),b(f,:));" << std::endl;
1046 stream <<
" else" << std::endl;
1047 stream <<
" y = nonzeros(x);" << std::endl;
1048 stream <<
" end" << std::endl;
1049 stream <<
"end" << std::endl;
1050 stream <<
"function y=if_else_zero_gen(c,e)" << std::endl;
1051 stream <<
" if isa(c+e,'casadi.SX') || isa(c+e,'casadi.MX') "
1052 "|| isa(c+e,'casadi.DM')" << std::endl;
1053 stream <<
" y = if_else(c, e, 0);" << std::endl;
1054 stream <<
" else" << std::endl;
1055 stream <<
" if c" << std::endl;
1056 stream <<
" y = x;" << std::endl;
1057 stream <<
" else" << std::endl;
1058 stream <<
" y = 0;" << std::endl;
1059 stream <<
" end" << std::endl;
1060 stream <<
" end" << std::endl;
1061 stream <<
"end" << std::endl;
1066 template<
typename DerivedType,
typename MatType,
typename NodeType>
1067 bool XFunction<DerivedType, MatType, NodeType>
1068 ::isInput(
const std::vector<MatType>& arg)
const {
1071 const casadi_int checking_depth = 2;
1072 for (casadi_int i=0; i<arg.size(); ++i) {
1073 if (!is_equal(arg[i], in_[i], checking_depth)) {
1080 template<
typename DerivedType,
typename MatType,
typename NodeType>
1081 void XFunction<DerivedType, MatType, NodeType>::
1082 call_forward(
const std::vector<MatType>& arg,
1083 const std::vector<MatType>& res,
1084 const std::vector<std::vector<MatType> >& fseed,
1085 std::vector<std::vector<MatType> >& fsens,
1086 bool always_inline,
bool never_inline)
const {
1087 casadi_assert(!(always_inline && never_inline),
"Inconsistent options");
1088 if (!should_inline(MatType::type_name()==
"SX", always_inline, never_inline)) {
1090 return FunctionInternal::call_forward(arg, res, fseed, fsens,
1091 always_inline, never_inline);
1095 if (fseed.empty()) {
1103 static_cast<const DerivedType*
>(
this)->ad_forward(fseed, fsens);
1106 Function f(
"tmp_call_forward", arg, res);
1107 static_cast<DerivedType *
>(f.get())->ad_forward(fseed, fsens);
1111 template<
typename DerivedType,
typename MatType,
typename NodeType>
1112 void XFunction<DerivedType, MatType, NodeType>::
1113 call_reverse(
const std::vector<MatType>& arg,
1114 const std::vector<MatType>& res,
1115 const std::vector<std::vector<MatType> >& aseed,
1116 std::vector<std::vector<MatType> >& asens,
1117 bool always_inline,
bool never_inline)
const {
1118 casadi_assert(!(always_inline && never_inline),
"Inconsistent options");
1119 if (!should_inline(MatType::type_name()==
"SX", always_inline, never_inline)) {
1121 return FunctionInternal::call_reverse(arg, res, aseed, asens,
1122 always_inline, never_inline);
1126 if (aseed.empty()) {
1134 static_cast<const DerivedType*
>(
this)->ad_reverse(aseed, asens);
1137 Function f(
"tmp_call_reverse", arg, res);
1138 static_cast<DerivedType *
>(f.get())->ad_reverse(aseed, asens);
1142 template<
typename DerivedType,
typename MatType,
typename NodeType>
1143 Function XFunction<DerivedType, MatType, NodeType>::
1144 factory(
const std::string& name,
1145 const std::vector<std::string>& s_in,
1146 const std::vector<std::string>& s_out,
1147 const Function::AuxOut& aux,
1148 const Dict& opts)
const {
1150 Dict g_ops = generate_options(
"clone");
1152 f_options[
"helper_options"] = g_ops;
1153 f_options[
"final_options"] = g_ops;
1154 update_dict(f_options, opts,
true);
1157 extract_from_dict_inplace(f_options,
"final_options", final_options);
1158 final_options[
"allow_duplicate_io_names"] =
true;
1162 for (casadi_int i=0; i<in_.size(); ++i) f.add_input(name_in_[i], in_[i], is_diff_in_[i]);
1163 for (casadi_int i=0; i<out_.size(); ++i) f.add_output(name_out_[i], out_[i], is_diff_out_[i]);
1167 std::vector<std::string> ret_iname;
1168 for (
const std::string& s : s_in) {
1170 ret_iname.push_back(f.request_input(s));
1171 }
catch (CasadiException& ex) {
1172 casadi_error(
"Cannot process factory input \"" + s +
"\":" + ex.what());
1177 std::vector<std::string> ret_oname;
1178 for (
const std::string& s : s_out) {
1180 ret_oname.push_back(f.request_output(s));
1181 }
catch (CasadiException& ex) {
1182 casadi_error(
"Cannot process factory output \"" + s +
"\":" + ex.what());
1187 f.calculate(f_options);
1190 std::vector<MatType> ret_in;
1191 ret_in.reserve(s_in.size());
1192 for (
const std::string& s : s_in) ret_in.push_back(f.get_input(s));
1195 std::vector<MatType> ret_out;
1196 ret_out.reserve(s_out.size());
1197 for (
const std::string& s : s_out) ret_out.push_back(f.get_output(s));
1200 Dict final_options_allow_free = final_options;
1201 final_options_allow_free[
"allow_free"] =
true;
1202 final_options_allow_free[
"allow_duplicate_io_names"] =
true;
1203 Function ret(name, ret_in, ret_out, ret_iname, ret_oname, final_options_allow_free);
1204 if (ret.has_free()) {
1207 std::vector<MatType> free_in = MatType::get_free(ret);
1208 std::vector<MatType> free_sub = free_in;
1209 for (
auto&& e : free_sub) e = MatType::zeros(e.sparsity());
1210 ret_out = substitute(ret_out, free_in, free_sub);
1211 ret = Function(name, ret_in, ret_out, ret_iname, ret_oname, final_options);
1216 template<
typename DerivedType,
typename MatType,
typename NodeType>
1217 std::vector<bool> XFunction<DerivedType, MatType, NodeType>::
1218 which_depends(
const std::string& s_in,
const std::vector<std::string>& s_out,
1219 casadi_int order,
bool tr)
const {
1222 auto it = std::find(name_in_.begin(), name_in_.end(), s_in);
1223 casadi_assert_dev(it!=name_in_.end());
1224 MatType arg = in_.at(it-name_in_.begin());
1227 std::vector<MatType> res;
1228 for (
auto&& s : s_out) {
1229 it = std::find(name_out_.begin(), name_out_.end(), s);
1230 casadi_assert_dev(it!=name_out_.end());
1231 res.push_back(out_.at(it-name_out_.begin()));
1235 return MatType::which_depends(veccat(res), arg, order, tr);
1238 template<
typename MatType>
1239 Sparsity _jacobian_sparsity(
const MatType &expr,
const MatType &var) {
1240 Dict opts{{
"max_io", 0}, {
"allow_free",
true}};
1241 Function f = Function(
"tmp_jacobian_sparsity", {var}, {expr}, opts);
1242 return f.jac_sparsity(0, 0,
false);
1245 template<
typename MatType>
1246 std::vector<bool> _which_depends(
const MatType &expr,
const MatType &var,
1247 casadi_int order,
bool tr) {
1249 if (expr.is_empty() || var.is_empty()) {
1250 return std::vector<bool>(tr? expr.numel() : var.numel(),
false);
1256 casadi_assert(order==1 || order==2,
1257 "which_depends: order argument must be 1 or 2, got " + str(order) +
" instead.");
1259 MatType v = MatType::sym(
"v", var.sparsity());
1260 for (casadi_int i=1;i<order;++i) {
1261 e = jtimes(e, var, v);
1264 Dict opts{{
"max_io", 0}, {
"allow_free",
true}};
1265 Function f = Function(
"tmp_which_depends", {var}, {e}, opts);
1267 std::vector<bvec_t> seed(tr? f.nnz_in(0) : f.nnz_out(0), 1);
1268 std::vector<bvec_t> sens(tr? f.nnz_out(0) : f.nnz_in(0), 0);
1271 f({get_ptr(seed)}, {get_ptr(sens)});
1273 f.rev({get_ptr(sens)}, {get_ptr(seed)});
1275 std::vector<bool> ret(sens.size());
1276 std::copy(sens.begin(), sens.end(), ret.begin());
1279 if (tr && e.sparsity()!=expr.sparsity()) {
1282 std::vector<casadi_int> source(sens.size());
1283 std::copy(ret.begin(), ret.end(), source.begin());
1284 std::vector<casadi_int> target(expr.nnz());
1287 std::vector<casadi_int> scratch(expr.size1());
1288 casadi_project(get_ptr(source), e.sparsity(), get_ptr(target), expr.sparsity(),
1292 ret.resize(expr.nnz());
1293 std::copy(target.begin(), target.end(), ret.begin());
1301 #undef CASADI_THROW_ERROR
std::map< std::string, std::vector< std::string > > AuxOut
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.