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