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>
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);
98 std::vector<MatType>
jac(
const Dict& opts)
const;
103 bool is_a(
const std::string& type,
bool recursive)
const override {
109 const std::vector<std::string>& s_in,
110 const std::vector<std::string>& s_out,
112 const Dict& opts)
const override;
121 const std::vector<std::string>& s_out,
122 casadi_int order,
bool tr=
false)
const override;
130 const std::vector<std::string>& inames,
131 const std::vector<std::string>& onames,
132 const Dict& opts)
const override;
141 const std::vector<std::string>& inames,
142 const std::vector<std::string>& onames,
143 const Dict& opts)
const override;
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;
167 const std::vector<std::pair<std::string, casadi_int> >& tasks)
const override;
176 const std::vector<std::pair<std::string, casadi_int> >& tasks,
177 std::vector<MatType>& new_in,
178 std::vector<MatType>& new_out)
const;
194 std::ostream &stream,
const Dict& options)
const override;
200 std::ostream &stream,
const Dict& options)
const = 0;
210 virtual bool isInput(
const std::vector<MatType>& arg)
const;
213 virtual bool should_inline(
bool with_sx,
bool always_inline,
bool never_inline)
const = 0;
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;
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;
284 template<
typename DerivedType,
typename MatType,
typename NodeType>
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)
293 if (!name_in.empty()) {
294 casadi_assert(ex_in.size()==name_in.size(),
295 "Mismatching number of input names");
299 if (!name_out.empty()) {
300 casadi_assert(ex_out.size()==name_out.size(),
301 "Mismatching number of output names");
306 template<
typename DerivedType,
typename MatType,
typename NodeType>
314 template<
typename DerivedType,
typename MatType,
typename NodeType>
317 s.
unpack(
"XFunction::out", out_);
320 template<
typename DerivedType,
typename MatType,
typename NodeType>
323 s.
pack(
"XFunction::out", out_);
326 template<
typename DerivedType,
typename MatType,
typename NodeType>
331 s.
pack(
"XFunction::in", in_);
335 template<
typename DerivedType,
typename MatType,
typename NodeType>
340 bool allow_duplicate_io_names =
false;
342 for (
auto&& op : opts) {
343 if (op.first==
"allow_duplicate_io_names") {
344 allow_duplicate_io_names = op.second;
348 if (verbose_) casadi_message(name_ +
"::init");
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.");
356 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
357 std::lock_guard<std::mutex> lock(MatType::get_mutex_temp());
361 bool has_duplicates =
false;
362 for (
auto&& i : in_) {
363 if (i.has_duplicates()) {
364 has_duplicates =
true;
369 for (
auto&& i : in_) i.reset_input();
371 if (has_duplicates) {
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";
377 casadi_error(s.str());
380 if (!allow_duplicate_io_names) {
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());
390 for (
size_t h : iohash) {
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());
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.");
411 template<
typename DerivedType,
typename MatType,
typename NodeType>
413 std::stack<NodeType*>& s, std::vector<NodeType*>& nodes) {
416 NodeType* t = s.top();
418 if (t && t->temp>=0) {
420 casadi_int next_dep = t->temp++;
422 if (next_dep < t->n_dep()) {
424 s.push(
static_cast<NodeType*
>(t->dep(next_dep).get()));
440 template<
typename DerivedType,
typename MatType,
typename NodeType>
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") {
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") {
461 casadi_error(
"No such Jacobian option: " + std::string(op.first));
466 std::vector<MatType> ret(n_in_ * n_out_);
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) {
473 ret[i * n_in_ + j] = MatType(nnz_out(i), nnz_in(j));
475 ret[i * n_in_ + j] = MatType(numel_out(i), numel_in(j));
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");
489 ret.at(0) = MatType::zeros(jac_sparsity(0, 0,
false, symmetric).
T());
490 if (verbose_) casadi_message(
"Allocated return value");
493 if (ret.at(0).nnz()==0) {
494 ret.at(0) = ret.at(0).T();
500 get_partition(iind, oind, D1, D2,
true, symmetric, allow_forward, allow_reverse);
501 if (verbose_) casadi_message(
"Graph coloring completed");
508 casadi_int max_nfdir = max_num_dir_;
509 casadi_int max_nadir = max_num_dir_;
512 casadi_int offset_nfdir = 0, offset_nadir = 0;
515 std::vector<MatType> res(out_);
518 std::vector<std::vector<MatType> > fseed, aseed, fsens, asens;
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();
526 std::vector<casadi_int> input_col = sparsity_in_.at(iind).get_col();
527 const casadi_int* input_row = sparsity_in_.at(iind).row();
530 std::vector<casadi_int> output_col = sparsity_out_.at(oind).get_col();
531 const casadi_int* output_row = sparsity_out_.at(oind).row();
534 if (verbose_) casadi_message(
"jac transposes and mapping");
535 std::vector<casadi_int> mapping;
542 std::vector<casadi_int> nzmap, nzmap2;
545 std::vector<casadi_int> adds, adds2;
548 std::vector<casadi_int> tmp;
551 casadi_int progress = -10;
554 casadi_int nsweep_fwd = nfdir/max_nfdir;
555 if (nfdir%max_nfdir>0) nsweep_fwd++;
556 casadi_int nsweep_adj = nadir/max_nadir;
557 if (nadir%max_nadir>0) nsweep_adj++;
558 casadi_int nsweep = std::max(nsweep_fwd, nsweep_adj);
560 casadi_message(
str(nsweep) +
" sweeps needed for " +
str(nfdir) +
" forward and "
561 +
str(nadir) +
" reverse directions");
565 std::vector<casadi_int> seed_col, seed_row;
568 for (casadi_int s=0; s<nsweep; ++s) {
571 casadi_int progress_new = (s*100)/nsweep;
573 if (progress_new / 10 > progress / 10) {
574 progress = progress_new;
575 casadi_message(
str(progress) +
" %");
580 casadi_int nfdir_batch = std::min(nfdir - offset_nfdir, max_nfdir);
581 casadi_int nadir_batch = std::min(nadir - offset_nadir, max_nadir);
584 fseed.resize(nfdir_batch);
585 for (casadi_int d=0; d<nfdir_batch; ++d) {
591 for (casadi_int el = D1.
colind(offset_nfdir+d); el<D1.
colind(offset_nfdir+d+1); ++el) {
594 casadi_int c = D1.
row(el);
597 seed_col.push_back(input_col[c]);
598 seed_row.push_back(input_row[c]);
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);
606 fseed[d][ind] = MatType::ones(
Sparsity::triplet(nrow, ncol, seed_row, seed_col));
608 fseed[d][ind] = MatType(nrow, ncol);
614 aseed.resize(nadir_batch);
615 for (casadi_int d=0; d<nadir_batch; ++d) {
621 for (casadi_int el = D2.
colind(offset_nadir+d); el<D2.
colind(offset_nadir+d+1); ++el) {
624 casadi_int c = D2.
row(el);
627 seed_col.push_back(output_col[c]);
628 seed_row.push_back(output_row[c]);
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);
636 aseed[d][ind] = MatType::ones(
Sparsity::triplet(nrow, ncol, seed_row, seed_col));
638 aseed[d][ind] = MatType(nrow, ncol);
644 fsens.resize(nfdir_batch);
645 for (casadi_int d=0; d<nfdir_batch; ++d) {
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));
654 asens.resize(nadir_batch);
655 for (casadi_int d=0; d<nadir_batch; ++d) {
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));
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'");
677 for (casadi_int d=0; d<nfdir_batch; ++d) {
679 if (fsens[d][oind].nnz()==0) {
686 tmp.resize(nnz_out(oind));
687 std::fill(tmp.begin(), tmp.end(), 0);
690 for (casadi_int el = D1.
colind(offset_nfdir+d); el<D1.
colind(offset_nfdir+d+1); ++el) {
693 casadi_int c = D1.
row(el);
696 for (casadi_int el_jsp=jsp_colind[c]; el_jsp<jsp_colind[c+1]; ++el_jsp) {
697 tmp[jsp_row[el_jsp]]++;
703 sparsity_out_.at(oind).find(nzmap);
704 fsens[d][oind].sparsity().get_nz(nzmap);
707 sparsity_in_.at(iind).find(nzmap2);
708 fsens[d][oind].sparsity().get_nz(nzmap2);
712 adds.resize(fsens[d][oind].nnz());
713 std::fill(adds.begin(), adds.end(), -1);
715 adds2.resize(adds.size());
716 std::fill(adds2.begin(), adds2.end(), -1);
720 for (casadi_int el = D1.
colind(offset_nfdir+d); el<D1.
colind(offset_nfdir+d+1); ++el) {
723 casadi_int c = D1.
row(el);
730 for (casadi_int el_out = jsp_trans.
colind(c); el_out<jsp_trans.
colind(c+1); ++el_out) {
733 casadi_int r_out = jsp_trans.
row(el_out);
736 casadi_int f_out = nzmap[r_out];
737 if (f_out<0)
continue;
740 casadi_int elJ = mapping[el_out];
744 adds[f_out] = el_out;
755 tmp.resize(adds.size());
757 for (casadi_int i=0; i<adds.size(); ++i) {
767 ret.at(0).nz(adds) = fsens[d][oind].nz(tmp);
771 tmp.resize(adds2.size());
773 for (casadi_int i=0; i<adds2.size(); ++i) {
775 adds2[sz] = adds2[i];
783 ret.at(0).nz(adds2) = fsens[d][oind].nz(tmp);
788 for (casadi_int d=0; d<nadir_batch; ++d) {
790 if (asens[d][iind].nnz()==0) {
795 sparsity_in_.at(iind).find(nzmap);
796 asens[d][iind].sparsity().get_nz(nzmap);
803 for (casadi_int el = D2.
colind(offset_nadir+d); el<D2.
colind(offset_nadir+d+1); ++el) {
806 casadi_int r = D2.
row(el);
809 for (casadi_int elJ = jsp.
colind(r); elJ<jsp.
colind(r+1); ++elJ) {
812 casadi_int inz = jsp.
row(elJ);
815 casadi_int anz = nzmap[inz];
825 ret.at(0).nz(adds) = asens[d][iind].nz(tmp);
829 offset_nfdir += nfdir_batch;
830 offset_nadir += nadir_batch;
834 for (MatType& Jb : ret) Jb = Jb.T();
837 }
catch (std::exception& e) {
838 CASADI_THROW_ERROR(
"jac", e.what());
842 template<
typename DerivedType,
typename MatType,
typename NodeType>
845 const std::vector<std::string>& inames,
846 const std::vector<std::string>& onames,
847 const Dict& opts)
const {
850 std::vector<std::vector<MatType> > fseed = fwd_seed<MatType>(nfwd), fsens;
853 static_cast<const DerivedType*
>(
this)->ad_forward(fseed, fsens);
854 casadi_assert_dev(fsens.size()==fseed.size());
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()));
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);
869 std::vector<MatType> ret_out(onames.size());
870 for (casadi_int i=0; i<n_out_; ++i) {
871 if (is_diff_out_[i]) {
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);
877 ret_out.at(i) = MatType(size1_out(i), size2_out(i) * nfwd);
882 options[
"allow_duplicate_io_names"] =
true;
884 return Function(name, ret_in, ret_out, inames, onames, options);
885 }
catch (std::exception& e) {
886 CASADI_THROW_ERROR(
"get_forward", e.what());
890 template<
typename DerivedType,
typename MatType,
typename NodeType>
893 const std::vector<std::string>& inames,
894 const std::vector<std::string>& onames,
895 const Dict& opts)
const {
898 std::vector<std::vector<MatType> > aseed = symbolicAdjSeed(nadj, out_), asens;
901 static_cast<const DerivedType*
>(
this)->ad_reverse(aseed, asens);
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()));
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);
916 std::vector<MatType> ret_out(onames.size());
917 for (casadi_int i=0; i<n_in_; ++i) {
918 if (is_diff_in_[i]) {
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);
924 ret_out.at(i) = MatType(size1_in(i), size2_in(i) * nadj);
929 options[
"allow_duplicate_io_names"] =
true;
931 return Function(name, ret_in, ret_out, inames, onames, options);
932 }
catch (std::exception& e) {
933 CASADI_THROW_ERROR(
"get_reverse", e.what());
937 template<
typename DerivedType,
typename MatType,
typename NodeType>
940 const std::vector<std::string>& inames,
941 const std::vector<std::string>& onames,
942 const Dict& opts)
const {
945 std::vector<MatType> diff_in =
vector_select(in_, is_diff_in_);
946 std::vector<MatType> diff_out =
vector_select(out_, is_diff_out_);
948 std::vector<MatType> non_diff_in =
vector_select(in_, is_diff_in_,
true);
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);
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));
961 Function tmp(
"flattened_" + name_, tmp_args, {veccat(diff_out)}, tmp_options);
963 MatType J = tmp.
get<DerivedType>()->jac(
Dict()).at(0);
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);
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));
982 ret_out.push_back(MatType(out_.at(i).numel(), in_.at(j).numel()));
985 if (is_diff_out_.at(i)) diff_i++;
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()));
996 options[
"allow_free"] =
true;
997 options[
"allow_duplicate_io_names"] =
true;
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;
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]);
1011 options[
"is_diff_out"] = is_diff_out;
1015 return Function(name, ret_in, ret_out, inames, onames, options);
1016 }
catch (std::exception& e) {
1017 CASADI_THROW_ERROR(
"get_jacobian", e.what());
1021 template<
typename DerivedType,
typename MatType,
typename 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 {
1026 std::vector<MatType> ret_in, ret_out;
1027 std::vector<std::string> ret_in_name, ret_out_name;
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));
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));
1042 return Function(name, ret_in, ret_out,
1043 ret_in_name, ret_out_name, opts);
1046 template<
typename DerivedType,
typename MatType,
typename NodeType>
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;
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") {
1063 std::vector<MatType> syms = MatType::symvar(veccat(new_out));
1065 for (MatType& e : new_in) {
1067 if (!contains_any(syms, MatType::symvar(e))) {
1069 e = MatType(e.size());
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);
1081 casadi_error(
"No such simplify task: '" + task +
"'.\n");
1083 if (count!=0)
continue;
1085 casadi_int nodes = MatType::n_nodes(veccat(new_out));
1086 if (prev_nodes != -1 && nodes >= prev_nodes)
break;
1092 template<
typename DerivedType,
typename MatType,
typename NodeType>
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);
1105 template<
typename DerivedType,
typename MatType,
typename NodeType>
1109 casadi_assert(!has_free(),
"export_code needs a Function without free variables");
1111 casadi_assert(lang==
"matlab",
"Only matlab language supported for now.");
1114 stream <<
"function [varargout] = " << name_ <<
"(varargin)" << std::endl;
1117 for (casadi_int i=0;i<n_out_;++i) {
1118 stream <<
" argout_" << i <<
" = cell(" << nnz_out(i) <<
",1);" << std::endl;
1122 opts[
"indent_level"] = 1;
1123 export_code_body(lang, stream, opts);
1126 for (casadi_int i=0;i<n_out_;++i) {
1127 const Sparsity& out = sparsity_out_.at(i);
1130 stream <<
" varargout{" << i+1 <<
"} = reshape(vertcat(argout_" << i <<
"{:}), ";
1131 stream << out.
size1() <<
", " << out.
size2() <<
");" << std::endl;
1135 opts[
"name"] =
"sp";
1136 opts[
"indent_level"] = 1;
1137 opts[
"as_matrix"] =
false;
1139 stream <<
" varargout{" << i+1 <<
"} = ";
1140 stream <<
"sparse(sp_i, sp_j, vertcat(argout_" << i <<
"{:}), sp_m, sp_n);" << std::endl;
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;
1173 template<
typename DerivedType,
typename MatType,
typename NodeType>
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)) {
1187 template<
typename DerivedType,
typename MatType,
typename NodeType>
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)) {
1198 always_inline, never_inline);
1202 if (fseed.empty()) {
1210 static_cast<const DerivedType*
>(
this)->ad_forward(fseed, fsens);
1213 Function f(
"tmp_call_forward", arg, res);
1214 static_cast<DerivedType *
>(f.
get())->ad_forward(fseed, fsens);
1218 template<
typename DerivedType,
typename MatType,
typename NodeType>
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)) {
1229 always_inline, never_inline);
1233 if (aseed.empty()) {
1241 static_cast<const DerivedType*
>(
this)->ad_reverse(aseed, asens);
1244 Function f(
"tmp_call_reverse", arg, res);
1245 static_cast<DerivedType *
>(f.
get())->ad_reverse(aseed, asens);
1249 template<
typename DerivedType,
typename MatType,
typename NodeType>
1251 factory(
const std::string& name,
1252 const std::vector<std::string>& s_in,
1253 const std::vector<std::string>& s_out,
1255 const Dict& opts)
const {
1257 Dict g_ops = generate_options(
"tmp");
1259 f_options[
"helper_options"] = g_ops;
1260 f_options[
"final_options"] = g_ops;
1265 final_options[
"allow_duplicate_io_names"] =
true;
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]);
1274 std::vector<std::string> ret_iname;
1275 for (
const std::string& s : s_in) {
1279 casadi_error(
"Cannot process factory input \"" + s +
"\":" + ex.
what());
1284 std::vector<std::string> ret_oname;
1285 for (
const std::string& s : s_out) {
1289 casadi_error(
"Cannot process factory output \"" + s +
"\":" + ex.
what());
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));
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));
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);
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);
1323 template<
typename DerivedType,
typename MatType,
typename NodeType>
1325 which_depends(
const std::string& s_in,
const std::vector<std::string>& s_out,
1326 casadi_int order,
bool tr)
const {
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());
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()));
1342 return MatType::which_depends(veccat(res), arg, order, tr);
1345 template<
typename MatType>
1347 Dict opts{{
"max_io", 0}, {
"allow_free",
true}};
1352 template<
typename MatType>
1354 casadi_int order,
bool tr) {
1356 if (expr.is_empty() || var.is_empty()) {
1357 return std::vector<bool>(tr? expr.numel() : var.numel(),
false);
1363 casadi_assert(order==1 || order==2,
1364 "which_depends: order argument must be 1 or 2, got " +
str(order) +
" instead.");
1366 MatType v = MatType::sym(
"v", var.sparsity());
1367 for (casadi_int i=1;i<order;++i) {
1368 e = jtimes(e, var, v);
1371 Dict opts{{
"max_io", 0}, {
"allow_free",
true}};
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);
1382 std::vector<bool> ret(sens.size());
1383 std::copy(sens.begin(), sens.end(), ret.begin());
1386 if (tr && e.sparsity()!=expr.sparsity()) {
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());
1394 std::vector<casadi_int> scratch(expr.size1());
1399 ret.resize(expr.nnz());
1400 std::copy(target.begin(), target.end(), ret.begin());
1408 #undef CASADI_THROW_ERROR
const char * what() const override
Display error.
Helper class for C code generation.
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
MatType get_output(const std::string &s)
std::string request_output(const std::string &s)
void add_dual(const Function::AuxOut &aux)
std::string request_input(const std::string &s)
void calculate(const Dict &opts=Dict())
MatType get_input(const std::string &s)
void add_input(const std::string &s, const MatType &e, bool is_diff)
void add_output(const std::string &s, const MatType &e, bool is_diff)
Internal class for Function.
void init(const Dict &opts) override
Initialize.
virtual void call_forward(const std::vector< MX > &arg, const std::vector< MX > &res, const std::vector< std::vector< MX > > &fseed, std::vector< std::vector< MX > > &fsens, bool always_inline, bool never_inline) const
Forward mode AD, virtual functions overloaded in derived classes.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
virtual void call_reverse(const std::vector< MX > &arg, const std::vector< MX > &res, const std::vector< std::vector< MX > > &aseed, std::vector< std::vector< MX > > &asens, bool always_inline, bool never_inline) const
Reverse mode, virtual functions overloaded in derived classes.
virtual bool is_a(const std::string &type, bool recursive) const
Check if the function is of a particular type.
std::vector< std::string > name_out_
std::vector< std::string > name_in_
Input and output scheme.
casadi_int nnz_out() const
Get number of output nonzeros.
FunctionInternal * get() const
int rev(bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w, int mem=0) const
Propagate sparsity backward.
bool has_free() const
Does the function have free variables.
casadi_int nnz_in() const
Get number of input nonzeros.
const std::vector< Sparsity > & jac_sparsity(bool compact=false) const
Get, if necessary generate, the sparsity of all Jacobian blocks.
std::map< std::string, std::vector< std::string > > AuxOut
bool is_null() const
Is a null pointer?
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
casadi_int size1() const
Get the number of rows.
Sparsity transpose(std::vector< casadi_int > &mapping, bool invert_mapping=false) const
Transpose the matrix and get the reordering of the non-zero entries.
Sparsity T() const
Transpose the matrix.
casadi_int size2() const
Get the number of columns.
const casadi_int * row() const
Get a reference to row-vector,.
void export_code(const std::string &lang, std::ostream &stream=casadi::uout(), const Dict &options=Dict()) const
Export matrix in specific language.
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
bool is_dense() const
Is dense?
static Sparsity triplet(casadi_int nrow, casadi_int ncol, const std::vector< casadi_int > &row, const std::vector< casadi_int > &col, std::vector< casadi_int > &mapping, bool invert_mapping)
Create a sparsity pattern given the nonzeros in sparse triplet form *.
Internal node class for the base class of SXFunction and MXFunction.
std::vector< bool > which_depends(const std::string &s_in, const std::vector< std::string > &s_out, casadi_int order, bool tr=false) const override
Which variables enter with some order.
std::vector< MatType > out_
Outputs of the function (needed for symbolic calculations)
void apply_simplify_passes(const std::vector< std::pair< std::string, casadi_int > > &tasks, std::vector< MatType > &new_in, std::vector< MatType > &new_out) const
Apply simplify passes in-place on a set of inputs/outputs.
void export_code(const std::string &lang, std::ostream &stream, const Dict &options) const override
Export function in a specific language.
void delayed_deserialize_members(DeserializingStream &s)
bool has_jacobian() const override
Return Jacobian of all input elements with respect to all output elements.
virtual bool should_inline(bool with_sx, bool always_inline, bool never_inline) const =0
void codegen_declarations(CodeGenerator &g) const override=0
Generate code for the declarations of the C function.
Function simplify_passes(const std::vector< std::pair< std::string, casadi_int > > &tasks) const override
Apply an ordered list of simplify passes.
bool has_codegen() const override
Is codegen supported?
Function get_jacobian(const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Return Jacobian of all input elements with respect to all output elements.
size_t get_n_out() override
Number of function inputs and outputs.
Function get_reverse(casadi_int nadj, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Generate a function that calculates nadj adjoint derivatives.
Sparsity get_sparsity_out(casadi_int i) override
Sparsities of function inputs and outputs.
void init(const Dict &opts) override
Initialize.
bool has_forward(casadi_int nfwd) const override
Generate a function that calculates nfwd forward derivatives.
void codegen_body(CodeGenerator &g) const override=0
Generate code for the body of the C function.
bool has_reverse(casadi_int nadj) const override
Generate a function that calculates nadj adjoint derivatives.
XFunction(const std::string &name, const std::vector< MatType > &ex_in, const std::vector< MatType > &ex_out, const std::vector< std::string > &name_in, const std::vector< std::string > &name_out)
Constructor.
virtual bool isInput(const std::vector< MatType > &arg) const
Helper function: Check if a vector equals ex_in.
bool has_spfwd() const override
Function slice(const std::string &name, const std::vector< casadi_int > &order_in, const std::vector< casadi_int > &order_out, const Dict &opts) const override
returns a new function with a selection of inputs/outputs of the original
std::vector< MatType > jac(const Dict &opts) const
Construct a complete Jacobian by compression.
std::vector< MatType > in_
Inputs of the function (needed for symbolic calculations)
~XFunction() override
Destructor.
size_t get_n_in() override
Number of function inputs and outputs.
Function factory(const std::string &name, const std::vector< std::string > &s_in, const std::vector< std::string > &s_out, const Function::AuxOut &aux, const Dict &opts) const override
void delayed_serialize_members(SerializingStream &s) const
Helper functions to avoid recursion limit.
void call_reverse(const std::vector< MatType > &arg, const std::vector< MatType > &res, const std::vector< std::vector< MatType > > &aseed, std::vector< std::vector< MatType > > &asens, bool always_inline, bool never_inline) const override
Create call to (cached) derivative function, reverse mode.
virtual void export_code_body(const std::string &lang, std::ostream &stream, const Dict &options) const =0
Export function body in a specific language.
Function get_forward(casadi_int nfwd, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Generate a function that calculates nfwd forward derivatives.
XFunction(DeserializingStream &s)
Deserializing constructor.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
bool has_sprev() const override
Sparsity get_sparsity_in(casadi_int i) override
Sparsities of function inputs and outputs.
void call_forward(const std::vector< MatType > &arg, const std::vector< MatType > &res, const std::vector< std::vector< MatType > > &fseed, std::vector< std::vector< MatType > > &fsens, bool always_inline, bool never_inline) const override
Create call to (cached) derivative function, forward mode.
static void sort_depth_first(std::stack< NodeType * > &s, std::vector< NodeType * > &nodes)
Topological sorting of the nodes based on Depth-First Search (DFS)
bool is_a(const std::string &type, bool recursive) const override
Check if the function is of a particular type.
bool is_equal(double x, double y, casadi_int depth=0)
std::vector< T > vector_tail(const std::vector< T > &v)
Return all but the first element of a vector.
std::string join(const std::vector< std::string > &l, const std::string &delim)
std::vector< bool > _which_depends(const MatType &expr, const MatType &var, casadi_int order, bool tr)
void casadi_project(const T1 *x, const casadi_int *sp_x, T1 *y, const casadi_int *sp_y, T1 *w)
Sparse copy: y <- x, w work vector (length >= number of rows)
Sparsity _jacobian_sparsity(const MatType &expr, const MatType &var)
void extract_from_dict_inplace(Dict &d, const std::string &key, T &value)
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
bool any(const std::vector< bool > &v)
Check if any arguments are true.
void update_dict(Dict &target, const Dict &source, bool recurse)
Update the target dictionary in place with source elements.
std::vector< T > vector_select(const std::vector< T > &v, const std::vector< bool > &s, bool invert=false)
Select subset of vector.
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.