25 #define CASADI_SX_INSTANTIATOR_CPP
26 #include "matrix_impl.hpp"
28 #include "sx_function.hpp"
29 #include "output_sx.hpp"
30 #include "linsol_internal.hpp"
37 casadi_assert(
numel()==1,
38 "Only scalar Matrix could have a truth value, but you "
39 "provided a shape" +
dim());
40 return nonzeros().at(0).__nonzero__();
56 std::vector<SXElem> retv;
62 std::string modname =
name;
63 for (std::string::iterator it=modname.begin(); it!=modname.end(); ++it) {
65 case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case ',':
case ';': *it =
' ';
69 std::istringstream iss(modname);
86 for (casadi_int k=0; k<sp.
nnz(); ++k) {
88 ss <<
name <<
"_" << k;
97 return SX(sp, retv,
false);
104 for (casadi_int i=0; i<
nnz(); ++i) {
111 for (casadi_int i=0; i<
nnz(); ++i) {
112 if (!
nonzeros().at(i).is_regular())
return false;
120 Function temp(
"tmp_is_smooth", {
SX()}, {*
this},
Dict{{
"max_io", 0}, {
"allow_free",
true}});
129 return scalar().__hash__();
134 return scalar().is_leaf();
139 return scalar().is_commutative();
144 for (casadi_int k=0; k<
nnz(); ++k)
145 if (!
nonzeros().at(k)->is_symbolic())
153 return scalar().is_call();
158 return scalar().is_output();
163 return scalar().has_output();
168 return scalar().get_output(oind);
173 return scalar().which_function();
178 return scalar().which_output();
191 casadi_int CASADI_EXPORT
SX::op()
const {
202 for (
auto&& i : nonzeros_) {
203 bool is_duplicate = i.get_temp()!=0;
205 casadi_warning(
"Duplicate expression: " +
str(i));
214 for (
auto&& i : nonzeros_) {
237 "expand requires a scalar expression. Got " + ex2.
dim() +
" instead.");
241 std::vector<std::vector<SXNode*> > terms;
242 std::vector<std::vector<double> > weights;
243 std::map<SXNode*, casadi_int> indices;
246 std::stack<SXNode*> to_be_expanded;
247 to_be_expanded.push(ex.
get());
249 while (!to_be_expanded.empty()) {
252 if (indices.find(to_be_expanded.top()) != indices.end()) {
254 to_be_expanded.pop();
259 std::vector<double> w;
260 std::vector<SXNode*> f;
262 if (to_be_expanded.top()->is_constant()) {
263 w.push_back(to_be_expanded.top()->to_double());
265 }
else if (to_be_expanded.top()->is_symbolic()) {
268 f.push_back(to_be_expanded.top());
271 casadi_assert_dev(to_be_expanded.top()->n_dep());
274 SXNode* node = to_be_expanded.top();
277 (node->op() ==
OP_MUL && (node->dep(0)->is_constant() ||
278 node->dep(1)->is_constant()))) {
280 if (indices.find(node->dep(0).get()) == indices.end()) {
281 to_be_expanded.push(node->dep(0).get());
284 if (indices.find(node->dep(1).get()) == indices.end()) {
285 to_be_expanded.push(node->dep(1).get());
290 casadi_int ind1 = indices[node->dep(0).get()];
291 casadi_int ind2 = indices[node->dep(1).get()];
294 if (node->op() ==
OP_MUL) {
297 if (node->dep(0)->is_constant()) {
298 fac = node->dep(0)->to_double();
302 fac = node->dep(1)->to_double();
306 for (casadi_int i=0; i<w.size(); ++i) w[i] *= fac;
309 if (node->op() ==
OP_ADD) {
310 f = terms[ind1]; f.insert(f.end(), terms[ind2].begin(), terms[ind2].end());
311 w = weights[ind1]; w.insert(w.end(), weights[ind2].begin(), weights[ind2].end());
313 f = terms[ind1]; f.insert(f.end(), terms[ind2].begin(), terms[ind2].end());
316 for (casadi_int i=0; i<weights[ind2].size(); ++i) w.push_back(-weights[ind2][i]);
319 std::vector<double> w_new; w_new.reserve(w.size());
320 std::vector<SXNode*> f_new; f_new.reserve(f.size());
321 std::map<SXNode*, casadi_int> f_ind;
323 for (casadi_int i=0; i<w.size(); i++) {
325 auto it = f_ind.find(f[i]);
326 if (it == f_ind.end()) {
327 w_new.push_back(w[i]);
328 f_new.push_back(f[i]);
329 f_ind[f[i]] = f_new.size()-1;
331 w_new[it->second] += w[i];
346 weights.push_back(w);
348 indices[to_be_expanded.top()] = terms.size()-1;
351 to_be_expanded.pop();
355 casadi_int thisind = indices[ex.
get()];
356 ww =
SX(weights[thisind]);
358 std::vector<SXElem> termsv(terms[thisind].
size());
359 for (casadi_int i=0; i<termsv.size(); ++i)
367 casadi_int n = val.numel();
369 casadi_assert(t.is_scalar(),
"t must be a scalar");
370 casadi_assert(tval.numel() == n-1,
"dimensions do not match");
373 for (casadi_int i=0; i<n-1; ++i) {
374 ret += (val(i+1)-val(i)) * (t>=tval(i));
383 casadi_int N = tval.numel();
384 casadi_assert(N>=2,
"pw_lin: N>=2");
385 casadi_assert(val.numel() == N,
"dimensions do not match");
389 for (casadi_int i=0; i<N-1; ++i)
390 g(i) = (val(i+1)- val(i))/(tval(i+1)-tval(i));
393 SX lseg =
SX(1, N-1);
394 for (casadi_int i=0; i<N-1; ++i)
395 lseg(i) = val(i) + g(i)*(t-tval(i));
403 const SX& b, casadi_int order,
const SX& w) {
404 casadi_assert(order == 5,
"gauss_quadrature: order must be 5");
405 casadi_assert(w.is_empty(),
"gauss_quadrature: empty weights");
412 Function fcn(
"gauss_quadrature", {x}, {f});
418 std::vector<double> xi;
419 xi.push_back(-std::sqrt(5 + 2*std::sqrt(10.0/7))/3);
420 xi.push_back(-std::sqrt(5 - 2*std::sqrt(10.0/7))/3);
422 xi.push_back(std::sqrt(5 - 2*std::sqrt(10.0/7))/3);
423 xi.push_back(std::sqrt(5 + 2*std::sqrt(10.0/7))/3);
426 std::vector<double> wi;
427 wi.push_back((322-13*std::sqrt(70.0))/900.0);
428 wi.push_back((322+13*std::sqrt(70.0))/900.0);
429 wi.push_back(128/225.0);
430 wi.push_back((322+13*std::sqrt(70.0))/900.0);
431 wi.push_back((322-13*std::sqrt(70.0))/900.0);
434 Function fcn(
"gauss_quadrature", {x}, {f});
435 std::vector<SXElem> f_val(5);
436 for (casadi_int i=0; i<5; ++i)
437 f_val[i] = fcn(
SX(xi[i])).at(0).scalar();
441 for (casadi_int i=0; i<5; ++i)
442 sum += wi[i]*f_val[i];
449 std::vector<SX>& res,
452 for (casadi_int el=0; el<r.nnz(); ++el) {
455 expand(r.nz(el), weights, terms);
458 r.nz(el) =
mtimes(terms.T(), weights);
467 for (casadi_int el=0; el<r.nnz(); ++el) {
470 expand(r.nz(el), weights, terms);
473 r.nz(el) =
mtimes(terms.T(), weights);
480 return transform(std::vector<SX>{x}, opts).at(0);
485 const std::vector<std::vector<GenericType> >& passes,
const Dict& opts) {
486 return transform(std::vector<SX>{x}, passes, opts).at(0);
490 std::vector<SX> CASADI_EXPORT
SX::transform(
const std::vector<SX>& x,
const Dict& opts) {
493 Function f(
"transform", arg, x,
494 {{
"allow_free",
true}, {
"allow_duplicate_io_names",
true}});
495 f = f.transform(opts);
500 std::vector<SX> CASADI_EXPORT
SX::transform(
const std::vector<SX>& x,
501 const std::vector<std::vector<GenericType> >& passes,
const Dict& opts) {
504 Function f(
"transform", arg, x,
505 {{
"allow_free",
true}, {
"allow_duplicate_io_names",
true}});
506 f = f.transform(passes, opts);
511 std::vector<SX> CASADI_EXPORT
512 SX::substitute(
const std::vector<SX>& ex,
const std::vector<SX>& v,
const std::vector<SX>& vdef) {
515 if (v.size()!=vdef.size()) {
516 casadi_warning(
"subtitute: number of symbols to replace ( " +
str(v.size()) +
") "
517 "must match number of expressions (" +
str(vdef.size()) +
") "
518 "to replace them with.");
522 bool all_equal =
true;
523 for (casadi_int k=0; k<v.size(); ++k) {
529 if (all_equal)
return ex;
532 for (casadi_int k=0; k<v.size(); ++k) {
536 std::vector<SX> vdef_mod = vdef;
537 vdef_mod[k] =
SX(v[k].
sparsity(), vdef[k]->at(0),
false);
540 casadi_error(
"Sparsities of v and vdef must match. Got v: "
541 + v[k].
dim() +
" and vdef: " + vdef[k].
dim() +
".");
548 Function F(
"tmp_substitute", v, ex,
Dict{{
"max_io", 0}, {
"allow_free",
true}});
554 return substitute(std::vector<SX>{ex}, std::vector<SX>{v}, std::vector<SX>{vdef}).front();
559 std::vector<SX >& ex,
bool reverse) {
561 casadi_assert_dev(v.size()==vdef.size());
562 for (casadi_int i=0; i<v.size(); ++i) {
563 casadi_assert(v[i].
is_symbolic(),
"the variable is not symbolic");
564 casadi_assert(v[i].
sparsity() == vdef[i].
sparsity(),
"the sparsity patterns of the "
565 "expression and its defining bexpression do not match");
569 if (v.empty())
return;
572 std::vector<SX> f_in;
573 if (!
reverse) f_in.insert(f_in.end(), v.begin(), v.end());
576 std::vector<SX> f_out = vdef;
577 f_out.insert(f_out.end(), ex.begin(), ex.end());
580 Function f(
"tmp_substitute_inplace", f_in, f_out,
Dict{{
"max_io", 0}, {
"allow_free",
true}});
583 SXFunction *ff = f.get<SXFunction>();
584 const std::vector<ScalarAtomic>& algorithm = ff->algorithm_;
585 std::vector<SXElem> work(f.sz_w());
588 std::vector<SXElem>::const_iterator b_it=ff->operations_.begin();
591 std::vector<SXElem>::const_iterator c_it = ff->constants_.begin();
594 std::vector<SXElem>::const_iterator p_it = ff->free_vars_.begin();
597 for (std::vector<ScalarAtomic>::const_iterator it=algorithm.begin(); it<algorithm.end(); ++it) {
601 work[it->i0] = vdef.at(it->i1)->at(it->i2);
604 if (it->i0 < v.size()) {
605 vdef.at(it->i0)->at(it->i2) = work[it->i1];
608 work[it->i1] = v.at(it->i0)->at(it->i2);
612 ex.at(it->i0 - v.size())->at(it->i2) = work[it->i1];
615 case OP_CONST: work[it->i0] = *c_it++;
break;
620 CASADI_MATH_FUN_BUILTIN(work[it->i1], work[it->i2], work[it->i0])
624 const casadi_int depth = 2;
625 work[it->i0].assignIfDuplicate(*b_it++, depth);
632 std::vector<SXElem>& symbol_v, std::vector<SXElem>& parametric_v,
633 bool extract_trivial, casadi_int v_offset,
634 const std::string& v_prefix,
const std::string& v_suffix) {
637 auto it = symbol_map.
find(node.
get());
641 if (is_trivial && !extract_trivial) {
645 if (it==symbol_map.end()) {
648 symbol_map[node.
get()] = sym;
651 symbol_v.push_back(sym);
652 parametric_v.push_back(node);
664 SX& expr_ret, std::vector<SX>& symbols, std::vector<SX>& parametric,
const Dict& opts) {
665 std::string v_prefix =
"e_";
666 std::string v_suffix =
"";
667 bool extract_trivial =
false;
668 casadi_int v_offset = 0;
669 for (
auto&&
op : opts) {
670 if (
op.first ==
"prefix") {
671 v_prefix = std::string(
op.second);
672 }
else if (
op.first ==
"suffix") {
673 v_suffix = std::string(
op.second);
674 }
else if (
op.first ==
"offset") {
675 v_offset =
op.second;
676 }
else if (
op.first ==
"extract_trivial") {
677 extract_trivial =
op.second;
679 casadi_error(
"No such option: " + std::string(
op.first));
682 Function f(
"f", std::vector<SX>{par},
683 std::vector<SX>{expr}, {{
"live_variables",
false},
684 {
"max_io", 0}, {
"allow_free",
true}});
685 SXFunction *ff = f.get<SXFunction>();
688 std::vector< SXElem > w(ff->worksize_);
694 std::vector< char > expr_status(ff->worksize_, 0);
697 std::vector<SXElem>::const_iterator b_it=ff->operations_.begin();
700 std::vector<SXElem>::const_iterator c_it = ff->constants_.begin();
703 std::vector<SXElem>::const_iterator p_it = ff->free_vars_.begin();
706 const SXElem* arg =
get_ptr(par.nonzeros());
710 std::vector<SXElem>& ret = expr_ret.nonzeros();
713 std::map<SXNode*, SXElem> symbol_map;
716 std::vector<SXElem> symbol_v, parametric_v;
719 for (
auto&& a : ff->algorithm_) {
723 expr_status[a.i0] = 1;
726 casadi_assert_dev(a.i0==0);
728 SXElem arg = w[a.i1];
729 if (expr_status[a.i1]==1) {
731 extract_trivial, v_offset, v_prefix, v_suffix);
738 expr_status[a.i0] = 0;
742 expr_status[a.i0] = 2;
746 const auto& m = ff->call_.el.at(a.i1);
747 const SXElem& orig = *b_it++;
748 std::vector<SXElem> deps(m.n_dep);
750 bool identical =
true;
751 for (casadi_int i=0;i<m.n_dep;++i) {
757 for (casadi_int i=0;i<m.n_dep;++i) {
758 max_status = std::max(max_status, expr_status[m.dep[i]]);
761 bool any_tainted = max_status==2;
765 for (casadi_int i=0;i<m.n_dep;++i) {
767 if (expr_status[m.dep[i]]==2)
continue;
769 if (expr_status[m.dep[i]]==0)
continue;
771 w[m.dep[i]] =
register_symbol(w[m.dep[i]], symbol_map, symbol_v, parametric_v,
772 extract_trivial, v_offset, v_prefix, v_suffix);
778 std::vector<SXElem> ret;
781 for (casadi_int i=0;i<m.n_res;++i) {
782 ret.push_back(orig.get_output(i));
785 for (casadi_int i=0;i<m.n_dep;++i) deps[i] = w[m.dep[i]];
790 for (casadi_int i=0;i<m.n_res;++i) {
791 if (m.res[i]>=0) expr_status[m.res[i]] = max_status;
794 for (casadi_int i=0;i<m.n_res;++i) {
795 if (m.res[i]>=0) w[m.res[i]] = ret[i];
804 SXElem w2 = is_binary ? w[a.i2] : 0;
806 char max_status = expr_status[a.i1];
808 max_status = std::max(max_status, expr_status[a.i2]);
810 bool any_tainted = max_status==2;
814 for (
int k=0;k<1+is_binary;++k) {
816 casadi_int el = k==0 ? a.i1 : a.i2;
817 if (expr_status[el]==2)
continue;
819 if (expr_status[el]==0)
continue;
821 SXElem& arg = k==0 ? w1 : w2;
824 extract_trivial, v_offset, v_prefix, v_suffix);
832 CASADI_MATH_FUN_BUILTIN(w1, w2, f)
838 const casadi_int depth = 2;
839 w[a.i0].assignIfDuplicate(*b_it++, depth);
842 expr_status[a.i0] = max_status;
847 symbols.resize(symbol_v.size());
848 parametric.resize(parametric_v.size());
850 for (casadi_int i=0;i<symbol_v.size();++i) {
851 symbols[i] = symbol_v[i];
852 parametric[i] = parametric_v[i];
858 const SX &sym_lin,
const SX &sym_const,
859 SX& expr_const,
SX& expr_lin,
SX& expr_nonlin) {
861 Function f(
"f", std::vector<SX>{sym_const, sym_lin},
862 std::vector<SX>{expr}, {{
"live_variables",
false},
864 SXFunction *ff = f.get<SXFunction>();
869 expr_nonlin =
SX::zeros(expr.sparsity());
871 std::vector<SXElem*> ret = {
872 get_ptr(expr_const.nonzeros()),
874 get_ptr(expr_nonlin.nonzeros())};
877 std::vector< std::array<SXElem, 3> > w(ff->worksize_,
878 std::array<SXElem, 3>{{0, 0, 0}});
880 std::vector<const SXElem*> arg(f.sz_arg());
881 arg[0] =
get_ptr(sym_const.nonzeros());
882 arg[1] =
get_ptr(sym_lin.nonzeros());
885 std::vector<SXElem>::const_iterator c_it = ff->constants_.begin();
888 std::vector<SXElem>::const_iterator p_it = ff->free_vars_.begin();
891 for (
auto&& a : ff->algorithm_) {
894 w[a.i0][a.i1] = arg[a.i1]==
nullptr ? 0 : arg[a.i1][a.i2];
897 casadi_assert_dev(a.i0==0);
898 ret[0][a.i2] = w[a.i1][0];
899 ret[1][a.i2] = w[a.i1][1];
900 ret[2][a.i2] = w[a.i1][2];
903 w[a.i0][0] = *c_it++;
906 w[a.i0][2] = *p_it++;
909 casadi_error(
"Not implemented");
918 bool CASADI_EXPORT SX::depends_on(
const SX &x,
const SX &arg) {
919 if (x.nnz()==0)
return false;
922 Function temp(
"tmp_depends_on", {arg}, {x},
Dict{{
"max_io", 0}, {
"allow_free",
true}});
925 std::vector<bvec_t> t_in(arg.nnz(), 1), t_out(x.nnz());
929 for (casadi_int i=0; i<t_out.size(); ++i) {
930 if (t_out[i])
return true;
937 bool CASADI_EXPORT SX::contains_all(
const std::vector<SX>& v,
const std::vector<SX> &n) {
938 if (n.empty())
return true;
942 for (
const SX& e : v) l.insert(e.scalar().get());
944 size_t l_unique = l.size();
946 for (
const SX& e : n) l.insert(e.scalar().get());
948 return l.size()==l_unique;
952 bool CASADI_EXPORT SX::contains_any(
const std::vector<SX>& v,
const std::vector<SX> &n) {
953 if (n.empty())
return true;
957 for (
const SX& e : v) l.insert(e.scalar().get());
959 size_t l_unique = l.size();
962 for (
const SX& e : n) r.insert(e.scalar().get());
964 size_t r_unique = r.size();
965 for (
const SX& e : n) l.insert(e.scalar().get());
967 return l.size()<l_unique+r_unique;
970 class IncrementalSerializer {
978 IncrementalSerializer() : serializer(ss) {
981 std::string pack(
const SXElem& a) {
984 a.serialize(serializer);
987 a.serialize(serializer);
988 std::string ret = ss.str();
995 std::stringstream ss;
997 std::vector<SXElem> ref;
998 SerializingStream serializer;
1003 std::vector<SX> CASADI_EXPORT SX::cse(
const std::vector<SX>& e) {
1007 Function f(
"f", std::vector<SX>{}, e, {{
"live_variables",
false},
1008 {
"max_io", 0}, {
"cse",
false}, {
"allow_free",
true}});
1009 SXFunction *ff = f.get<SXFunction>();
1011 std::vector<SX> ret;
1012 for (casadi_int i=0;i<e.size();++i) {
1013 ret.push_back(SX::zeros(e.at(i).sparsity()));
1017 std::vector<SXElem> w(ff->worksize_);
1019 std::vector<const SXElem*> arg(f.sz_arg());
1024 std::vector<SXElem*> res(f.sz_res());
1025 for (casadi_int i=0;i<e.size();++i) {
1026 res[i] =
get_ptr(ret.at(i).nonzeros());
1029 std::unordered_map<std::string, SXElem > cache;
1030 IncrementalSerializer s;
1033 std::vector<SXElem>::const_iterator b_it = ff->operations_.begin();
1037 for (
auto&& a : ff->algorithm_) {
1047 const SXElem &f = *b_it++;
1048 std::string key = s.pack(f);
1050 auto itk = cache.find(key);
1051 if (itk==cache.end()) {
1059 std::vector<SXElem>::const_iterator c_it = ff->constants_.begin();
1062 std::vector<SXElem>::const_iterator p_it = ff->free_vars_.begin();
1064 std::unordered_map<std::string, Function> function_cache;
1067 for (
auto&& a : ff->algorithm_) {
1070 w[a.i0] = arg[a.i1]==
nullptr ? 0 : arg[a.i1][a.i2];
1071 if (arg[a.i1]!=
nullptr) cache[s.pack(w[a.i0])] = w[a.i0];
1074 if (res[a.i0]!=
nullptr) res[a.i0][a.i2] = w[a.i1];
1078 cache[s.pack(w[a.i0])] = w[a.i0];
1082 cache[s.pack(w[a.i0])] = w[a.i0];
1086 const auto& m = ff->call_.el.at(a.i1);
1089 std::vector<SXElem> deps(m.n_dep);
1090 for (casadi_int i=0;i<m.n_dep;++i) deps[i] = w[m.dep[i]];
1093 std::string key = m.f.serialize();
1094 auto itk = function_cache.find(key);
1095 if (itk==function_cache.end()) {
1096 function_cache[key] = m.f;
1100 std::vector<SXElem> ret = SXElem::call(function_cache[key], deps);
1102 SXElem call_node = ret[0].dep(0);
1105 key = s.pack(call_node);
1106 auto it = cache.find(key);
1107 if (it==cache.end()) {
1109 cache[key] = call_node;
1112 call_node = it->second;
1114 for (casadi_int i=0; i<ret.size(); ++i) {
1116 ret[i] = call_node.get_output(ret[i].which_output());
1121 for (casadi_int i=0;i<m.n_res;++i) {
1122 if (m.res[i]>=0) w[m.res[i]] = ret[i];
1134 CASADI_MATH_FUN_BUILTIN(w[a.i1], w[a.i2], f)
1136 casadi_error(
"Not implemented");
1139 std::string key = s.pack(f);
1141 auto itk = cache.find(key);
1142 if (itk==cache.end()) {
1157 SX CASADI_EXPORT SX::jacobian(
const SX &f,
const SX &x,
const Dict& opts) {
1161 h_opts[
"allow_free"] =
true;
1162 Function h(
"jac_helper", {x}, {f}, h_opts);
1163 return h.get<SXFunction>()->jac(opts_remainder).at(0);
1167 SX CASADI_EXPORT SX::hessian(
const SX &ex,
const SX &arg, SX &g,
const Dict& opts) {
1168 Dict all_opts = opts;
1169 if (!opts.count(
"symmetric")) all_opts[
"symmetric"] =
true;
1170 g = gradient(ex, arg);
1171 return jacobian(g, arg, all_opts);
1175 SX CASADI_EXPORT SX::hessian(
const SX &ex,
const SX &arg,
const Dict& opts) {
1177 return hessian(ex, arg, g, opts);
1181 std::vector<std::vector<SX> > CASADI_EXPORT
1182 SX::forward(
const std::vector<SX> &ex,
const std::vector<SX> &arg,
1183 const std::vector<std::vector<SX> > &v,
const Dict& opts) {
1187 h_opts[
"allow_free"] =
true;
1189 bool always_inline =
false;
1190 bool never_inline =
false;
1191 for (
auto&& op : opts_remainder) {
1192 if (op.first==
"always_inline") {
1193 always_inline = op.second;
1194 }
else if (op.first==
"never_inline") {
1195 never_inline = op.second;
1197 casadi_error(
"No such option: " + std::string(op.first));
1201 Function temp(
"forward_temp", arg, ex, h_opts);
1202 std::vector<std::vector<SX> > ret;
1203 temp->call_forward(arg, ex, v, ret, always_inline, never_inline);
1208 std::vector<std::vector<SX> > CASADI_EXPORT
1209 SX::reverse(
const std::vector<SX> &ex,
const std::vector<SX> &arg,
1210 const std::vector<std::vector<SX> > &v,
const Dict& opts) {
1214 h_opts[
"allow_free"] =
true;
1216 bool always_inline =
false;
1217 bool never_inline =
false;
1218 for (
auto&& op : opts_remainder) {
1219 if (op.first==
"always_inline") {
1220 always_inline = op.second;
1221 }
else if (op.first==
"never_inline") {
1222 never_inline = op.second;
1224 casadi_error(
"No such option: " + std::string(op.first));
1228 Function temp(
"reverse_temp", arg, ex, h_opts);
1229 std::vector<std::vector<SX> > ret;
1230 temp->call_reverse(arg, ex, v, ret, always_inline, never_inline);
1235 std::vector<bool> CASADI_EXPORT SX::which_depends(
const SX &expr,
1236 const SX &var, casadi_int order,
bool tr) {
1241 Sparsity CASADI_EXPORT SX::jacobian_sparsity(
const SX &f,
const SX &x) {
1246 SX CASADI_EXPORT SX::taylor(
const SX& f,
const SX& x,
1247 const SX& a, casadi_int order) {
1248 casadi_assert_dev(x.is_scalar() && a.is_scalar());
1249 if (f.nnz()!=f.numel())
1250 throw CasadiException(
"taylor: not implemented for sparse matrices");
1253 SX result = substitute(ff, x, a);
1257 for (casadi_int i=1; i<=order; i++) {
1258 ff = jacobian(ff, x);
1259 nf*=
static_cast<double>(i);
1260 result+=1/nf * substitute(ff, x, a) * dxa;
1263 return reshape(result, f.size2(), f.size1()).T();
1267 const std::vector<casadi_int>&order_contributions,
1269 double current_denom=1, casadi_int current_order=1) {
1270 SX result = substitute(ex, x, a)*current_dx/current_denom;
1271 for (casadi_int i=0;i<x.
nnz();i++) {
1272 if (order_contributions[i]<=order) {
1275 order-order_contributions[i],
1276 order_contributions,
1277 current_dx*(x->at(i)-a->at(i)),
1278 current_denom*
static_cast<double>(current_order),
1286 SX CASADI_EXPORT SX::mtaylor(
const SX& f,
const SX& x,
const SX& a, casadi_int order,
1287 const std::vector<casadi_int>& order_contributions) {
1288 casadi_assert(f.nnz()==f.numel() && x.nnz()==x.numel(),
1289 "mtaylor: not implemented for sparse matrices");
1291 casadi_assert(a.sparsity()==x.sparsity(),
1292 "mtaylor: expected a of shape " + x.dim() +
", but got " + a.dim() +
".");
1294 casadi_assert(x.nnz()==order_contributions.size(),
1295 "mtaylor: number of non-zero elements in x (" + str(x.nnz())
1296 +
") must match size of order_contributions ("
1297 + str(order_contributions.size()) +
")");
1299 return reshape(mtaylor_recursive(vec(f), x, a, order,
1300 order_contributions),
1301 f.
size2(), f.size1()).T();
1305 SX CASADI_EXPORT SX::mtaylor(
const SX& f,
const SX& x,
const SX& a, casadi_int order) {
1306 return mtaylor(f, x, a, order, std::vector<casadi_int>(x.nnz(), 1));
1310 casadi_int CASADI_EXPORT SX::n_nodes(
const SX& x) {
1311 Dict opts{{
"max_io", 0}, {
"cse",
false}, {
"allow_free",
true}};
1312 Function f(
"tmp_n_nodes", {
SX()}, {x}, opts);
1317 std::string CASADI_EXPORT
1318 SX::print_operator(
const SX& X,
const std::vector<std::string>& args) {
1319 SXElem x =
X.scalar();
1320 casadi_int ndeps = casadi_math<double>::ndeps(x.op());
1321 casadi_assert(ndeps==1 || ndeps==2,
"Not a unary or binary operator");
1322 casadi_assert(args.size()==ndeps,
"Wrong number of arguments");
1324 return casadi_math<double>::print(x.op(), args.at(0));
1326 return casadi_math<double>::print(x.op(), args.at(0), args.at(1));
1331 std::vector<SX> CASADI_EXPORT SX::symvar(
const SX& x) {
1332 Dict opts{{
"max_io", 0}, {
"cse",
false}, {
"allow_free",
true}};
1333 Function f(
"tmp_symvar", std::vector<SX>{}, {x}, opts);
1338 void CASADI_EXPORT SX::extract(std::vector<SX>& ex, std::vector<SX>& v_sx,
1339 std::vector<SX>& vdef_sx,
const Dict& opts) {
1341 std::string v_prefix =
"v_", v_suffix =
"";
1342 bool lift_shared =
true, lift_calls =
false;
1343 casadi_int v_ind = 0;
1344 for (
auto&& op : opts) {
1345 if (op.first ==
"prefix") {
1346 v_prefix = std::string(op.second);
1347 }
else if (op.first ==
"suffix") {
1348 v_suffix = std::string(op.second);
1349 }
else if (op.first ==
"lift_shared") {
1350 lift_shared = op.second;
1351 }
else if (op.first ==
"lift_calls") {
1352 lift_calls = op.second;
1353 }
else if (op.first ==
"offset") {
1356 casadi_error(
"No such option: " + std::string(op.first));
1360 casadi_assert(lift_shared,
"Not implemented");
1361 casadi_assert(!lift_calls,
"Not implemented");
1363 Function f(
"tmp_extract", std::vector<SX>(), ex, Dict{{
"max_io", 0}, {
"allow_free",
true}});
1364 SXFunction *ff = f.get<SXFunction>();
1366 const std::vector<ScalarAtomic>& algorithm = ff->algorithm_;
1367 std::vector<SXElem> work(f.sz_w());
1368 std::vector<SXElem> work2 = work;
1370 std::vector<SXElem>::const_iterator b_it=ff->operations_.begin();
1372 std::vector<SXElem>::const_iterator c_it = ff->constants_.begin();
1374 std::vector<SXElem>::const_iterator p_it = ff->free_vars_.begin();
1376 std::vector<casadi_int> usecount(work.size(), 0);
1378 std::vector<SXElem> v, vdef;
1379 for (std::vector<ScalarAtomic>::const_iterator it=algorithm.begin(); it<algorithm.end(); ++it) {
1385 CASADI_MATH_BINARY_BUILTIN
1387 if (usecount[it->i2]==0) {
1389 }
else if (usecount[it->i2]==1) {
1391 vdef.push_back(work[it->i2]);
1392 usecount[it->i2]=-1;
1397 if (usecount[it->i1]==0) {
1399 }
else if (usecount[it->i1]==1) {
1400 vdef.push_back(work[it->i1]);
1401 usecount[it->i1]=-1;
1410 usecount[it->i0] = -1;
1413 work[it->i0] = *b_it++;
1414 usecount[it->i0] = 0;
1419 std::stringstream v_name;
1420 for (casadi_int i=0; i<vdef.size(); ++i) {
1421 v_name.str(std::string());
1422 v_name << v_prefix << (v_ind++) << v_suffix;
1423 v.push_back(SXElem::sym(v_name.str()));
1426 casadi_assert(vdef.size() < std::numeric_limits<int>::max(),
"Integer overflow");
1428 for (casadi_int i=0; i<vdef.size(); ++i) {
1429 vdef[i].set_temp(
static_cast<int>(i)+1);
1432 std::vector<SXElem> marked = vdef;
1434 b_it=ff->operations_.begin();
1436 for (std::vector<ScalarAtomic>::const_iterator it=algorithm.begin(); it<algorithm.end(); ++it) {
1438 case OP_OUTPUT: ex.at(it->i0)->at(it->i2) = work[it->i1];
break;
1439 case OP_CONST: work2[it->i0] = work[it->i0] = *c_it++;
break;
1440 case OP_PARAMETER: work2[it->i0] = work[it->i0] = *p_it++;
break;
1444 CASADI_MATH_FUN_BUILTIN(work[it->i1], work[it->i2], work[it->i0])
1446 work2[it->i0] = *b_it++;
1448 casadi_int ind = work2[it->i0].get_temp()-1;
1450 vdef.at(ind) = work[it->i0];
1451 work[it->i0] = v.at(ind);
1457 for (std::vector<SXElem>::iterator it=marked.begin(); it!=marked.end(); ++it) {
1461 v_sx.resize(v.size());
1462 std::copy(v.begin(), v.end(), v_sx.begin());
1463 vdef_sx.resize(vdef.size());
1464 std::copy(vdef.begin(), vdef.end(), vdef_sx.begin());
1468 void CASADI_EXPORT SX::shared(std::vector<SX >& ex,
1469 std::vector<SX >& v,
1470 std::vector<SX >& vdef,
1471 const std::string& v_prefix,
1472 const std::string& v_suffix) {
1474 extract(ex, v, vdef, Dict{{
"lift_shared",
true}, {
"lift_calls",
false},
1475 {
"prefix", v_prefix}, {
"suffix", v_suffix}});
1479 SX CASADI_EXPORT SX::poly_coeff(
const SX& ex,
const SX& x) {
1480 casadi_assert_dev(ex.is_scalar());
1481 casadi_assert_dev(x.is_scalar());
1482 casadi_assert_dev(x.is_symbolic());
1484 std::vector<SXElem> r;
1487 casadi_int mult = 1;
1488 bool success =
false;
1489 for (casadi_int i=0; i<1000; ++i) {
1490 r.push_back((substitute(j, x, 0)/
static_cast<double>(mult)).scalar());
1499 if (!success) casadi_error(
"poly: supplied expression does not appear to be polynomial.");
1501 std::reverse(r.begin(), r.end());
1507 SX CASADI_EXPORT SX::poly_roots(
const SX& p) {
1508 casadi_assert(p.size2()==1,
1509 "poly_root(): supplied parameter must be column vector but got "
1511 casadi_assert_dev(p.is_dense());
1516 }
else if (p.size1()==3) {
1520 SX ds = sqrt(b*b-4*a*c);
1523 SX ret = SX::vertcat({(bm-ds)/a2, (bm+ds)/a2});
1525 }
else if (p.size1()==4) {
1536 SX b = r + 2.0/27*pp*p_-p_*q/3;
1540 SX phi = acos(-b/2/sqrt(-a3*a3*a3));
1542 SX ret = SX::vertcat({cos(phi/3), cos((phi+2*pi)/3), cos((phi+4*pi)/3)});
1547 }
else if (p.size1()==5) {
1555 SX f = c - (3*bb/8);
1556 SX g = d + (bb*
b / 8) - b*c/2;
1557 SX h = e - (3*bb*bb/256) + (bb * c/16) - (
b*d/4);
1558 SX poly = SX::vertcat({1, f/2, ((f*f -4*h)/16), -g*g/64});
1559 SX y = poly_roots(poly);
1571 SX ret = SX::vertcat({
1577 }
else if (
is_equal(p(p.nnz()-1)->at(0), 0)) {
1578 SX ret = SX::vertcat({poly_roots(p(
range(p.nnz()-1))), 0});
1581 casadi_error(
"poly_roots: polynomials of order at most 4 are supported. "
1582 "Got order " +
str(p.size1()-1) +
".");
1588 SX CASADI_EXPORT SX::det(
const SX& A,
const std::string& lsolver,
const Dict& opts) {
1589 auto& plugin = LinsolInternal::getPlugin(lsolver);
1590 casadi_assert(plugin.exposed.det,
1591 "Linsol plugin '" + lsolver +
"' does not provide a symbolic determinant. "
1592 "Try the 'symbolicqr' plugin.");
1593 return plugin.exposed.det(A, opts);
1597 SX CASADI_EXPORT SX::eig_symbolic(
const SX& m) {
1598 casadi_assert(m.size1()==m.size2(),
"eig(): supplied matrix must be square");
1600 std::vector<SX> ret;
1603 std::vector<casadi_int> offset;
1604 std::vector<casadi_int> index;
1605 casadi_int nb = m.sparsity().scc(offset, index);
1607 SX m_perm = m(offset, offset);
1609 SX l = SX::sym(
"l");
1611 for (casadi_int k=0; k<nb; ++k) {
1612 std::vector<casadi_int> r =
range(index.at(k), index.at(k+1));
1614 ret.push_back(poly_roots(poly_coeff(det(SX::eye(r.size())*l-m_perm(r, r)), l)));
1617 return vertcat(ret);
1621 std::vector<SXElem> CASADI_EXPORT SX::call(
const Function& f,
const std::vector<SXElem>& dep) {
1622 return SXElem::call(f, dep);
1626 void CASADI_EXPORT SX::print_split(casadi_int nnz,
const SXElem* nonzeros,
1627 std::vector<std::string>& nz,
1628 std::vector<std::string>& inter) {
1630 std::map<const SXNode*, casadi_int> nodeind;
1631 for (casadi_int i=0; i<nnz; ++i) nonzeros[i]->can_inline(nodeind);
1637 for (casadi_int i=0; i<nnz; ++i) nz.push_back(nonzeros[i]->print_compact(nodeind, inter));
1640 template<> std::vector<SX> CASADI_EXPORT SX::get_input(
const Function& f) {
1644 template<> std::vector<SX> CASADI_EXPORT SX::get_free(
const Function& f) {
1649 Dict CASADI_EXPORT SX::info()
const {
1650 return {{
"function",
Function(
"f", std::vector<SX>{}, std::vector<SX>{*
this})}};
1654 void CASADI_EXPORT SX::to_file(
const std::string& filename,
1656 const std::string& format_hint) {
1657 casadi_error(
"Not implemented");
1661 bool CASADI_EXPORT SX::simplify_const_folding(std::vector<SX>& arg,
1662 std::vector<SX>& res,
1668 bool CASADI_EXPORT SX::simplify_ref_count(std::vector<SX>& arg,
1669 std::vector<SX>& res,
1671 Dict temp_opts = {{
"live_variables",
false},
1674 {
"allow_free",
true}};
1675 Function f(
"temp", arg, res, temp_opts);
1676 SXFunction *ff = f.get<SXFunction>();
1677 const auto& algorithm_ = ff->algorithm_;
1679 std::vector<casadi_int> rwork(ff->worksize_);
1680 for (
auto&& a : algorithm_) {
1692 const auto& m = ff->call_.el.at(a.i1);
1693 for (casadi_int i=0;i<m.n_dep;++i) {
1700 bool is_binary = casadi_math<SXElem>::is_binary(a.op);
1711 std::vector<const SXElem*> argp(f.sz_arg());
1712 for (casadi_int i=0;i<arg.size();++i) {
1713 argp[i] =
get_ptr(arg.at(i).nonzeros());
1716 std::vector<SXElem*> resp(f.sz_res());
1717 for (casadi_int i=0;i<res.size();++i) {
1718 resp[i] =
get_ptr(res.at(i).nonzeros());
1721 std::vector<SXElem> w(ff->worksize_);
1724 std::vector<SXElem>::const_iterator b_it = ff->operations_.begin();
1727 std::vector<SXElem>::const_iterator c_it = ff->constants_.begin();
1730 std::vector<SXElem>::const_iterator p_it = ff->free_vars_.begin();
1732 for (
auto&& a : algorithm_) {
1735 w[a.i0] = argp[a.i1]==
nullptr ? 0 : argp[a.i1][a.i2];
1738 if (resp[a.i0]!=
nullptr) resp[a.i0][a.i2] = w[a.i1];
1744 w[a.i0] = *p_it++;
break;
1747 const auto& m = ff->call_.el.at(a.i1);
1748 const SXElem& orig = *b_it++;
1749 std::vector<SXElem> deps(m.n_dep);
1750 bool identical =
true;
1752 std::vector<SXElem> ret;
1753 for (casadi_int i=0;i<m.n_dep;++i) {
1754 identical &= SXElem::is_equal(w[m.dep.at(i)], orig->dep(i), 2);
1757 ret = OutputSX::split(orig, m.n_res);
1759 for (casadi_int i=0;i<m.n_dep;++i) deps[i] = w[m.dep[i]];
1760 ret = SXElem::call(m.f, deps);
1762 for (casadi_int i=0;i<m.n_res;++i) {
1763 if (m.res[i]>=0) w[m.res[i]] = ret[i];
1772 if (casadi_math<MX>::is_binary(a.op)) {
1773 f = SXElem::binary(a.op, w[a.i1], w[a.i2], rwork[a.i1]==1, rwork[a.i2]==1);
1774 }
else if (casadi_math<MX>::is_unary(a.op)) {
1775 f = SXElem::unary(a.op, w[a.i1], rwork[a.i1]==1);
1778 CASADI_MATH_FUN_BUILTIN(w[a.i1], w[a.i2], f)
1784 const casadi_int depth = 2;
1785 f.assignIfDuplicate(*b_it++, depth);
1795 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
1797 CASADI_EXPORT std::mutex& SX::get_mutex_temp() {
1798 return SXElem::mutex_temp;
1803 #pragma GCC diagnostic push
1804 #pragma GCC diagnostic ignored "-Wattributes"
1808 #pragma GCC diagnostic pop
const SX sx_in(casadi_int iind) const
Create fresh symbols with the names and sparsity patterns of the inputs.
std::vector< SX > free_sx() const
Get the free variables of an SXFunction as SX symbols; other classes raise.
casadi_int numel() const
Get the number of elements, size1()*size2()
bool is_dense() const
Check if the matrix is dense, i.e. nnz() equals numel()
std::pair< casadi_int, casadi_int > size() const
Get the shape as a pair (number of rows, number of columns)
casadi_int nnz() const
Get the number of structural nonzeros.
casadi_int size2() const
Get the second dimension (i.e. number of columns)
std::string dim(bool with_nz=false) const
Get string representation of dimensions.
static Matrix< Scalar > zeros(casadi_int nrow=1, casadi_int ncol=1)
Create a dense matrix or a matrix with specified sparsity with all entries zero.
bool is_scalar(bool scalar_and_dense=false) const
Check if the matrix is scalar, i.e. 1-by-1.
static MX find(const MX &x)
Sparse matrix class. SX and DM are specializations.
casadi_int which_output() const
Get the index of evaluation output - only valid when is_output() is true.
std::vector< Scalar > & nonzeros()
static std::vector< std::vector< Matrix< Scalar > > > reverse(const std::vector< Matrix< Scalar > > &ex, const std::vector< Matrix< Scalar > > &arg, const std::vector< std::vector< Matrix< Scalar > > > &v, const Dict &opts=Dict())
static Matrix< Scalar > simplify(const Matrix< Scalar > &x)
static void extract_parametric(const Matrix< Scalar > &expr, const Matrix< Scalar > &par, Matrix< Scalar > &expr_ret, std::vector< Matrix< Scalar > > &symbols, std::vector< Matrix< Scalar >> ¶metric, const Dict &opts)
static void separate_linear(const Matrix< Scalar > &expr, const Matrix< Scalar > &sym_lin, const Matrix< Scalar > &sym_const, Matrix< Scalar > &expr_const, Matrix< Scalar > &expr_lin, Matrix< Scalar > &expr_nonlin)
bool is_smooth() const
Check whether an SX expression contains only smooth operations.
static void set_max_depth(casadi_int eq_depth=1)
Set the depth to which SX subexpressions are compared during simplification.
casadi_int n_dep() const
Number of operands of a 1-by-1 SX expression (0 for symbols and constants)
void get(Matrix< Scalar > &m, bool ind1, const Slice &rr) const
Get elements by a single (linear) index.
friend Scalar * get_ptr(Matrix< Scalar > &v)
bool __nonzero__() const
Truth value of a 1-by-1 matrix (Python: bool(x))
static Matrix< Scalar > transform(const Matrix< Scalar > &x, const Dict &opts=Dict())
static std::vector< Matrix< Scalar > > symvar(const Matrix< Scalar > &x)
const Sparsity & sparsity() const
Const access the sparsity - reference to data member.
bool has_duplicates() const
Detect symbolic primitives that appear more than once in an SX matrix.
bool has_output() const
Check if a multiple output node.
casadi_int element_hash() const
Hash of the node of a 1-by-1 SX expression.
bool is_leaf() const
Check if SX is a leaf of the SX graph.
static Matrix< Scalar > gauss_quadrature(const Matrix< Scalar > &f, const Matrix< Scalar > &x, const Matrix< Scalar > &a, const Matrix< Scalar > &b, casadi_int order=5)
static Matrix< Scalar > mtimes(const Matrix< Scalar > &x, const Matrix< Scalar > &y, const std::string &blas="reference")
static Matrix< Scalar > pw_lin(const Matrix< Scalar > &t, const Matrix< Scalar > &tval, const Matrix< Scalar > &val)
bool is_regular() const
Check that no entry is nan, inf or -inf.
Matrix< Scalar > get_output(casadi_int oind) const
Get an output.
static void expand(const Matrix< Scalar > &x, Matrix< Scalar > &weights, Matrix< Scalar > &terms)
Matrix< Scalar > dep(casadi_int ch=0) const
Get operand ch of a 1-by-1 SX expression.
void reset_input() const
Clear the markers set by has_duplicates.
static Matrix< Scalar > _sym(const std::string &name, const Sparsity &sp)
bool is_symbolic() const
Check if symbolic (Dense)
static void substitute_inplace(const std::vector< Matrix< Scalar > > &v, std::vector< Matrix< Scalar > > &vdef, std::vector< Matrix< Scalar > > &ex, bool revers)
Function which_function() const
Get function - only valid when is_call() is true.
bool is_commutative() const
Check whether a binary SX is commutative.
static bool simplify_combine_terms(std::vector< Matrix< Scalar > > &arg, std::vector< Matrix< Scalar > > &res, const Dict &opts=Dict())
static Matrix< Scalar > substitute(const Matrix< Scalar > &ex, const Matrix< Scalar > &v, const Matrix< Scalar > &vdef)
casadi_int op() const
Get operation type.
bool is_output() const
Check if evaluation output.
bool is_valid_input() const
Check if matrix can be used to define function inputs.
bool is_call() const
Check if function call.
std::string name() const
Name of a 1-by-1 symbolic SX primitive.
static bool is_equal(const Matrix< Scalar > &x, const Matrix< Scalar > &y, casadi_int depth=0)
static casadi_int get_max_depth()
Get the depth to which SX subexpressions are compared during simplification.
static Matrix< Scalar > pw_const(const Matrix< Scalar > &t, const Matrix< Scalar > &tval, const Matrix< Scalar > &val)
const Scalar scalar() const
Convert to scalar type.
bool is_op(casadi_int op) const
Is it a certain operation.
The basic scalar symbolic class of CasADi.
static std::vector< SXElem > call(const Function &f, const std::vector< SXElem > &deps)
bool is_minus_inf() const
static SXElem create(SXNode *node)
SXNode * get() const
Get a pointer to the node.
static bool is_equal(const SXElem &x, const SXElem &y, casadi_int depth=0)
Check equality up to a given depth.
static SXElem sym(const std::string &name)
Create a symbolic primitive.
Internal node class for SXFunction.
bool is_smooth() const
Check if smooth.
static casadi_int eq_depth_
static MatType veccat(const std::vector< MatType > &x)
bool is_scalar(bool scalar_and_dense=false) const
Check whether the pattern is 1-by-1.
casadi_int nnz() const
Number of structural nonzeros.
bool is_equal(double x, double y, casadi_int depth=0)
std::vector< casadi_int > range(casadi_int start, casadi_int stop, casadi_int step, casadi_int len)
Range function.
std::vector< bool > _which_depends(const MatType &expr, const MatType &var, casadi_int order, bool tr)
Sparsity _jacobian_sparsity(const MatType &expr, const MatType &var)
SX mtaylor_recursive(const SX &ex, const SX &x, const SX &a, casadi_int order, const std::vector< casadi_int > &order_contributions, const SXElem ¤t_dx=casadi_limits< SXElem >::one, double current_denom=1, casadi_int current_order=1)
MX register_symbol(const MX &node, std::map< MXNode *, MX > &symbol_map, std::vector< MX > &symbol_v, std::vector< MX > ¶metric_v, bool extract_trivial, casadi_int v_offset, const std::string &v_prefix, const std::string &v_suffix)
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.
std::vector< T > reverse(const std::vector< T > &v)
Reverse a list.
Dict extract_from_dict(const Dict &d, const std::string &key, T &value)
static bool is_binary(unsigned char op)
Is binary operation?
static void fun_linear(unsigned char op, const T *x, const T *y, T *f)
Evaluate function on a const/linear/nonlinear partition.