1 /*
2  *    This file is part of CasADi.
3  *
4  *    CasADi -- A symbolic framework for dynamic optimization.
5  *    Copyright (C) 2010-2014 Joel Andersson, Joris Gillis, Moritz Diehl,
6  *                            K.U. Leuven. All rights reserved.
7  *    Copyright (C) 2011-2014 Greg Horn
8  *
9  *    CasADi is free software; you can redistribute it and/or
10  *    modify it under the terms of the GNU Lesser General Public
11  *    License as published by the Free Software Foundation; either
12  *    version 3 of the License, or (at your option) any later version.
13  *
14  *    CasADi is distributed in the hope that it will be useful,
15  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *    Lesser General Public License for more details.
18  *
19  *    You should have received a copy of the GNU Lesser General Public
20  *    License along with CasADi; if not, write to the Free Software
21  *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24 
25 
26 #include "mx_node.hpp"
27 #include "symbolic_mx.hpp"
28 #include "constant_mx.hpp"
29 #include "multiple_output.hpp"
30 #include "casadi_misc.hpp"
31 #include "norm.hpp"
32 #include "calculus.hpp"
33 #include "mx_function.hpp"
34 #include "linsol.hpp"
35 #include "expm.hpp"
36 #include "serializing_stream.hpp"
37 #include "im.hpp"
38 #include "bspline.hpp"
39 
40 // Throw informative error message
41 #define CASADI_THROW_ERROR(FNAME, WHAT) \
42 throw CasadiException("Error in MX::" FNAME " at " + CASADI_WHERE + ":\n"\
43   + std::string(WHAT));
44 
45 // Throw informative error message
46 #define CASADI_THROW_ERROR_OBJ(FNAME, WHAT) \
47 throw CasadiException("Error in MX::" FNAME " for node of type " \
48   + this->class_name() + " at " + CASADI_WHERE + ":\n" + std::string(WHAT));
49 
50 using namespace std;
51 namespace casadi {
52 
53   template class GenericMatrix< MX >;
54 
~MX()55   MX::~MX() {
56   }
57 
MX()58   MX::MX() {
59     own(ZeroByZero::getInstance());
60   }
61 
MX(MXNode * node,bool dummy1,bool dummy2,bool dummy3,bool dummy4)62   MX::MX(MXNode* node, bool dummy1, bool dummy2, bool dummy3, bool dummy4) {
63     own(node);
64   }
65 
create(MXNode * node)66   MX MX::create(MXNode* node) {
67     return MX(node, false, false, false, false);
68   }
69 
MX(double x)70   MX::MX(double x) {
71     own(ConstantMX::create(Sparsity::dense(1, 1), x));
72   }
73 
MX(const DM & x)74   MX::MX(const DM& x) {
75     own(ConstantMX::create(x));
76   }
77 
MX(const std::vector<double> & x)78   MX::MX(const std::vector<double>& x) {
79     own(ConstantMX::create(DM(x)));
80   }
81 
MX(const Sparsity & sp,const MX & val)82   MX::MX(const Sparsity& sp, const MX& val) {
83     if (sp.is_reshape(val.sparsity())) {
84       *this = reshape(val, sp);
85     } else if (val.is_scalar()) {
86       // Dense matrix if val dense
87       if (val.is_dense()) {
88         if (val.is_constant()) {
89           own(ConstantMX::create(sp, static_cast<double>(val)));
90         } else {
91           *this = val->get_nzref(sp, std::vector<casadi_int>(sp.nnz(), 0));
92         }
93       } else {
94         // Empty matrix
95         own(ConstantMX::create(Sparsity(sp.size()), 0));
96       }
97     } else {
98       casadi_assert_dev(val.is_column() && sp.nnz()==val.size1());
99       *this = densify(val)->get_nzref(sp, range(sp.nnz()));
100     }
101   }
102 
MX(const Sparsity & sp)103   MX::MX(const Sparsity& sp) {
104     own(ConstantMX::create(sp, 1));
105   }
106 
MX(casadi_int nrow,casadi_int ncol)107   MX::MX(casadi_int nrow, casadi_int ncol) {
108     own(ConstantMX::create(Sparsity(nrow, ncol), 0));
109   }
110 
MX(const std::pair<casadi_int,casadi_int> & rc)111   MX::MX(const std::pair<casadi_int, casadi_int>& rc) {
112     own(ConstantMX::create(Sparsity(rc), 0));
113   }
114 
MX(const Sparsity & sp,double val,bool dummy)115   MX::MX(const Sparsity& sp, double val, bool dummy) {
116     own(ConstantMX::create(sp, val));
117   }
118 
MX(const Sparsity & sp,const std::string & fname)119   MX::MX(const Sparsity& sp, const std::string& fname) {
120     own(ConstantMX::create(sp, fname));
121   }
122 
createMultipleOutput(MXNode * node)123   std::vector<MX> MX::createMultipleOutput(MXNode* node) {
124     casadi_assert_dev(dynamic_cast<MultipleOutput*>(node)!=nullptr);
125     MX x =  MX::create(node);
126     std::vector<MX> ret(x->nout());
127     for (casadi_int i=0; i<ret.size(); ++i) {
128       ret[i] = MX::create(new OutputNode(x, i));
129       if (ret[i].is_empty(true)) {
130         ret[i] = MX(0, 0);
131       } else if (ret[i].nnz()==0) {
132         ret[i] = MX(ret[i].size());
133       }
134     }
135     return ret;
136   }
137 
__nonzero__() const138   bool MX::__nonzero__() const {
139     return (*this)->__nonzero__();
140   }
141 
get(MX & m,bool ind1,const Slice & rr,const Slice & cc) const142   void MX::get(MX& m, bool ind1, const Slice& rr, const Slice& cc) const {
143     // Fall back on (IM, IM)
144     return get(m, ind1, rr.all(size1(), ind1), cc.all(size2(), ind1));
145   }
146 
get(MX & m,bool ind1,const Slice & rr,const Matrix<casadi_int> & cc) const147   void MX::get(MX& m, bool ind1, const Slice& rr, const Matrix<casadi_int>& cc) const {
148     // Fall back on (IM, IM)
149     get(m, ind1, rr.all(size1(), ind1), cc);
150   }
151 
get(MX & m,bool ind1,const Matrix<casadi_int> & rr,const Slice & cc) const152   void MX::get(MX& m, bool ind1, const Matrix<casadi_int>& rr, const Slice& cc) const {
153     // Fall back on (IM, IM)
154     get(m, ind1, rr, cc.all(size2(), ind1));
155   }
156 
get(MX & m,bool ind1,const Matrix<casadi_int> & rr,const Matrix<casadi_int> & cc) const157   void MX::get(MX& m, bool ind1, const Matrix<casadi_int>& rr, const Matrix<casadi_int>& cc) const {
158     // Make sure dense vectors
159     casadi_assert(rr.is_dense() && rr.is_vector(),
160                           "Marix::get: First index must be a dense vector");
161     casadi_assert(cc.is_dense() && cc.is_vector(),
162                           "Marix::get: Second index must be a dense vector");
163 
164     // Get the sparsity pattern - does bounds checking
165     std::vector<casadi_int> mapping;
166     Sparsity sp = sparsity().sub(rr.nonzeros(), cc.nonzeros(), mapping, ind1);
167 
168     // Create return MX
169     m = (*this)->get_nzref(sp, mapping);
170   }
171 
get(MX & m,bool ind1,const Slice & rr) const172   void MX::get(MX& m, bool ind1, const Slice& rr) const {
173     // Fall back on IM
174     get(m, ind1, rr.all(numel(), ind1));
175   }
176 
get(MX & m,bool ind1,const Matrix<casadi_int> & rr) const177   void MX::get(MX& m, bool ind1, const Matrix<casadi_int>& rr) const {
178     // If the indexed matrix is dense, use nonzero indexing
179     if (is_dense()) {
180       return get_nz(m, ind1, rr);
181     }
182 
183     // If indexed matrix was a row/column vector, make sure that the result is too
184     bool tr = (is_column() && rr.is_row()) || (is_row() && rr.is_column());
185 
186     // Get the sparsity pattern - does bounds checking
187     std::vector<casadi_int> mapping;
188     Sparsity sp = sparsity().sub(rr.nonzeros(), tr ? rr.sparsity().T() : rr.sparsity(),
189                                  mapping, ind1);
190 
191     // Create return MX
192     m = (*this)->get_nzref(sp, mapping);
193   }
194 
get(MX & m,bool ind1,const Sparsity & sp) const195   void MX::get(MX& m, bool ind1, const Sparsity& sp) const {
196     casadi_assert(size()==sp.size(),
197       "get(Sparsity sp): shape mismatch. This matrix has shape "
198       + str(size()) + ", but supplied sparsity index has shape "
199       + str(sp.size()) + ".");
200     m = project(*this, sp);
201   }
202 
get(MX & m,bool ind1,const MX & rr) const203   void MX::get(MX& m, bool ind1, const MX& rr) const {
204     casadi_assert(is_dense(), "Parametric slicing only supported for dense matrices."
205                               "Got " + dim(true) + " instead.");
206     get_nz(m, ind1, rr);
207   }
208 
get(MX & m,bool ind1,const Slice & rr,const MX & cc) const209   void MX::get(MX& m, bool ind1, const Slice& rr, const MX& cc) const {
210     casadi_assert(is_dense(), "Parametric slicing only supported for dense matrices. ");
211     m = (*this)->get_nz_ref(rr.apply(size1()), (ind1 ? cc-1 : cc)*size1());
212   }
213 
get(MX & m,bool ind1,const MX & rr,const Slice & cc) const214   void MX::get(MX& m, bool ind1, const MX& rr, const Slice& cc) const {
215     casadi_assert(is_dense(), "Parametric slicing only supported for dense matrices.");
216     m = (*this)->get_nz_ref(ind1 ? rr-1 : rr, cc.apply(size2())*size1());
217   }
218 
get(MX & m,bool ind1,const MX & rr,const MX & cc) const219   void MX::get(MX& m, bool ind1, const MX& rr, const MX& cc) const {
220     casadi_assert(is_dense(), "Parametric slicing only supported for dense matrices.");
221     m = (*this)->get_nz_ref(ind1 ? rr-1 : rr, (ind1 ? cc-1 : cc)*size1());
222   }
223 
set(const MX & m,bool ind1,const Slice & rr,const Slice & cc)224   void MX::set(const MX& m, bool ind1, const Slice& rr, const Slice& cc) {
225     // Fall back on (IM, IM)
226     set(m, ind1, rr.all(size1(), ind1), cc.all(size2(), ind1));
227   }
228 
set(const MX & m,bool ind1,const Slice & rr,const Matrix<casadi_int> & cc)229   void MX::set(const MX& m, bool ind1, const Slice& rr, const Matrix<casadi_int>& cc) {
230     // Fall back on (IM, IM)
231     set(m, ind1, rr.all(size1(), ind1), cc);
232   }
233 
set(const MX & m,bool ind1,const Matrix<casadi_int> & rr,const Slice & cc)234   void MX::set(const MX& m, bool ind1, const Matrix<casadi_int>& rr, const Slice& cc) {
235     // Fall back on (IM, IM)
236     set(m, ind1, rr, cc.all(size2(), ind1));
237   }
238 
set(const MX & m,bool ind1,const Matrix<casadi_int> & rr,const Matrix<casadi_int> & cc)239   void MX::set(const MX& m, bool ind1, const Matrix<casadi_int>& rr, const Matrix<casadi_int>& cc) {
240     // Row vector rr (e.g. in MATLAB) is transposed to column vector
241     if (rr.size1()==1 && rr.size2()>1) {
242       return set(m, ind1, rr.T(), cc);
243     }
244 
245     // Row vector cc (e.g. in MATLAB) is transposed to column vector
246     if (cc.size1()==1 && cc.size2()>1) {
247       return set(m, ind1, rr, cc.T());
248     }
249 
250     // Make sure rr and cc are dense vectors
251     casadi_assert(rr.is_dense() && rr.is_column(),
252       "MX::set: First index not dense vector");
253     casadi_assert(cc.is_dense() && cc.is_column(),
254       "MX::set: Second index not dense vector");
255 
256     // Assert dimensions of assigning matrix
257     if (rr.size1() != m.size1() || cc.size1() != m.size2()) {
258       if (m.is_scalar()) {
259         // m scalar means "set all"
260         return set(repmat(m, rr.size1(), cc.size1()), ind1, rr, cc);
261       } else if (rr.size1() == m.size2() && cc.size1() == m.size1()
262                  && std::min(m.size1(), m.size2()) == 1) {
263         // m is transposed if necessary
264         return set(m.T(), ind1, rr, cc);
265       } else {
266         // Error otherwise
267         casadi_error("Dimension mismatch. lhs is " + str(rr.size1()) + "-by-"
268                      + str(cc.size1()) + ", while rhs is " + str(m.size()));
269       }
270     }
271 
272     // Dimensions
273     casadi_int sz1 = size1(), sz2 = size2();
274 
275     // Report out-of-bounds
276     casadi_assert_in_range(rr.nonzeros(), -sz1+ind1, sz1+ind1);
277     casadi_assert_in_range(cc.nonzeros(), -sz2+ind1, sz2+ind1);
278 
279     // If we are assigning with something sparse, first remove existing entries
280     if (!m.is_dense()) {
281       erase(rr.nonzeros(), cc.nonzeros(), ind1);
282     }
283 
284     // Collect all assignments
285     IM el = IM::zeros(m.sparsity());
286     for (casadi_int j=0; j<el.size2(); ++j) { // Loop over columns of m
287       casadi_int this_j = cc->at(j) - ind1; // Corresponding column in this
288       if (this_j<0) this_j += sz2;
289       for (casadi_int k=el.colind(j); k<el.colind(j+1); ++k) { // Loop over rows of m
290         casadi_int i = m.row(k);
291         casadi_int this_i = rr->at(i) - ind1; // Corresponding row in this
292         if (this_i<0) this_i += sz1;
293         el->at(k) = this_i + this_j*sz1;
294       }
295     }
296     return set(m, false, el);
297   }
298 
set(const MX & m,bool ind1,const Slice & rr)299   void MX::set(const MX& m, bool ind1, const Slice& rr) {
300     // Fall back on IM
301     set(m, ind1, rr.all(size1(), ind1));
302   }
303 
set(const MX & m,bool ind1,const Matrix<casadi_int> & rr)304   void MX::set(const MX& m, bool ind1, const Matrix<casadi_int>& rr) {
305     // Assert dimensions of assigning matrix
306     if (rr.sparsity() != m.sparsity()) {
307       if (rr.size() == m.size()) {
308         // Remove submatrix to be replaced
309         erase(rr.nonzeros(), ind1);
310 
311         // Find the intersection between rr's and m's sparsity patterns
312         Sparsity sp = rr.sparsity() * m.sparsity();
313 
314         // Project both matrices to this sparsity
315         return set(project(m, sp), ind1, Matrix<casadi_int>::project(rr, sp));
316       } else if (m.is_scalar()) {
317         // m scalar means "set all"
318         if (m.is_dense()) {
319           return set(MX(rr.sparsity(), m), ind1, rr);
320         } else {
321           return set(MX(rr.size()), ind1, rr);
322         }
323       } else if (rr.size1() == m.size2() && rr.size2() == m.size1()
324                  && std::min(m.size1(), m.size2()) == 1) {
325         // m is transposed if necessary
326         return set(m.T(), ind1, rr);
327       } else {
328         // Error otherwise
329         casadi_error("Dimension mismatch. lhs is " + str(rr.size())
330                      + ", while rhs is " + str(m.size()));
331       }
332     }
333 
334     // Dimensions of this
335     casadi_int sz1 = size1(), sz2 = size2(), sz = nnz(), nel = numel(), rrsz = rr.nnz();
336 
337     // Quick return if nothing to set
338     if (rrsz==0) return;
339 
340     // Check bounds
341     casadi_assert_in_range(rr.nonzeros(), -nel+ind1, nel+ind1);
342 
343     // Dense mode
344     if (is_dense() && m.is_dense()) {
345       return set_nz(m, ind1, rr);
346     }
347 
348     // Construct new sparsity pattern
349     std::vector<casadi_int> new_row=sparsity().get_row(), new_col=sparsity().get_col();
350     std::vector<casadi_int> nz(rr.nonzeros());
351     new_row.reserve(sz+rrsz);
352     new_col.reserve(sz+rrsz);
353     nz.reserve(rrsz);
354     for (std::vector<casadi_int>::iterator i=nz.begin(); i!=nz.end(); ++i) {
355       if (ind1) (*i)--;
356       if (*i<0) *i += nel;
357       new_row.push_back(*i % sz1);
358       new_col.push_back(*i / sz1);
359     }
360     Sparsity sp = Sparsity::triplet(sz1, sz2, new_row, new_col);
361 
362     // If needed, update pattern
363     if (sp != sparsity()) *this = project(*this, sp);
364 
365     // Find the nonzeros corresponding to rr
366     sparsity().get_nz(nz);
367 
368     // Create a nonzero assignment node
369     *this = m->get_nzassign(*this, nz);
370   }
371 
set(const MX & m,bool ind1,const Sparsity & sp)372   void MX::set(const MX& m, bool ind1, const Sparsity& sp) {
373     casadi_assert(size()==sp.size(),
374       "set(Sparsity sp): shape mismatch. This matrix has shape "
375       + str(size()) + ", but supplied sparsity index has shape "
376       + str(sp.size()) + ".");
377     std::vector<casadi_int> ii = sp.find();
378     if (m.is_scalar()) {
379       (*this)(ii) = densify(m);
380     } else {
381       (*this)(ii) = densify(m(ii));
382     }
383   }
384 
get_nz(MX & m,bool ind1,const Slice & kk) const385   void MX::get_nz(MX& m, bool ind1, const Slice& kk) const {
386     // Fallback on IM
387     get_nz(m, ind1, kk.all(nnz(), ind1));
388   }
389 
get_nz(MX & m,bool ind1,const Matrix<casadi_int> & kk) const390   void MX::get_nz(MX& m, bool ind1, const Matrix<casadi_int>& kk) const {
391     // If indexed matrix was a row/column vector, make sure that the result is too
392     bool tr = (is_column() && kk.is_row()) || (is_row() && kk.is_column());
393 
394     // Quick return if no entries
395     if (kk.nnz()==0) {
396       m = MX::zeros(tr ? kk.sparsity().T() : kk.sparsity());
397       return;
398     }
399 
400     // Check bounds
401     casadi_int sz = nnz();
402     casadi_assert_in_range(kk.nonzeros(), -sz+ind1, sz+ind1);
403 
404     // Handle index-1, negative indices
405     if (ind1 || *std::min_element(kk->begin(), kk->end())<0) {
406       Matrix<casadi_int> kk_mod = kk;
407       for (auto&& i : kk_mod.nonzeros()) {
408         casadi_assert(!(ind1 && i<=0),
409           "Matlab is 1-based, but requested index " + str(i) + ". "
410           "Note that negative slices are disabled in the Matlab interface. "
411           "Possibly you may want to use 'end'.");
412         if (ind1) i--;
413         if (i<0) i += sz;
414       }
415       get_nz(m, false, kk_mod); // Call recursively
416       return;
417     }
418 
419     // Return reference to the nonzeros
420     m = (*this)->get_nzref(tr ? kk.sparsity().T() : kk.sparsity(), kk.nonzeros());
421   }
422 
get_nz(MX & m,bool ind1,const MX & kk) const423   void MX::get_nz(MX& m, bool ind1, const MX& kk) const {
424     // Create return MX
425     m = (*this)->get_nz_ref(ind1 ? kk-1.0 : kk);
426   }
427 
get_nz(MX & m,bool ind1,const MX & inner,const MX & outer) const428   void MX::get_nz(MX& m, bool ind1, const MX& inner, const MX& outer) const {
429     // Create return MX
430     m = (*this)->get_nz_ref(ind1 ? inner-1.0: inner, ind1 ? outer-1.0: outer);
431   }
432 
get_nz(MX & m,bool ind1,const Slice & inner,const MX & outer) const433   void MX::get_nz(MX& m, bool ind1, const Slice& inner, const MX& outer) const {
434     // Create return MX
435     m = (*this)->get_nz_ref(ind1 ? inner-1: inner, ind1 ? outer-1.0: outer);
436   }
437 
get_nz(MX & m,bool ind1,const MX & inner,const Slice & outer) const438   void MX::get_nz(MX& m, bool ind1, const MX& inner, const Slice& outer) const {
439     // Create return MX
440     m = (*this)->get_nz_ref(ind1 ? inner-1.0: inner, ind1 ? outer-1: outer);
441   }
442 
set_nz(const MX & m,bool ind1,const Slice & kk)443   void MX::set_nz(const MX& m, bool ind1, const Slice& kk) {
444     // Fallback on IM
445     set_nz(m, ind1, kk.all(nnz(), ind1));
446   }
447 
set_nz(const MX & m,bool ind1,const Matrix<casadi_int> & kk)448   void MX::set_nz(const MX& m, bool ind1, const Matrix<casadi_int>& kk) {
449     casadi_assert(kk.nnz()==m.nnz() || m.nnz()==1,
450       "MX::set_nz: length of non-zero indices (" + str(kk.nnz()) + ") " +
451       "must match size of rhs (" + str(m.nnz()) + ").");
452 
453     // Assert dimensions of assigning matrix
454     if (kk.sparsity() != m.sparsity()) {
455       if (m.is_scalar()) {
456         // m scalar means "set all"
457         if (!m.is_dense()) return; // Nothing to set
458         return set_nz(MX(kk.sparsity(), m), ind1, kk);
459       } else if (kk.size() == m.size()) {
460         // Project sparsity if needed
461         return set_nz(project(m, kk.sparsity()), ind1, kk);
462       } else if (kk.size1() == m.size2() && kk.size2() == m.size1()
463                  && std::min(m.size1(), m.size2()) == 1) {
464         // m is transposed if necessary
465         return set_nz(m.T(), ind1, kk);
466       } else {
467         // Error otherwise
468         casadi_error("Dimension mismatch. lhs is " + str(kk.size())
469                      + ", while rhs is " + str(m.size()));
470       }
471     }
472 
473     // Call recursively if points both objects point to the same node
474     if (this==&m) {
475       MX m_copy = m;
476       return set_nz(m_copy, ind1, kk);
477     }
478 
479     // Check bounds
480     casadi_int sz = nnz();
481     casadi_assert_in_range(kk.nonzeros(), -sz+ind1, sz+ind1);
482 
483     // Quick return if no assignments to be made
484     if (kk.nnz()==0) return;
485 
486     // Handle index-1, negative indices
487     if (ind1 || *std::min_element(kk->begin(), kk->end())<0) {
488       Matrix<casadi_int> kk_mod = kk;
489       for (auto&& i : kk_mod.nonzeros()) {
490         casadi_assert(!(ind1 && i<=0),
491           "Matlab is 1-based, but requested index " + str(i) + ". "
492           "Note that negative slices are disabled in the Matlab interface. "
493           "Possibly you may want to use 'end'.");
494         if (ind1) i--;
495         if (i<0) i += sz;
496       }
497       return set_nz(m, false, kk_mod); // Call recursively
498     }
499 
500     // Create a nonzero assignment node
501     *this = m->get_nzassign(*this, kk.nonzeros());
502   }
503 
set_nz(const MX & m,bool ind1,const MX & kk)504   void MX::set_nz(const MX& m, bool ind1, const MX& kk) {
505     *this = m->get_nzassign(*this, ind1 ? kk-1 : kk);
506   }
507 
binary(casadi_int op,const MX & x,const MX & y)508   MX MX::binary(casadi_int op, const MX &x, const MX &y) {
509     // Check, correct dimensions
510     if (x.size()!=y.size() && !x.is_scalar() && !y.is_scalar()) {
511       // x and y are horizontal multiples of each other?
512       if (!x.is_empty() && !y.is_empty()) {
513         if (x.size1() == y.size1() && x.size2() % y.size2() == 0) {
514           return binary(op, x, repmat(y, 1, x.size2() / y.size2()));
515         } else if (y.size1() == x.size1() && y.size2() % x.size2() == 0) {
516           return binary(op, repmat(x, 1, y.size2() / x.size2()), y);
517         }
518       }
519       // Dimension mismatch
520       casadi_error("Dimension mismatch for " + casadi_math<double>::print(op, "x", "y") +
521                    ", x is " + x.dim() + ", while y is " + y.dim());
522     }
523     // Call internal class
524     return x->get_binary(op, y);
525   }
526 
unary(casadi_int op,const MX & x)527   MX MX::unary(casadi_int op, const MX &x) {
528     return x->get_unary(Operation(op));
529   }
530 
get() const531   MXNode* MX::get() const {
532     return static_cast<MXNode*>(SharedObject::get());
533   }
534 
operator ->()535   MXNode* MX::operator->() {
536     return static_cast<MXNode*>(SharedObject::operator->());
537   }
538 
operator ->() const539   const MXNode* MX::operator->() const {
540     return static_cast<const MXNode*>(SharedObject::operator->());
541   }
542 
inf(casadi_int nrow,casadi_int ncol)543   MX MX::inf(casadi_int nrow, casadi_int ncol) {
544     return inf(Sparsity::dense(nrow, ncol));
545   }
546 
inf(const std::pair<casadi_int,casadi_int> & rc)547   MX MX::inf(const std::pair<casadi_int, casadi_int> &rc) {
548     return inf(rc.first, rc.second);
549   }
550 
inf(const Sparsity & sp)551   MX MX::inf(const Sparsity& sp) {
552     return create(ConstantMX::create(sp, numeric_limits<double>::infinity()));
553   }
554 
nan(casadi_int nrow,casadi_int ncol)555   MX MX::nan(casadi_int nrow, casadi_int ncol) {
556     return nan(Sparsity::dense(nrow, ncol));
557   }
558 
nan(const std::pair<casadi_int,casadi_int> & rc)559   MX MX::nan(const std::pair<casadi_int, casadi_int>& rc) {
560     return nan(rc.first, rc.second);
561   }
562 
nan(const Sparsity & sp)563   MX MX::nan(const Sparsity& sp) {
564     return create(ConstantMX::create(sp, numeric_limits<double>::quiet_NaN()));
565   }
566 
eye(casadi_int n)567   MX MX::eye(casadi_int n) {
568     return MX(DM::eye(n));
569   }
570 
operator -() const571   MX MX::operator-() const {
572     if ((*this)->op()==OP_NEG) {
573       return (*this)->dep(0);
574     } else {
575       return (*this)->get_unary(OP_NEG);
576     }
577   }
578 
MX(const MX & x)579   MX::MX(const MX& x) : SharedObject(x) {
580   }
581 
sparsity() const582   const Sparsity& MX::sparsity() const {
583     return (*this)->sparsity();
584   }
585 
erase(const std::vector<casadi_int> & rr,const std::vector<casadi_int> & cc,bool ind1)586   void MX::erase(const std::vector<casadi_int>& rr, const std::vector<casadi_int>& cc, bool ind1) {
587     // Get sparsity of the new matrix
588     Sparsity sp = sparsity();
589 
590     // Erase from sparsity pattern
591     std::vector<casadi_int> mapping = sp.erase(rr, cc, ind1);
592 
593     // Create new matrix
594     if (mapping.size()!=nnz()) {
595       MX ret = (*this)->get_nzref(sp, mapping);
596       *this = ret;
597     }
598   }
599 
erase(const std::vector<casadi_int> & rr,bool ind1)600   void MX::erase(const std::vector<casadi_int>& rr, bool ind1) {
601     // Get sparsity of the new matrix
602     Sparsity sp = sparsity();
603 
604     // Erase from sparsity pattern
605     std::vector<casadi_int> mapping = sp.erase(rr, ind1);
606 
607     // Create new matrix
608     if (mapping.size()!=nnz()) {
609       MX ret = (*this)->get_nzref(sp, mapping);
610       *this = ret;
611     }
612   }
613 
enlarge(casadi_int nrow,casadi_int ncol,const std::vector<casadi_int> & rr,const std::vector<casadi_int> & cc,bool ind1)614   void MX::enlarge(casadi_int nrow, casadi_int ncol,
615                     const std::vector<casadi_int>& rr,
616                     const std::vector<casadi_int>& cc, bool ind1) {
617     Sparsity sp = sparsity();
618     sp.enlarge(nrow, ncol, rr, cc, ind1);
619 
620     MX ret = (*this)->get_nzref(sp, range(nnz())); // FIXME?
621     *this = ret;
622   }
623 
mtimes(const MX & x,const MX & y)624   MX MX::mtimes(const MX& x, const MX& y) {
625     if (x.is_scalar() || y.is_scalar()) {
626       // Use element-wise multiplication if at least one factor scalar
627       return x*y;
628     } else {
629       MX z = MX::zeros(Sparsity::mtimes(x.sparsity(), y.sparsity()));
630       return mac(x, y, z);
631     }
632   }
633 
einstein(const MX & A,const MX & B,const MX & C,const std::vector<casadi_int> & dim_a,const std::vector<casadi_int> & dim_b,const std::vector<casadi_int> & dim_c,const std::vector<casadi_int> & a,const std::vector<casadi_int> & b,const std::vector<casadi_int> & c)634   MX MX::einstein(const MX& A, const MX& B, const MX& C,
635       const std::vector<casadi_int>& dim_a, const std::vector<casadi_int>& dim_b,
636       const std::vector<casadi_int>& dim_c,
637       const std::vector<casadi_int>& a, const std::vector<casadi_int>& b,
638       const std::vector<casadi_int>& c) {
639     return C->get_einstein(A, B, dim_c, dim_a, dim_b, c, a, b);
640   }
641 
einstein(const MX & A,const MX & B,const std::vector<casadi_int> & dim_a,const std::vector<casadi_int> & dim_b,const std::vector<casadi_int> & dim_c,const std::vector<casadi_int> & a,const std::vector<casadi_int> & b,const std::vector<casadi_int> & c)642   MX MX::einstein(const MX& A, const MX& B,
643       const std::vector<casadi_int>& dim_a, const std::vector<casadi_int>& dim_b,
644       const std::vector<casadi_int>& dim_c,
645       const std::vector<casadi_int>& a, const std::vector<casadi_int>& b,
646       const std::vector<casadi_int>& c) {
647     return MX::zeros(product(dim_c), 1)->get_einstein(A, B, dim_c, dim_a, dim_b, c, a, b);
648   }
649 
cumsum(const MX & x,casadi_int axis)650   MX MX::cumsum(const MX &x, casadi_int axis) {
651     if (axis==-1) axis = x.is_row();
652     MX r = axis==0 ? x.T() : x;
653     Sparsity sl = r(Slice(), 0).sparsity();
654     MX acc = MX::sym("acc", sl);
655     MX u = MX::sym("u", sl);
656 
657     Function f("f", {acc, u}, {acc+u});
658     f = f.mapaccum(r.size2());
659     MX ret = f(std::vector<MX>{0, r})[0];
660 
661     return axis==0 ? ret.T() : ret;
662   }
663 
mac(const MX & x,const MX & y,const MX & z)664   MX MX::mac(const MX& x, const MX& y, const MX& z) {
665     if (x.is_scalar() || y.is_scalar()) {
666       // Use element-wise multiplication if at least one factor scalar
667       return z + x*y;
668     }
669 
670     // Check matching dimensions
671     casadi_assert(x.size2()==y.size1(),
672       "Matrix product with incompatible dimensions. Lhs is "
673       + x.dim() + " and rhs is " + y.dim() + ".");
674 
675     // Check if we can simplify the product
676     if (x.is_eye()) {
677       return y + z;
678     } else if (y.is_eye()) {
679       return x + z;
680     } else if (x.is_zero() || y.is_zero()) {
681       return z;
682     } else {
683       return x->get_mac(y, z);
684     }
685   }
686 
dot(const MX & x,const MX & y)687   MX MX::dot(const MX& x, const MX& y) {
688     return x->get_dot(y);
689   }
690 
printme(const MX & b) const691   MX MX::printme(const MX& b) const {
692     return binary(OP_PRINTME, *this, b);
693   }
694 
attachAssert(const MX & y,const std::string & fail_message) const695   MX MX::attachAssert(const MX& y, const std::string &fail_message) const {
696     casadi_assert(y.is_scalar(),
697       "Error in attachAssert: assertion expression y must be scalar, "
698       "but got " + y.dim());
699     return(*this)->get_assert(y, fail_message);
700   }
701 
monitor(const std::string & comment) const702   MX MX::monitor(const std::string& comment) const {
703     return(*this)->get_monitor(comment);
704   }
705 
lift(const MX & x,const MX & x_guess)706   MX MX::lift(const MX& x, const MX& x_guess) {
707     casadi_assert_dev(x.sparsity()==x_guess.sparsity());
708     return x->_get_binary(OP_LIFT, x_guess, false, false);
709   }
710 
evalf(const MX & m)711   DM MX::evalf(const MX& m) {
712     Function f("f", std::vector<MX>{}, {m});
713     return f(std::vector<DM>{})[0];
714   }
715 
mrdivide(const MX & b,const MX & a)716   MX MX::mrdivide(const MX& b, const MX& a) {
717     if (a.is_scalar() || b.is_scalar()) return b/a;
718     return solve(a.T(), b.T()).T();
719   }
720 
mldivide(const MX & a,const MX & b)721   MX MX::mldivide(const MX& a, const MX& b) {
722     if (a.is_scalar() || b.is_scalar()) return b/a;
723     return solve(a, b);
724   }
725 
dep(casadi_int ch) const726   MX MX::dep(casadi_int ch) const {
727     return (*this)->dep(ch);
728   }
729 
n_dep() const730   casadi_int MX::n_dep() const {
731     return (*this)->n_dep();
732   }
733 
name() const734   std::string MX::name() const {
735     return (*this)->name();
736   }
737 
is_symbolic() const738   bool MX::is_symbolic() const {
739     return (*this)->op()==OP_PARAMETER;
740   }
741 
is_constant() const742   bool MX::is_constant() const {
743     return (*this)->op()==OP_CONST;
744   }
745 
is_call() const746   bool MX::is_call() const {
747     return (*this)->op()==OP_CALL;
748   }
749 
which_function() const750   Function MX::which_function() const {
751     return (*this)->which_function();
752   }
753 
is_output() const754   bool MX::is_output() const {
755     return (*this)->is_output();
756   }
757 
which_output() const758   casadi_int MX::which_output() const {
759     return (*this)->which_output();
760   }
761 
is_op(casadi_int op) const762   bool MX::is_op(casadi_int op) const {
763     return (*this)->op()==op;
764   }
765 
is_multiplication() const766   bool MX::is_multiplication() const {
767     return (*this)->op()==OP_MTIMES;
768   }
769 
is_norm() const770   bool MX::is_norm() const {
771     return dynamic_cast<const Norm*>(get())!=nullptr;
772   }
773 
operator double() const774   MX::operator double() const {
775     return (*this)->to_double();
776   }
777 
operator DM() const778   MX::operator DM() const {
779     return (*this)->get_DM();
780   }
781 
is_binary() const782   bool MX::is_binary() const {
783     return (*this)->is_binary();
784   }
785 
is_unary() const786   bool MX::is_unary() const {
787     return (*this)->is_unary();
788   }
789 
op() const790   casadi_int MX::op() const {
791     return (*this)->op();
792   }
793 
info() const794   Dict MX::info() const {
795     return (*this)->info();
796   }
797 
serialize(SerializingStream & s) const798   void MX::serialize(SerializingStream& s) const {
799     return (*this)->serialize(s);
800   }
801 
deserialize(DeserializingStream & s)802   MX MX::deserialize(DeserializingStream& s) {
803     return MX::create(MXNode::deserialize(s));
804   }
805 
is_equal(const MX & x,const MX & y,casadi_int depth)806   bool MX::is_equal(const MX& x, const MX& y, casadi_int depth) {
807     return MXNode::is_equal(x.get(), y.get(), depth);
808   }
809 
mmin(const MX & x)810   MX MX::mmin(const MX &x) {
811     return x->get_mmin();
812   }
813 
mmax(const MX & x)814   MX MX::mmax(const MX &x) {
815     return x->get_mmax();
816   }
817 
is_commutative() const818   bool MX::is_commutative() const {
819     if (is_unary()) return true;
820     casadi_assert(is_binary() || is_unary(),
821       "MX::is_commutative: must be binary or unary operation");
822     return operation_checker<CommChecker>(op());
823   }
824 
mapping() const825   Matrix<casadi_int> MX::mapping() const {
826     return (*this)->mapping();
827   }
828 
get_temp() const829   casadi_int MX::get_temp() const {
830     return (*this)->temp;
831   }
832 
set_temp(casadi_int t) const833   void MX::set_temp(casadi_int t) const {
834     (*this)->temp = t;
835   }
836 
n_out() const837   casadi_int MX::n_out() const {
838     return (*this)->nout();
839   }
840 
get_output(casadi_int oind) const841   MX MX::get_output(casadi_int oind) const {
842     return (*this)->get_output(oind);
843   }
844 
project(const MX & x,const Sparsity & sp,bool intersect)845   MX MX::project(const MX& x, const Sparsity& sp, bool intersect) {
846     if (x.is_empty() || (sp==x.sparsity())) {
847       return x;
848     } else {
849       casadi_assert(sp.size()==x.size(), "Dimension mismatch");
850       if (intersect) {
851         return x->get_project(sp.intersect(x.sparsity()));
852       } else {
853         return x->get_project(sp);
854       }
855     }
856   }
857 
densify(const MX & x,const MX & val)858   MX MX::densify(const MX& x, const MX& val) {
859     casadi_assert_dev(val.is_scalar());
860     if (x.is_dense()) {
861       return x; // Already ok
862     } else if (val->is_zero()) {
863       return project(x, Sparsity::dense(x.size()));
864     } else {
865       MX ret = MX::repmat(val, x.size());
866       ret(x.sparsity()) = x;
867       return ret;
868     }
869   }
870 
871   casadi_int MX::eq_depth_ = 1;
872 
set_max_depth(casadi_int eq_depth)873   void MX::set_max_depth(casadi_int eq_depth) {
874     eq_depth_ = eq_depth;
875   }
876 
get_max_depth()877   casadi_int MX::get_max_depth() {
878     return eq_depth_;
879   }
880 
_sym(const std::string & name,const Sparsity & sp)881   MX MX::_sym(const std::string& name, const Sparsity& sp) {
882     if (sp.nnz()==0) {
883       return MX::zeros(sp);
884     } else {
885       return MX::create(new SymbolicMX(name, sp));
886     }
887   }
888 
is_valid_input() const889   bool MX::is_valid_input() const {
890     return (*this)->is_valid_input();
891   }
892 
n_primitives() const893   casadi_int MX::n_primitives() const {
894     return (*this)->n_primitives();
895   }
896 
primitives() const897   std::vector<MX> MX::primitives() const {
898     std::vector<MX> ret(n_primitives());
899     std::vector<MX>::iterator it=ret.begin();
900     (*this)->primitives(it);
901     casadi_assert_dev(it==ret.end());
902     return ret;
903   }
904 
split_primitives(const MX & x) const905   std::vector<MX> MX::split_primitives(const MX& x) const {
906     std::vector<MX> ret(n_primitives());
907     std::vector<MX>::iterator it=ret.begin();
908     (*this)->split_primitives(x, it);
909     casadi_assert_dev(it==ret.end());
910     return ret;
911   }
912 
join_primitives(const std::vector<MX> & v) const913   MX MX::join_primitives(const std::vector<MX>& v) const {
914     casadi_assert(v.size()==n_primitives(), "Wrong number of primitives supplied");
915     std::vector<MX>::const_iterator it=v.begin();
916     MX ret = (*this)->join_primitives(it);
917     casadi_assert_dev(it==v.end());
918     return ret;
919   }
920 
has_duplicates() const921   bool MX::has_duplicates() const {
922     return (*this)->has_duplicates();
923   }
924 
reset_input() const925   void MX::reset_input() const {
926     (*this)->reset_input();
927   }
928 
is_eye() const929   bool MX::is_eye() const {
930     return (*this)->is_eye();
931   }
932 
is_zero() const933   bool MX::is_zero() const {
934     if (nnz()==0) {
935       return true;
936     } else {
937       return (*this)->is_zero();
938     }
939   }
940 
is_one() const941   bool MX::is_one() const {
942     return (*this)->is_one();
943   }
944 
is_minus_one() const945   bool MX::is_minus_one() const {
946     return (*this)->is_value(-1);
947   }
948 
is_transpose() const949   bool MX::is_transpose() const {
950     return op()==OP_TRANSPOSE;
951   }
952 
is_regular() const953   bool MX::is_regular() const {
954     if (is_constant()) {
955       return static_cast<DM>(*this).is_regular();
956     } else {
957       casadi_error("Cannot check regularity for symbolic MX");
958     }
959   }
960 
T() const961   MX MX::T() const {
962     return (*this)->get_transpose();
963   }
964 
test_cast(const SharedObjectInternal * ptr)965   bool MX::test_cast(const SharedObjectInternal* ptr) {
966     return dynamic_cast<const MXNode*>(ptr)!=nullptr;
967   }
968 
969   // Helper function
has_empty(const vector<MX> & x,bool both=false)970   bool has_empty(const vector<MX>& x, bool both=false) {
971     for (auto&& i : x) {
972       if (i.is_empty(both)) return true;
973     }
974     return false;
975   }
976 
trim_empty(const vector<MX> & x,bool both=false)977   vector<MX> trim_empty(const vector<MX>& x, bool both=false) {
978     vector<MX> ret;
979     for (auto&& i : x) {
980       if (!i.is_empty(both)) ret.push_back(i);
981     }
982     return ret;
983   }
984 
horzcat(const vector<MX> & x)985   MX MX::horzcat(const vector<MX>& x) {
986     // Check dimensions
987     if (x.size()>1) {
988       vector<MX> ne = trim_empty(x, true);
989       for (casadi_int i=0;i<ne.size();i++) {
990         casadi_assert(ne[i].size1()==ne[0].size1(),
991           "horzcat dimension mismatch  x[" + str(i) + "]:" + ne[i].dim() +
992           " and x[0]: " + ne[0].dim() + ".");
993       }
994     }
995 
996     if (x.empty()) {
997       return MX();
998     } else if (x.size()==1) {
999       return x.front();
1000     } else if (has_empty(x)) {
1001       std::vector<MX> ret = trim_empty(x);
1002       if (ret.empty()) {
1003         // We still want horzcat(zeros(0,5),zeros(0,5)) -> zeros(0,10)
1004         ret = trim_empty(x, true);
1005         casadi_int s = 0;
1006         casadi_int nrow = 0;
1007         for (casadi_int i=0;i<ret.size();++i) {
1008           s+= ret[i].size2();
1009           casadi_assert_dev(nrow==0 || nrow==ret[i].size1());
1010           nrow = ret[i].size1();
1011         }
1012         return MX::zeros(nrow, s);
1013       } else {
1014         return horzcat(ret);
1015       }
1016     } else {
1017       return x.front()->get_horzcat(x);
1018     }
1019   }
1020 
diagcat(const vector<MX> & x)1021   MX MX::diagcat(const vector<MX>& x) {
1022     if (x.empty()) {
1023       return MX();
1024     } else if (x.size()==1) {
1025       return x.front();
1026     } else if (has_empty(x)) {
1027       std::vector<MX> ret = trim_empty(x);
1028       if (ret.empty()) {
1029         // We still want diagcat(zeros(5,0),zeros(5,0)) -> zeros(10,0)
1030         ret = trim_empty(x, true);
1031         casadi_int s1 = 0;
1032         casadi_int s2 = 0;
1033         for (casadi_int i=0;i<ret.size();++i) {
1034           s1+= ret[i].size1();
1035           s2+= ret[i].size2();
1036         }
1037         return MX::zeros(s1, s2);
1038       } else {
1039         return diagcat(ret);
1040       }
1041     } else {
1042       return x.front()->get_diagcat(x);
1043     }
1044   }
1045 
vertcat(const vector<MX> & x)1046   MX MX::vertcat(const vector<MX>& x) {
1047     // Check dimensions
1048     if (x.size()>1) {
1049       vector<MX> ne = trim_empty(x, true);
1050       for (casadi_int i=0;i<ne.size();i++) {
1051         casadi_assert(ne[i].size2()==ne[0].size2(),
1052           "vertcat dimension mismatch  x[" + str(i) + "]:" + ne[i].dim() +
1053           " and x[0]: " + ne[0].dim() + ".");
1054       }
1055     }
1056 
1057     if (x.empty()) {
1058       return MX();
1059     } else if (x.size()==1) {
1060       return x.front();
1061     } else if (has_empty(x)) {
1062       std::vector<MX> ret = trim_empty(x);
1063       if (ret.empty()) {
1064         // We still want vertcat(zeros(5,0),zeros(5,0)) -> zeros(10,0)
1065         ret = trim_empty(x, true);
1066         casadi_int s = 0;
1067         casadi_int ncol = 0;
1068         for (casadi_int i=0;i<ret.size();++i) {
1069           s+= ret[i].size1();
1070           casadi_assert_dev(ncol==0 || ret[i].size2()==ncol);
1071           ncol = ret[i].size2();
1072         }
1073         return MX::zeros(s, ncol);
1074       } else {
1075         return vertcat(ret);
1076       }
1077     } else if (!x.front().is_column()) {
1078       // Vertcat operation only supports vectors, rewrite using horzcat
1079       vector<MX> xT = x;
1080       for (vector<MX>::iterator i=xT.begin(); i!=xT.end(); ++i) *i = i->T();
1081       return horzcat(xT).T();
1082     } else {
1083       return x.front()->get_vertcat(x);
1084     }
1085   }
1086 
horzsplit(const MX & x,const std::vector<casadi_int> & offset)1087   std::vector<MX> MX::horzsplit(const MX& x, const std::vector<casadi_int>& offset) {
1088     // Consistency check
1089     casadi_assert_dev(!offset.empty());
1090     casadi_assert_dev(offset.front()==0);
1091     casadi_assert_dev(offset.back()==x.size2());
1092     casadi_assert_dev(is_monotone(offset));
1093 
1094     // Trivial return if possible
1095     if (offset.size()==1) {
1096       return vector<MX>(0);
1097     } else if (offset.size()==2) {
1098       return vector<MX>(1, x);
1099     } else {
1100       return x->get_horzsplit(offset);
1101     }
1102   }
1103 
diagsplit(const MX & x,const std::vector<casadi_int> & offset1,const std::vector<casadi_int> & offset2)1104   std::vector<MX> MX::diagsplit(const MX& x, const std::vector<casadi_int>& offset1,
1105                                 const std::vector<casadi_int>& offset2) {
1106     // Consistency check
1107     casadi_assert_dev(!offset1.empty());
1108     casadi_assert_dev(offset1.front()==0);
1109     casadi_assert_dev(offset1.back()==x.size1());
1110     casadi_assert_dev(is_monotone(offset1));
1111 
1112     // Consistency check
1113     casadi_assert_dev(!offset2.empty());
1114     casadi_assert_dev(offset2.front()==0);
1115     casadi_assert_dev(offset2.back()==x.size2());
1116     casadi_assert_dev(is_monotone(offset2));
1117 
1118     return x->get_diagsplit(offset1, offset2);
1119   }
1120 
vertsplit(const MX & x,const std::vector<casadi_int> & offset)1121   std::vector<MX> MX::vertsplit(const MX& x, const std::vector<casadi_int>& offset) {
1122     if (x.is_column()) {
1123       // Consistency check
1124       casadi_assert_dev(!offset.empty());
1125       casadi_assert_dev(offset.front()==0);
1126       casadi_assert_dev(offset.back()==x.size1());
1127       casadi_assert_dev(is_monotone(offset));
1128 
1129       // Trivial return if possible
1130       if (offset.size()==1) {
1131         return vector<MX>();
1132       } else if (offset.size()==2) {
1133         return vector<MX>(1, x);
1134       } else {
1135         return x->get_vertsplit(offset);
1136       }
1137     } else {
1138       std::vector<MX> ret = horzsplit(x.T(), offset);
1139       for (auto&& e : ret) e = e.T();
1140       return ret;
1141     }
1142   }
1143 
blockcat(const std::vector<std::vector<MX>> & v)1144   MX MX::blockcat(const std::vector< std::vector<MX > > &v) {
1145     // Quick return if no block rows
1146     if (v.empty()) return MX(0, 0);
1147 
1148     // Make sure same number of block columns
1149     casadi_int ncols = v.front().size();
1150     for (auto&& e : v) {
1151       casadi_assert(e.size()==ncols, "blockcat: Inconsistent number of block columns");
1152     }
1153 
1154     // Quick return if no block columns
1155     if (v.front().empty()) return MX(0, 0);
1156 
1157     // Horizontally concatenate all columns for each row, then vertically concatenate rows
1158     std::vector<MX> rows;
1159     for (auto&& e : v) {
1160       rows.push_back(horzcat(e));
1161     }
1162     return vertcat(rows);
1163   }
1164 
norm_2(const MX & x)1165   MX MX::norm_2(const MX& x) {
1166     if (x.is_vector()) {
1167       return norm_fro(x);
1168     } else {
1169       return x->get_norm_2();
1170     }
1171   }
1172 
norm_fro(const MX & x)1173   MX MX::norm_fro(const MX& x) {
1174     return x->get_norm_fro();
1175   }
1176 
norm_1(const MX & x)1177   MX MX::norm_1(const MX& x) {
1178     return x->get_norm_1();
1179   }
1180 
norm_inf(const MX & x)1181   MX MX::norm_inf(const MX& x) {
1182     return x->get_norm_inf();
1183   }
1184 
simplify(const MX & x)1185   MX MX::simplify(const MX& x) {
1186     return x;
1187   }
1188 
reshape(const MX & x,casadi_int nrow,casadi_int ncol)1189   MX MX::reshape(const MX& x, casadi_int nrow, casadi_int ncol) {
1190     // Quick return if trivial
1191     if (nrow==x.size1() && ncol==x.size2()) return x;
1192 
1193     // Reshape the sparsity pattern
1194     return reshape(x, Sparsity::reshape(x.sparsity(), nrow, ncol));
1195   }
1196 
reshape(const MX & x,const Sparsity & sp)1197   MX MX::reshape(const MX& x, const Sparsity& sp) {
1198     // Quick return if trivial
1199     if (sp==x.sparsity()) return x;
1200 
1201     // Call internal method
1202     return x->get_reshape(sp);
1203   }
1204 
if_else(const MX & cond,const MX & x_true,const MX & x_false,bool short_circuit)1205   MX MX::if_else(const MX &cond, const MX &x_true, const MX &x_false, bool short_circuit) {
1206     if (short_circuit) {
1207       // Get symbolic primitives
1208       vector<MX> arg = symvar(veccat(vector<MX>{x_true, x_false}));
1209 
1210       // Form functions for cases
1211       Function f_true("f_true", arg, {x_true});
1212       Function f_false("f_false", arg, {x_false});
1213 
1214       // Form Switch
1215       Function sw = Function::if_else("switch", f_true, f_false);
1216 
1217       // Call the Switch
1218       vector<MX> sw_arg;
1219       sw_arg.push_back(cond);
1220       sw_arg.insert(sw_arg.end(), arg.begin(), arg.end());
1221       return sw(sw_arg).at(0);
1222     } else {
1223       return if_else_zero(cond, x_true) + if_else_zero(!cond, x_false);
1224     }
1225   }
1226 
conditional(const MX & ind,const std::vector<MX> & x,const MX & x_default,bool short_circuit)1227   MX MX::conditional(const MX& ind, const std::vector<MX>& x,
1228                      const MX& x_default, bool short_circuit) {
1229     if (short_circuit) {
1230       // Get symbolic primitives
1231       std::vector<MX> arg = x;
1232       arg.push_back(x_default);
1233       arg = symvar(veccat(arg));
1234 
1235       // Form functions for cases
1236       vector<Function> f(x.size());
1237       for (casadi_int k=0; k<x.size(); ++k) {
1238         stringstream ss;
1239         ss << "f_case" << k;
1240         f[k] = Function(ss.str(), arg, {x[k]});
1241       }
1242       Function f_default("f_default", arg, {x_default});
1243 
1244       // Form Switch
1245       Function sw = Function::conditional("switch", f, f_default);
1246 
1247       // Call the Switch
1248       vector<MX> sw_arg;
1249       sw_arg.push_back(ind);
1250       sw_arg.insert(sw_arg.end(), arg.begin(), arg.end());
1251       return sw(sw_arg).at(0);
1252     } else {
1253       MX ret = x_default;
1254       for (casadi_int k=0; k<x.size(); ++k) {
1255         ret = if_else(ind==static_cast<double>(k), x[k], ret);
1256       }
1257       return ret;
1258     }
1259   }
1260 
unite(const MX & A,const MX & B)1261   MX MX::unite(const MX& A, const MX& B) {
1262     // Join the sparsity patterns
1263     std::vector<unsigned char> mapping;
1264     Sparsity sp = A.sparsity().unite(B.sparsity(), mapping);
1265 
1266     // Split up the mapping
1267     std::vector<casadi_int> nzA, nzB;
1268 
1269     // Copy sparsity
1270     for (casadi_int k=0; k<mapping.size(); ++k) {
1271       if (mapping[k]==1) {
1272         nzA.push_back(k);
1273       } else if (mapping[k]==2) {
1274         nzB.push_back(k);
1275       } else {
1276         throw CasadiException("Pattern intersection not empty");
1277       }
1278     }
1279 
1280     // Create mapping
1281     MX ret = MX::zeros(sp);
1282     ret = A->get_nzassign(ret, nzA);
1283     ret = B->get_nzassign(ret, nzB);
1284     return ret;
1285   }
1286 
trace(const MX & x)1287   MX MX::trace(const MX& x) {
1288     casadi_assert(x.is_square(), "trace: must be square");
1289     MX res(0);
1290     for (casadi_int i=0; i < x.size2(); i ++) {
1291       res += x(i, i);
1292     }
1293     return res;
1294   }
1295 
diag(const MX & x)1296   MX MX::diag(const MX& x) {
1297     // Nonzero mapping
1298     std::vector<casadi_int> mapping;
1299 
1300     // Get the sparsity
1301     Sparsity sp = x.sparsity().get_diag(mapping);
1302 
1303     // Create a reference to the nonzeros
1304     return x->get_nzref(sp, mapping);
1305   }
1306 
n_nodes(const MX & x)1307   casadi_int MX::n_nodes(const MX& x) {
1308     Function f("tmp", vector<MX>{}, {x});
1309     return f.n_nodes();
1310   }
1311 
sum2(const MX & x)1312   MX MX::sum2(const MX& x) {
1313     return mtimes(x, MX::ones(x.size2(), 1));
1314   }
1315 
sum1(const MX & x)1316   MX MX::sum1(const MX& x) {
1317     return mtimes(MX::ones(1, x.size1()), x);
1318   }
1319 
polyval(const MX & p,const MX & x)1320   MX MX::polyval(const MX& p, const MX& x) {
1321     casadi_assert(p.is_dense(), "polynomial coefficients vector must be a vector");
1322     casadi_assert(p.is_column() && p.nnz()>0, "polynomial coefficients must be a vector");
1323     MX ret = p.nz(0);
1324     for (casadi_int i=1; i<p.nnz(); ++i) {
1325       ret = ret*x + p.nz(i);
1326     }
1327     return ret;
1328   }
1329 
print_operator(const MX & x,const std::vector<std::string> & args)1330   std::string MX::print_operator(const MX& x, const std::vector<std::string>& args) {
1331     return x->disp(args);
1332   }
1333 
substitute_inplace(const std::vector<MX> & v,std::vector<MX> & vdef,std::vector<MX> & ex,bool reverse)1334   void MX::substitute_inplace(const std::vector<MX>& v, std::vector<MX>& vdef,
1335                              std::vector<MX>& ex, bool reverse) {
1336     casadi_assert(v.size()==vdef.size(),
1337                           "Mismatch in the number of expression to substitute.");
1338     for (casadi_int k=0; k<v.size(); ++k) {
1339       casadi_assert(v[k].is_symbolic(),
1340         "Variable " + str(k) + " is not symbolic");
1341       casadi_assert(v[k].size() == vdef[k].size(),
1342         "Inconsistent shape for variable " + str(k) + ".");
1343     }
1344     casadi_assert(reverse==false, "Not implemented");
1345 
1346     // quick return if nothing to replace
1347     if (v.empty()) return;
1348 
1349     // If ex can be split up, call recursively
1350     for (const MX& e1 : ex) {
1351       if (e1.n_primitives()!=1) {
1352         // Split ex into its primitives
1353         vector<MX> ex1;
1354         for (const MX& e : ex) {
1355           vector<MX> prim = e.primitives();
1356           ex1.insert(ex1.end(), prim.begin(), prim.end());
1357         }
1358 
1359         // Call recursively
1360         substitute_inplace(v, vdef, ex1, reverse);
1361 
1362         // Join primitives
1363         vector<MX>::const_iterator start, stop=ex1.begin();
1364         for (MX& e : ex) {
1365           start = stop;
1366           stop = start + e.n_primitives();
1367           e = e.join_primitives(vector<MX>(start, stop));
1368         }
1369         return;
1370       }
1371     }
1372 
1373     // implemented in MXFunction
1374     std::vector<MX> f_out = vdef;
1375     f_out.insert(f_out.end(), ex.begin(), ex.end());
1376     Function temp("temp", {v}, f_out);
1377     temp.get<MXFunction>()->substitute_inplace(vdef, ex);
1378   }
1379 
substitute(const MX & ex,const MX & v,const MX & vdef)1380   MX MX::substitute(const MX& ex, const MX& v, const MX& vdef) {
1381     return substitute(vector<MX>{ex}, vector<MX>{v}, vector<MX>{vdef}).front();
1382   }
1383 
substitute(const std::vector<MX> & ex,const std::vector<MX> & v,const std::vector<MX> & vdef)1384   std::vector<MX> MX::substitute(const std::vector<MX> &ex, const std::vector<MX> &v,
1385                                  const std::vector<MX> &vdef) {
1386     // Assert consistent dimensions
1387     casadi_assert_dev(v.size()==vdef.size());
1388 
1389     // Quick return if all equal
1390     bool all_equal = true;
1391     for (casadi_int k=0; k<v.size(); ++k) {
1392       if (v[k].size()!=vdef[k].size() || !is_equal(v[k], vdef[k])) {
1393         all_equal = false;
1394         break;
1395       }
1396     }
1397     if (all_equal) return ex;
1398 
1399     // Otherwise, evaluate symbolically
1400     Function F("tmp", v, ex);
1401     std::vector<MX> ret;
1402     F.call(vdef, ret, true);
1403     return ret;
1404   }
1405 
graph_substitute(const MX & x,const std::vector<MX> & v,const std::vector<MX> & vdef)1406   MX MX::graph_substitute(const MX& x, const std::vector<MX> &v,
1407                           const std::vector<MX> &vdef) {
1408     return graph_substitute(std::vector<MX>{x}, v, vdef).at(0);
1409   }
1410 
graph_substitute(const std::vector<MX> & ex,const std::vector<MX> & expr,const std::vector<MX> & exprs)1411   std::vector<MX> MX::graph_substitute(const std::vector<MX>& ex,
1412                                        const std::vector<MX>& expr,
1413                                        const std::vector<MX>& exprs) {
1414     casadi_assert(expr.size()==exprs.size(),
1415       "Mismatch in the number of expression to substitute: "
1416       + str(expr.size()) + " <-> " + str(exprs.size()) + ".");
1417 
1418     // Sort the expression
1419     Function f("tmp", vector<MX>{}, ex);
1420     MXFunction *ff = f.get<MXFunction>();
1421 
1422     // Get references to the internal data structures
1423     const vector<MXAlgEl>& algorithm = ff->algorithm_;
1424     vector<MX> swork(ff->workloc_.size()-1);
1425 
1426     // A boolean vector indicated whoch nodes are tainted by substitutions
1427     vector<bool> tainted(swork.size());
1428 
1429     // Temporary stringstream
1430     stringstream ss;
1431 
1432     // Construct lookup table for expressions
1433     std::map<const MXNode*, casadi_int> expr_lookup;
1434     for (casadi_int i=0;i<expr.size();++i) {
1435       expr_lookup[expr[i].operator->()] = i;
1436     }
1437 
1438     // Construct found map
1439     std::vector<bool> expr_found(expr.size());
1440 
1441     // Allocate output vector
1442     vector<MX> f_out(f.n_out());
1443     vector<MX> oarg, ores;
1444 
1445     // expr_lookup iterator
1446     std::map<const MXNode*, casadi_int>::const_iterator it_lookup;
1447 
1448     for (auto it=algorithm.begin(); it!=algorithm.end(); ++it) {
1449 
1450       if (it->op != OP_OUTPUT) {
1451         // Check if it->data points to a supplied expr
1452         it_lookup = expr_lookup.find((it->data).operator->());
1453 
1454         if (it->res.front()>=0 && it_lookup!=expr_lookup.end()) {
1455           // Fill in that expression in-place
1456           swork[it->res.front()] = exprs[it_lookup->second];
1457           tainted[it->res.front()] = true;
1458           expr_found[it_lookup->second] = true;
1459           continue;
1460         }
1461       }
1462 
1463       switch (it->op) {
1464       case OP_INPUT:
1465         tainted[it->res.front()] = false;
1466         break;
1467       case OP_PARAMETER:
1468         swork[it->res.front()] = it->data;
1469         tainted[it->res.front()] = false;
1470         break;
1471       case OP_OUTPUT:
1472         casadi_assert(it->data->segment()==0, "Not implemented");
1473         f_out[it->data->ind()] = swork[it->arg.front()];
1474         break;
1475       default:
1476         {
1477           bool node_tainted = false;
1478 
1479           // Arguments of the operation
1480           oarg.resize(it->arg.size());
1481           for (casadi_int i=0; i<oarg.size(); ++i) {
1482             casadi_int el = it->arg[i];
1483             if (el>=0) node_tainted =  node_tainted || tainted[el];
1484             oarg[i] = el<0 ? MX(it->data->dep(i).size()) : swork.at(el);
1485           }
1486 
1487           // Perform the operation
1488           ores.resize(it->res.size());
1489           if (it->res.size()==1 && it->res[0]>=0 && !node_tainted) {
1490             ores.at(0) = it->data;
1491           } else {
1492             it->data->eval_mx(oarg, ores);
1493           }
1494 
1495           // Get the result
1496           for (casadi_int i=0; i<ores.size(); ++i) {
1497             casadi_int el = it->res[i];
1498             if (el>=0) swork.at(el) = ores[i];
1499             if (el>=0) tainted[el] = node_tainted;
1500           }
1501         }
1502       }
1503     }
1504 
1505     bool all_found=true;
1506     for (casadi_int i=0;i<expr.size();++i) {
1507       all_found = all_found && expr_found[i];
1508     }
1509 
1510     //casadi_assert(all_found,
1511     //             "MXFunction::extractNodes(const std::vector<MX>& expr):"
1512     //             " failed to locate all input expr."
1513     //             << std::endl << "Here's a boolean list showing which ones where found: "
1514     //             << expr_found);
1515 
1516     return f_out;
1517 
1518   }
1519 
shared(std::vector<MX> & ex,std::vector<MX> & v,std::vector<MX> & vdef,const std::string & v_prefix,const std::string & v_suffix)1520   void MX::shared(std::vector<MX>& ex, std::vector<MX>& v, std::vector<MX>& vdef,
1521                          const std::string& v_prefix, const std::string& v_suffix) {
1522     try {
1523       // Sort the expression
1524       Function f("tmp", vector<MX>{}, ex);
1525       auto *ff = f.get<MXFunction>();
1526 
1527       // Get references to the internal data structures
1528       const vector<MXAlgEl>& algorithm = ff->algorithm_;
1529       vector<MX> work(ff->workloc_.size()-1);
1530 
1531       // Count how many times an expression has been used
1532       vector<casadi_int> usecount(work.size(), 0);
1533 
1534       // Remember the origin of every calculation
1535       vector<pair<casadi_int, casadi_int> > origin(work.size(), make_pair(-1, -1));
1536 
1537       // Which evaluations to replace
1538       vector<pair<casadi_int, casadi_int> > replace;
1539 
1540       // Evaluate the algorithm to identify which evaluations to replace
1541       casadi_int k=0;
1542       for (auto it=algorithm.begin(); it<algorithm.end(); ++it, ++k) {
1543         // Increase usage counters
1544         switch (it->op) {
1545         case OP_CONST:
1546         case OP_PARAMETER:
1547           break;
1548         default: // Unary operation, binary operation or output
1549           for (casadi_int c=0; c<it->arg.size(); ++c) {
1550             if (usecount[it->arg[c]]==0) {
1551               usecount[it->arg[c]]=1;
1552             } else if (usecount[it->arg[c]]==1) {
1553               replace.push_back(origin[it->arg[c]]);
1554               usecount[it->arg[c]]=-1; // Extracted, do not extract again
1555             }
1556           }
1557         }
1558 
1559         // Perform the operation
1560         switch (it->op) {
1561         case OP_OUTPUT:
1562           break;
1563         case OP_CONST:
1564         case OP_PARAMETER:
1565           usecount[it->res.front()] = -1; // Never extract since it is a primitive type
1566           break;
1567         default:
1568           for (casadi_int c=0; c<it->res.size(); ++c) {
1569             if (it->res[c]>=0) {
1570               work[it->res[c]] = it->data.get_output(c);
1571               usecount[it->res[c]] = 0; // Not (yet) extracted
1572               origin[it->res[c]] = make_pair(k, c);
1573             }
1574           }
1575           break;
1576         }
1577       }
1578 
1579       // New variables and definitions
1580       v.clear();
1581       v.reserve(replace.size());
1582       vdef.clear();
1583       vdef.reserve(replace.size());
1584 
1585       // Quick return
1586       if (replace.empty()) return;
1587 
1588       // Sort the elements to be replaced in the order of appearence in the algorithm
1589       sort(replace.begin(), replace.end());
1590       vector<pair<casadi_int, casadi_int> >::const_iterator replace_it=replace.begin();
1591 
1592       // Name of intermediate variables
1593       stringstream v_name;
1594 
1595       // Arguments for calling the atomic operations
1596       vector<MX> oarg, ores;
1597 
1598       // Evaluate the algorithm
1599       k=0;
1600       for (auto it=algorithm.begin(); it<algorithm.end(); ++it, ++k) {
1601         switch (it->op) {
1602         case OP_OUTPUT:
1603           casadi_assert(it->data->segment()==0, "Not implemented");
1604           ex[it->data->ind()] = work[it->arg.front()];
1605           break;
1606         case OP_CONST:
1607         case OP_PARAMETER:
1608           work[it->res.front()] = it->data;
1609           break;
1610         default:
1611           {
1612             // Arguments of the operation
1613             oarg.resize(it->arg.size());
1614             for (casadi_int i=0; i<oarg.size(); ++i) {
1615               casadi_int el = it->arg[i];
1616               oarg[i] = el<0 ? MX(it->data->dep(i).size()) : work.at(el);
1617             }
1618 
1619             // Perform the operation
1620             ores.resize(it->res.size());
1621             it->data->eval_mx(oarg, ores);
1622 
1623             // Get the result
1624             for (casadi_int i=0; i<ores.size(); ++i) {
1625               casadi_int el = it->res[i];
1626               if (el>=0) work.at(el) = ores[i];
1627             }
1628 
1629             // Possibly replace results with new variables
1630             for (casadi_int c=0; c<it->res.size(); ++c) {
1631               casadi_int ind = it->res[c];
1632               if (ind>=0 && replace_it->first==k && replace_it->second==c) {
1633                 // Store the result
1634                 vdef.push_back(work[ind]);
1635 
1636                 // Create a new variable
1637                 v_name.str(string());
1638                 v_name << v_prefix << v.size() << v_suffix;
1639                 v.push_back(MX::sym(v_name.str()));
1640 
1641                 // Use in calculations
1642                 work[ind] = v.back();
1643 
1644                 // Go to the next element to be replaced
1645                 replace_it++;
1646               }
1647             }
1648           }
1649         }
1650       }
1651     } catch (std::exception& e) {
1652       CASADI_THROW_ERROR("shared", e.what());
1653     }
1654   }
1655 
jacobian(const MX & f,const MX & x,const Dict & opts)1656   MX MX::jacobian(const MX &f, const MX &x, const Dict& opts) {
1657     try {
1658       Dict h_opts;
1659       Dict opts_remainder = extract_from_dict(opts, "helper_options", h_opts);
1660       Function h("helper_jacobian_MX", {x}, {f}, h_opts);
1661       return h.get<MXFunction>()->jac(0, 0, opts_remainder);
1662     } catch (std::exception& e) {
1663       CASADI_THROW_ERROR("jacobian", e.what());
1664     }
1665   }
1666 
hessian(const MX & f,const MX & x,const Dict & opts)1667   MX MX::hessian(const MX& f, const MX& x, const Dict& opts) {
1668     MX g;
1669     return hessian(f, x, g, opts);
1670   }
1671 
hessian(const MX & f,const MX & x,MX & g,const Dict & opts)1672   MX MX::hessian(const MX& f, const MX& x, MX &g, const Dict& opts) {
1673     try {
1674       Dict all_opts = opts;
1675       g = gradient(f, x, opts);
1676       if (!opts.count("symmetric")) all_opts["symmetric"] = true;
1677       return jacobian(g, x, all_opts);
1678     } catch (std::exception& e) {
1679       CASADI_THROW_ERROR("hessian", e.what());
1680     }
1681   }
1682 
1683   std::vector<std::vector<MX> >
forward(const std::vector<MX> & ex,const std::vector<MX> & arg,const std::vector<std::vector<MX>> & v,const Dict & opts)1684   MX::forward(const std::vector<MX> &ex,
1685               const std::vector<MX> &arg,
1686               const std::vector<std::vector<MX> > &v, const Dict& opts) {
1687     try {
1688       // Read options
1689       bool always_inline = true;
1690       bool never_inline = false;
1691 
1692       Dict h_opts;
1693       Dict opts_remainder = extract_from_dict(opts, "helper_options", h_opts);
1694       for (auto&& op : opts_remainder) {
1695         if (op.first=="always_inline") {
1696           always_inline = op.second;
1697         } else if (op.first=="never_inline") {
1698           never_inline = op.second;
1699         } else {
1700           casadi_error("No such option: " + string(op.first));
1701         }
1702       }
1703       // Call internal function on a temporary object
1704       Function temp("forward_temp", arg, ex, h_opts);
1705       std::vector<std::vector<MX> > ret;
1706       temp->call_forward(arg, ex, v, ret, always_inline, never_inline);
1707       return ret;
1708     } catch (std::exception& e) {
1709       CASADI_THROW_ERROR("forward", e.what());
1710     }
1711   }
1712 
1713   std::vector<std::vector<MX> >
reverse(const std::vector<MX> & ex,const std::vector<MX> & arg,const std::vector<std::vector<MX>> & v,const Dict & opts)1714   MX::reverse(const std::vector<MX> &ex,
1715               const std::vector<MX> &arg,
1716               const std::vector<std::vector<MX> > &v, const Dict& opts) {
1717     try {
1718       // Read options
1719       bool always_inline = true;
1720       bool never_inline = false;
1721 
1722 
1723       Dict h_opts;
1724       Dict opts_remainder = extract_from_dict(opts, "helper_options", h_opts);
1725 
1726       for (auto&& op : opts_remainder) {
1727         if (op.first=="always_inline") {
1728           always_inline = op.second;
1729         } else if (op.first=="never_inline") {
1730           never_inline = op.second;
1731         } else {
1732           casadi_error("No such option: " + string(op.first));
1733         }
1734       }
1735       // Call internal function on a temporary object
1736       Function temp("reverse_temp", arg, ex, h_opts);
1737       std::vector<std::vector<MX> > ret;
1738       temp->call_reverse(arg, ex, v, ret, always_inline, never_inline);
1739       return ret;
1740     } catch (std::exception& e) {
1741       CASADI_THROW_ERROR("reverse", e.what());
1742     }
1743   }
1744 
which_depends(const MX & expr,const MX & var,casadi_int order,bool tr)1745   std::vector<bool> MX::which_depends(const MX &expr, const MX &var, casadi_int order, bool tr) {
1746     return _which_depends(expr, var, order, tr);
1747   }
1748 
det(const MX & x)1749   MX MX::det(const MX& x) {
1750     return x->get_det();
1751   }
1752 
inv_node(const MX & x)1753   MX MX::inv_node(const MX& x) {
1754     return x->get_inv();
1755   }
1756 
inv_minor(const MX & A)1757   MX MX::inv_minor(const MX& A) {
1758     casadi_error("Not implemented");
1759   }
1760 
inv(const MX & x,const std::string & lsolver,const Dict & dict)1761   MX MX::inv(const MX& x, const std::string& lsolver, const Dict& dict) {
1762     return solve(x, MX::eye(x.size1()), lsolver, dict);
1763   }
1764 
symvar(const MX & x)1765   std::vector<MX> MX::symvar(const MX& x) {
1766     Function f("f", vector<MX>{}, {x});
1767     return f.free_mx();
1768   }
1769 
matrix_expand(const MX & e,const std::vector<MX> & boundary,const Dict & options)1770   MX MX::matrix_expand(const MX& e, const std::vector<MX> &boundary, const Dict &options) {
1771     return matrix_expand(vector<MX>{e}, boundary, options).at(0);
1772   }
1773 
matrix_expand(const std::vector<MX> & e,const std::vector<MX> & boundary,const Dict & options)1774   std::vector<MX> MX::matrix_expand(const std::vector<MX>& e,
1775                                     const std::vector<MX> &boundary,
1776                                     const Dict &options) {
1777 
1778     // Create symbols for boundary nodes
1779     std::vector<MX> syms(boundary.size());
1780 
1781     for (casadi_int i=0;i<syms.size();++i) {
1782       syms[i] = MX::sym("x", boundary[i].sparsity());
1783     }
1784 
1785     // Substitute symbols for boundary nodes
1786     std::vector<MX> ret = graph_substitute(e, boundary, syms);
1787 
1788     // Obtain list of dependents
1789     std::vector<MX> v = symvar(veccat(ret));
1790 
1791     // Construct an MXFunction with it
1792     Function f("tmp", v, ret);
1793 
1794     // Expand to SXFunction
1795     Function s = f.expand("expand_" + f.name(), options);
1796     std::vector<MX> r;
1797     s.call(graph_substitute(v, syms, boundary), r, true);
1798     return r;
1799   }
1800 
kron(const MX & a,const MX & b)1801   MX MX::kron(const MX& a, const MX& b) {
1802     const Sparsity &a_sp = a.sparsity();
1803     MX filler(b.size());
1804     std::vector< std::vector< MX > > blocks(a.size1(), std::vector< MX >(a.size2(), filler));
1805     for (casadi_int i=0; i<a.size1(); ++i) {
1806       for (casadi_int j=0; j<a.size2(); ++j) {
1807         casadi_int k = a_sp.get_nz(i, j);
1808         if (k!=-1) {
1809           blocks[i][j] = a.nz(k)*b;
1810         }
1811       }
1812     }
1813     return blockcat(blocks);
1814   }
1815 
repmat(const MX & x,casadi_int n,casadi_int m)1816   MX MX::repmat(const MX& x, casadi_int n, casadi_int m) {
1817     if (n==0 && m==0) {
1818       return MX();
1819     } else if (n==0) {
1820       return MX(0, x.size2()*m);
1821     } else if (m==0) {
1822       return MX(x.size1()*n, 0);
1823     } else if (n==1 && m==1) {
1824       return x;
1825     } else {
1826       return x->get_repmat(n, m);
1827     }
1828   }
1829 
repsum(const MX & x,casadi_int n,casadi_int m)1830   MX MX::repsum(const MX& x, casadi_int n, casadi_int m) {
1831     return x->get_repsum(n, m);
1832   }
1833 
solve(const MX & a,const MX & b,const std::string & lsolver,const Dict & dict)1834   MX MX::solve(const MX& a, const MX& b, const std::string& lsolver, const Dict& dict) {
1835     Linsol mysolver("tmp", lsolver, a.sparsity(), dict);
1836     return mysolver.solve(a, b, false);
1837   }
1838 
pinv(const MX & A,const std::string & lsolver,const Dict & dict)1839   MX MX::pinv(const MX& A, const std::string& lsolver, const Dict& dict) {
1840     if (A.size1()>=A.size2()) {
1841       return solve(mtimes(A.T(), A), A.T(), lsolver, dict);
1842     } else {
1843       return solve(mtimes(A, A.T()), A, lsolver, dict).T();
1844     }
1845   }
1846 
expm_const(const MX & A,const MX & t)1847   MX MX::expm_const(const MX& A, const MX& t) {
1848     Dict opts;
1849     opts["const_A"] = true;
1850     Function ret = expmsol("mysolver", "slicot", A.sparsity(), opts);
1851     return ret(std::vector<MX>{A, t})[0];
1852   }
1853 
expm(const MX & A)1854   MX MX::expm(const MX& A) {
1855     Function ret = expmsol("mysolver", "slicot", A.sparsity());
1856     return ret(std::vector<MX>{A, 1})[0];
1857   }
1858 
nullspace(const MX & A)1859   MX MX::nullspace(const MX& A) {
1860     SX A_sx = SX::sym("A", A.sparsity());
1861     Function f("nullspace", {A_sx}, {SX::nullspace(A_sx)});
1862     return f(A).at(0);
1863   }
1864 
depends_on(const MX & x,const MX & arg)1865   bool MX::depends_on(const MX &x, const MX &arg) {
1866     if (x.nnz()==0) return false;
1867 
1868     // Construct a temporary algorithm
1869     Function temp("tmp", {arg}, {x});
1870 
1871     // Perform a single dependency sweep
1872     vector<bvec_t> t_in(arg.nnz(), 1), t_out(x.nnz());
1873     temp({get_ptr(t_in)}, {get_ptr(t_out)});
1874 
1875     // Loop over results
1876     for (casadi_int i=0; i<t_out.size(); ++i) {
1877       if (t_out[i]) return true;
1878     }
1879 
1880     return false;
1881   }
1882 
find(const MX & x)1883   MX MX::find(const MX& x) {
1884     return x->get_find();
1885   }
1886 
low(const MX & v,const MX & p,const Dict & options)1887   MX MX::low(const MX& v, const MX& p, const Dict& options) {
1888     return p->get_low(v, options);
1889   }
1890 
bspline(const MX & x,const DM & coeffs,const std::vector<std::vector<double>> & knots,const std::vector<casadi_int> & degree,casadi_int m,const Dict & opts)1891   MX MX::bspline(const MX& x,
1892             const DM& coeffs,
1893             const std::vector< std::vector<double> >& knots,
1894             const std::vector<casadi_int>& degree,
1895             casadi_int m,
1896             const Dict& opts) {
1897     return BSpline::create(x, knots, coeffs.nonzeros(), degree, m, opts);
1898   }
1899 
bspline(const MX & x,const MX & coeffs,const std::vector<std::vector<double>> & knots,const std::vector<casadi_int> & degree,casadi_int m,const Dict & opts)1900   MX MX::bspline(const MX& x, const MX& coeffs,
1901             const std::vector< std::vector<double> >& knots,
1902             const std::vector<casadi_int>& degree,
1903             casadi_int m,
1904             const Dict& opts) {
1905     return BSplineParametric::create(x, coeffs, knots, degree, m, opts);
1906   }
1907 
bspline_dual(const std::vector<double> & x,const std::vector<std::vector<double>> & knots,const std::vector<casadi_int> & degree,const Dict & opts)1908   DM MX::bspline_dual(const std::vector<double>& x,
1909             const std::vector< std::vector<double> >& knots,
1910             const std::vector<casadi_int>& degree,
1911             const Dict& opts) {
1912     return BSpline::dual(x, knots, degree, opts);
1913   }
1914 
convexify(const MX & H,const Dict & opts)1915   MX MX::convexify(const MX& H,
1916             const Dict& opts) {
1917     return H->get_convexify(opts);
1918   }
1919 
interpn_G(casadi_int i,const MX & v,const std::vector<MX> & xis,const std::vector<MX> & L,const std::vector<MX> & Lp,const std::vector<casadi_int> & strides,const Slice & I,const MX & offset=0)1920   MX interpn_G(casadi_int i, // Dimension to interpolate along
1921                 const MX& v, // Coefficients
1922                 const std::vector<MX>& xis, // Normalised coordinates
1923                 const std::vector<MX>& L, const std::vector<MX>& Lp, // Lower indices
1924                 const std::vector<casadi_int>& strides,
1925                 const Slice& I,
1926                 const MX& offset=0 // Offset into coefficients vector
1927                 ) {
1928     if (i==0) {
1929       MX ret;
1930       v.get_nz(ret, false, offset, I);
1931       return ret;
1932     } else {
1933       casadi_int j = xis.size()-i;
1934       MX offsetL, offsetR;
1935       if (strides[j]==1) {
1936         offsetL = offset+L[j];
1937         offsetR = offset+Lp[j];
1938       } else {
1939         offsetL = offset+L[j]*strides[j];
1940         offsetR = offsetL+strides[j];
1941       }
1942       MX vl = interpn_G(i-1, v, xis, L, Lp, strides, I, offsetL);
1943       MX vu = interpn_G(i-1, v, xis, L, Lp, strides, I, offsetR);
1944 
1945       // Perform interpolation between vl and vu
1946       return vl + xis[j]*(vu-vl);
1947     }
1948   }
1949 
interpn_linear(const std::vector<MX> & x,const MX & v,const std::vector<MX> & xq,const Dict & opts)1950   MX MX::interpn_linear(const std::vector<MX>& x, const MX& v, const std::vector<MX>& xq,
1951       const Dict& opts) {
1952 
1953     casadi_int n_dim = x.size();
1954     std::vector<std::string> lookup_mode(n_dim, "auto");
1955     for (auto&& op : opts) {
1956       if (op.first=="lookup_mode") {
1957         lookup_mode = op.second;
1958       } else {
1959         casadi_error("Unknown option '" + op.first + "'.");
1960       }
1961     }
1962 
1963     casadi_assert_dev(xq.size()==n_dim);
1964     casadi_assert_dev(v.is_vector());
1965 
1966     // Extract grid dimensions
1967     std::vector<casadi_int> x_dims;
1968     for (auto e : x) x_dims.push_back(e.numel());
1969 
1970     // Determine multipicity of output
1971     casadi_int n_out = v.numel()/product(x_dims);
1972     casadi_assert(n_out*product(x_dims)==v.numel(),
1973       "Dimension mismatch: coefficients (" + str(v.numel()) + ") should be "
1974       "an integer multiple of product-of-dimensions (" + str(product(x_dims)) + ").");
1975 
1976     // Dimension check xq
1977     casadi_int nq = xq[0].numel();
1978     for (auto e : xq) {
1979       casadi_assert_dev(e.is_vector() && e.numel()==nq);
1980     }
1981 
1982     // Compute stride vector
1983     std::vector<casadi_int> strides;
1984     strides.push_back(n_out);
1985     for (auto d : x_dims) strides.push_back(strides.back()*d);
1986 
1987     // Pre-compute lower index and normalized coordinate
1988     // (Allows for more sub-expression sharing)
1989     std::vector<MX> xis, Ls, Lps;
1990     for (casadi_int i=0;i<n_dim;++i) {
1991       MX L = low(x[i], xq[i], {{"lookup_mode", lookup_mode[i]}});
1992       MX Lp = L+1;
1993       MX xl, xu;
1994       x[i].get_nz(xl, false, L);
1995       x[i].get_nz(xu, false, Lp);
1996       xis.push_back((xq[i]-xl)/(xu-xl));
1997       Ls.push_back(L);
1998       Lps.push_back(Lp);
1999     }
2000 
2001     Slice I(0, n_out);
2002 
2003     return interpn_G(n_dim, v, xis, Ls, Lps, strides, I);
2004   }
2005 
get_input(const Function & f)2006   std::vector<MX> MX::get_input(const Function& f) {
2007     return f.mx_in();
2008   }
2009 
get_free(const Function & f)2010   std::vector<MX> MX::get_free(const Function& f) {
2011     return f.free_mx();
2012   }
2013 
_bilin(const MX & A,const MX & x,const MX & y)2014   MX MX::_bilin(const MX& A, const MX& x, const MX& y) {
2015    return A->get_bilin(x, y);
2016  }
2017 
_rank1(const MX & A,const MX & alpha,const MX & x,const MX & y)2018  MX MX::_rank1(const MX& A, const MX& alpha, const MX& x, const MX& y) {
2019    return A->get_rank1(alpha, x, y);
2020  }
2021 
eval_mx(const std::vector<MX> & arg,std::vector<MX> & res) const2022  void MX::eval_mx(const std::vector<MX>& arg, std::vector<MX>& res) const {
2023    try {
2024      (*this)->eval_mx(arg, res);
2025    } catch (std::exception& e) {
2026      CASADI_THROW_ERROR_OBJ("eval_mx", e.what());
2027    }
2028  }
2029 
ad_forward(const std::vector<std::vector<MX>> & fseed,std::vector<std::vector<MX>> & fsens) const2030  void MX::ad_forward(const std::vector<std::vector<MX> >& fseed,
2031                  std::vector<std::vector<MX> >& fsens) const {
2032    try {
2033      (*this)->ad_forward(fseed, fsens);
2034    } catch (std::exception& e) {
2035      CASADI_THROW_ERROR_OBJ("ad_forward", e.what());
2036    }
2037  }
2038 
ad_reverse(const std::vector<std::vector<MX>> & aseed,std::vector<std::vector<MX>> & asens) const2039  void MX::ad_reverse(const std::vector<std::vector<MX> >& aseed,
2040                  std::vector<std::vector<MX> >& asens) const {
2041    try {
2042      (*this)->ad_reverse(aseed, asens);
2043    } catch (std::exception& e) {
2044      CASADI_THROW_ERROR_OBJ("ad_reverse", e.what());
2045    }
2046  }
2047 
2048 #undef CASADI_THROW_ERROR
2049 } // namespace casadi
2050