integration_tools.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU 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 "integration_tools.hpp"
27 #include "integrator.hpp"
28 #include "rootfinder.hpp"
29 #include "polynomial.hpp"
30 #include "nlpsol.hpp"
31 
32 namespace casadi {
33 
34 const long double legendre_points1[] = { 0.50000000000000000000 };
35 const long double legendre_points2[] =
36  { 0.21132486540518713447, 0.78867513459481286553 };
37 const long double legendre_points3[] =
38  { 0.11270166537925824235, 0.50000000000000000000,
39  0.88729833462074170214 };
40 const long double legendre_points4[] =
41  { 0.06943184420297354720, 0.33000947820757187134,
42  0.66999052179242823968, 0.93056815579702623076 };
43 const long double legendre_points5[] =
44  { 0.04691007703066807366, 0.23076534494715861268,
45  0.49999999999999994449, 0.76923465505284149835, 0.95308992296933192634 };
46 const long double legendre_points6[] =
47  { 0.03376524289842347537, 0.16939530676686742616,
48  0.38069040695840172805, 0.61930959304159849399, 0.83060469323313235179,
49  0.96623475710157580298 };
50 const long double legendre_points7[] =
51  { 0.02544604382862047931, 0.12923440720030288098,
52  0.29707742431130129690, 0.50000000000000000000, 0.70292257568869853657,
53  0.87076559279969734106, 0.97455395617137896558 };
54 const long double legendre_points8[] =
55  { 0.01985507175123157886, 0.10166676129318691357,
56  0.23723379504183561561, 0.40828267875217505445, 0.59171732124782483453,
57  0.76276620495816449541, 0.89833323870681347501, 0.98014492824876797705 };
58 const long double legendre_points9[] =
59  { 0.01591988024618706810, 0.08198444633668211523,
60  0.19331428364970504319, 0.33787328829809543107, 0.49999999999999988898,
61  0.66212671170190451342, 0.80668571635029517886, 0.91801555366331766272,
62  0.98408011975381259884 };
63 const long double* legendre_points[] =
66 
67 // Radau collocation points
68 const long double radau_points1[] =
69  { 1.00000000000000000000 };
70 const long double radau_points2[] =
71  { 0.33333333333333337034, 1.00000000000000000000 };
72 const long double radau_points3[] =
73  { 0.15505102572168222297, 0.64494897427831787695,
74  1.00000000000000000000 };
75 const long double radau_points4[] =
76  { 0.08858795951270420632, 0.40946686444073465694,
77  0.78765946176084700170, 1.00000000000000000000 };
78 const long double radau_points5[] =
79  { 0.05710419611451822419, 0.27684301363812369168,
80  0.58359043236891683382, 0.86024013565621926247, 1.00000000000000000000 };
81 const long double radau_points6[] =
82  { 0.03980985705146905529, 0.19801341787360787761,
83  0.43797481024738621480, 0.69546427335363603106, 0.90146491420117347282,
84  1.00000000000000000000 };
85 const long double radau_points7[] =
86  { 0.02931642715978521885, 0.14807859966848435640,
87  0.33698469028115418666, 0.55867151877155019069, 0.76923386203005450490,
88  0.92694567131974103802, 1.00000000000000000000 };
89 const long double radau_points8[] =
90  { 0.02247938643871305597, 0.11467905316090415413,
91  0.26578982278458951338, 0.45284637366944457959, 0.64737528288683043876,
92  0.81975930826310761113, 0.94373743946307731001, 1.00000000000000000000 };
93 const long double radau_points9[] =
94  { 0.01777991514736393386, 0.09132360789979432347,
95  0.21430847939563035798, 0.37193216458327238438, 0.54518668480342658000,
96  0.71317524285556954666, 0.85563374295785443735, 0.95536604471003006012,
97  1.00000000000000000000 };
98 const long double* radau_points[] =
101 
102 template<typename RealT>
103 std::vector<RealT> collocation_pointsGen(casadi_int order, const std::string& scheme) {
104  if (scheme=="radau") {
105  casadi_assert(order>0 && order<10,
106  "Error in collocationPoints(order, scheme): "
107  "only order up to 9 supported for scheme 'radau', but got " + str(order) + ".");
108  return std::vector<RealT>(radau_points[order], radau_points[order]+order);
109  } else if (scheme=="legendre") {
110  casadi_assert(order>0 && order<10,
111  "Error in collocationPoints(order, scheme): "
112  "only order up to 9 supported for scheme 'legendre', but got " + str(order) + ".");
113  return std::vector<RealT>(legendre_points[order], legendre_points[order]+order);
114  } else {
115  casadi_error("Error in collocationPoints(order, scheme): unknown scheme '"
116  + scheme + "'. Select one of 'radau', 'legendre'.");
117  }
118 }
119 
120 std::vector<double> collocation_points(casadi_int order, const std::string& scheme) {
121  return collocation_pointsGen<double>(order, scheme);
122 }
123 
124 std::vector<long double> collocation_pointsL(casadi_int order, const std::string& scheme) {
125  return collocation_pointsGen<long double>(order, scheme);
126 }
127 
128 Function simpleRK(Function f, casadi_int N, casadi_int order) {
129  // Consistency check
130  casadi_assert(N>=1,
131  "Parameter N (number of steps) must be at least 1, but got " + str(N) + ".");
132  casadi_assert(order==4, "Only RK order 4 is supported now.");
133  casadi_assert(f.n_in()==2, "Function must have two inputs: x and p");
134  casadi_assert(f.n_out()==1, "Function must have one outputs: dot(x)");
135 
136  MX x0 = MX::sym("x0", f.sparsity_in(0));
137  MX p = MX::sym("p", f.sparsity_in(1));
138  MX h = MX::sym("h");
139 
140  std::vector<double> b(order);
141  b[0]=1.0/6;
142  b[1]=1.0/3;
143  b[2]=1.0/3;
144  b[3]=1.0/6;
145 
146  std::vector<double> c(order);
147  c[0]=0;
148  c[1]=1.0/2;
149  c[2]=1.0/2;
150  c[3]=1;
151 
152  std::vector< std::vector<double> > A(order-1);
153  A[0].resize(1);
154  A[0][0]=1.0/2;
155  A[1].resize(2);
156  A[1][0]=0;A[1][1]=1.0/2;
157  A[2].resize(3);
158  A[2][0]=0;
159  A[2][1]=0;A[2][2]=1;
160 
161  // Time step
162  MX dt = h/N;
163 
164  std::vector<MX> k(order);
165  std::vector<MX> f_arg(2);
166 
167  // Integrate
168  MX xf = x0;
169  for (casadi_int i=0; i<N; ++i) {
170  for (casadi_int j=0; j<order; ++j) {
171  MX xL = 0;
172  for (casadi_int jj=0; jj<j; ++jj) {
173  xL += k.at(jj)*A.at(j-1).at(jj);
174  }
175  f_arg[0] = xf+xL;
176  f_arg[1] = p;
177  k[j] = dt*f(f_arg).at(0);
178  }
179 
180  for (casadi_int j=0; j<order; ++j) {
181  xf += b.at(j)*k.at(j);
182  }
183  }
184 
185  // Form discrete-time dynamics
186  return Function("F", {x0, p, h}, {xf}, {"x0", "p", "h"}, {"xf"});
187 }
188 
189 void collocation_interpolators(const std::vector<double> & tau,
190  std::vector< std::vector<double> > &C, std::vector< double > &D) {
191  // Find the degree of the interpolation
192  casadi_int deg = tau.size();
193 
194  // Include zero
195  std::vector<double> etau_root = tau;
196  etau_root.insert(etau_root.begin(), 0);
197 
198  // Allocate storage space for resulting coefficients
199  C.resize(deg+1);
200  for (casadi_int i=0;i<deg+1;++i) {
201  C[i].resize(deg+1);
202  }
203  D.resize(deg+1);
204 
205  // Collocation point
206  SX tau_sym = SX::sym("tau");
207 
208  // For all collocation points
209  for (casadi_int j=0; j<deg+1; ++j) {
210  // Construct Lagrange polynomials to get the polynomial basis at the collocation point
211  SX L = 1;
212  for (casadi_int j2=0; j2<deg+1; ++j2) {
213  if (j2 != j) {
214  L *= (tau_sym-etau_root[j2])/(etau_root[j]-etau_root[j2]);
215  }
216  }
217 
218  Function lfcn("lfcn", {tau_sym}, {L});
219 
220  // Evaluate the polynomial at the final time to get the
221  // coefficients of the continuity equation
222  D[j] = lfcn(std::vector<DM>{1.}).at(0)->front();
223 
224  // Evaluate the time derivative of the polynomial at all collocation points to
225  // get the coefficients of the continuity equation
226  Function tfcn("tfcn", {tau_sym}, {tangent(L, tau_sym)});
227  for (casadi_int j2=0; j2<deg+1; ++j2) {
228  C[j2][j] = tfcn(std::vector<DM>{etau_root[j2]}).at(0)->front();
229  }
230  }
231 }
232 
233 void collocation_coeff(const std::vector<double> & tau,
234  DM &C, DM &D, DM &B) {
235  // Find the degree of the interpolation
236  casadi_int deg = tau.size();
237 
238  // Include zero
239  std::vector<double> etau_root = tau;
240  etau_root.insert(etau_root.begin(), 0);
241 
242  // Check if tau ends with '1' (cfr. radau scheme)
243  bool has_end = tau.back()==1;
244 
245  // Coefficients of the collocation equation
246  std::vector<std::vector<double> > C_(deg+1, std::vector<double>(deg+1, 0));
247 
248  // Coefficients of the continuity equation
249  std::vector<double> D_(deg+1, 0);
250 
251  // Coefficients of the quadratures
252  std::vector<double> B_(deg+1, 0);
253 
254  // For all collocation points
255  for (casadi_int j=0; j<deg+1; ++j) {
256 
257  // Construct Lagrange polynomials to get the polynomial basis at the collocation point
258  Polynomial p = 1;
259  for (casadi_int r=0; r<deg+1; ++r) {
260  if (r!=j) {
261  p *= Polynomial(-etau_root[r], 1)/(etau_root[j]-etau_root[r]);
262  }
263  }
264 
265  // Evaluate the polynomial at the final time to get the
266  // coefficients of the continuity equation
267  if (has_end) {
268  D_[j] = j==deg ? 1 : 0;
269  } else {
270  D_[j] = p(1.0);
271  }
272  // Evaluate the time derivative of the polynomial at all collocation points to
273  // get the coefficients of the continuity equation
274  Polynomial dp = p.derivative();
275  for (casadi_int r=0; r<deg+1; ++r) {
276  C_[j][r] = dp(etau_root[r]);
277  }
278 
279  // Integrate polynomial to get the coefficients of the quadratures
280  Polynomial ip = p.anti_derivative();
281  B_[j] = ip(1.0);
282  }
283  C = DM(C_);
284  C = C(Slice(), Slice(1, deg+1)); // NOLINT(cppcoreguidelines-slicing)
285  D = DM(D_);
286  B = DM(std::vector<double>(B_.begin()+1, B_.end()));
287 }
288 
289 Function simpleIRK(Function f, casadi_int N, casadi_int order, const std::string& scheme,
290  const std::string& solver,
291  const Dict& solver_options) {
292  // Consistency check
293  casadi_assert(N>=1,
294  "Parameter N (number of steps) must be at least 1, but got " + str(N) + ".");
295  casadi_assert(f.n_in()==2, "Function must have two inputs: x and p");
296  casadi_assert(f.n_out()==1, "Function must have one outputs: dot(x)");
297 
298  // Obtain collocation points
299  std::vector<double> tau_root = collocation_points(order, scheme);
300 
301  // Retrieve collocation interpolating matrices
302  std::vector < std::vector <double> > C;
303  std::vector < double > D;
304  collocation_interpolators(tau_root, C, D);
305 
306  // Inputs of constructed function
307  MX x0 = MX::sym("x0", f.sparsity_in(0));
308  MX p = MX::sym("p", f.sparsity_in(1));
309  MX h = MX::sym("h");
310 
311  // Time step
312  MX dt = h/N;
313 
314  // Implicitly defined variables
315  MX v = MX::sym("v", repmat(x0.sparsity(), order));
316  std::vector<MX> x = vertsplit(v, x0.size1());
317  x.insert(x.begin(), x0);
318 
319  // Collect the equations that implicitly define v
320  std::vector<MX> V_eq, f_in(2), f_out;
321  for (casadi_int j=1; j<order+1; ++j) {
322  // Expression for the state derivative at the collocation point
323  MX xp_j = 0;
324  for (casadi_int r=0; r<=order; ++r) xp_j+= C[j][r]*x[r];
325 
326  // Collocation equations
327  f_in[0] = x[j];
328  f_in[1] = p;
329  f_out = f(f_in);
330  V_eq.push_back(dt*f_out.at(0)-xp_j);
331  }
332 
333  // Root-finding function
334  Function rfp("rfp", {v, x0, p, h}, {vertcat(V_eq)});
335 
336  // Create a implicit function instance to solve the system of equations
337  Function ifcn = rootfinder("ifcn", solver, rfp, solver_options);
338 
339  // Get state at end time
340  MX xf = x0;
341  for (casadi_int k=0; k<N; ++k) {
342  std::vector<MX> ifcn_out = ifcn({repmat(xf, order), xf, p, h});
343  x = vertsplit(ifcn_out[0], xf.size1());
344 
345  // State at end of step
346  xf = D[0]*xf;
347  for (casadi_int i=1; i<=order; ++i) {
348  xf += D[i]*x[i-1];
349  }
350  }
351 
352  // Form discrete-time dynamics
353  return Function("F", {x0, p, h}, {xf}, {"x0", "p", "h"}, {"xf"});
354 }
355 
356 Function simpleIntegrator(Function f, const std::string& plugin,
357  const Dict& plugin_options) {
358  // Consistency check
359  casadi_assert(f.n_in()==2, "Function must have two inputs: x and p");
360  casadi_assert(f.n_out()==1, "Function must have one outputs: dot(x)");
361 
362  // Sparsities
363  Sparsity x_sp = f.sparsity_in(0);
364  Sparsity p_sp = f.sparsity_in(1);
365 
366  // Wrapper function inputs
367  MX x = MX::sym("x", x_sp);
368  MX u = MX::sym("u", vertcat(Sparsity::scalar(), vec(p_sp))); // augment p with t
369 
370  // Normalized xdot
371  casadi_int u_offset[] = {0, 1, 1+p_sp.size1()};
372  std::vector<MX> pp = vertsplit(u, std::vector<casadi_int>(u_offset, u_offset+3));
373  MX h = pp[0];
374  MX p = reshape(pp[1], p_sp.size());
375  MX f_in[] = {x, p};
376  MX xdot = f(std::vector<MX>(f_in, f_in+2)).at(0);
377  xdot *= h;
378 
379  // Form DAE function
380  MXDict dae = {{"x", x}, {"p", u}, {"ode", xdot}};
381 
382  // Create integrator function with normalized time from 0 to 1
383  Function ifcn = integrator("integrator", plugin, dae, plugin_options);
384 
385  // Inputs of constructed function
386  MX x0 = MX::sym("x0", x_sp);
387  p = MX::sym("p", p_sp);
388  h = MX::sym("h");
389 
390  // State at end
391  MX xf = ifcn(MXDict{{"x0", x0}, {"p", vertcat(h, vec(p))}}).at("xf");
392 
393  // Form discrete-time dynamics
394  return Function("F", {x0, p, h}, {xf}, {"x0", "p", "h"}, {"xf"});
395 }
396 
397 
398 std::vector<casadi_int> invert_lookup(const std::vector<casadi_int>& lookup) {
399  std::vector<casadi_int> ret(lookup.size(), -1);
400  for (casadi_int i=0;i<lookup.size();++i) {
401  casadi_int e = lookup[i];
402  if (e>=0) {
403  ret[e] = i;
404  }
405  }
406  return ret;
407 }
408 
409 namespace IndexReduction {
410 
411  struct EquationStruct;
412  /*
413  struct VariableStruct {
414  std::vector<struct EquationStruct*> eqs;
415  // Eligability to serve as candidate, one for each pass during phase 1
416  std::vector<bool> eligible1;
417  struct EquationStruct* assign;
418  // Which variable is this variable's derivative?
419  struct VariableStruct* der;
420  // Which variable produces this variable by differentiation?
421  struct VariableStruct* ;
422  bool visited;
423  // Eligability to serve as candidate during phase 2
424  bool eligible2;
425  };*/
426 
427  struct VariableStruct {
428  std::vector<struct EquationStruct*> eqs;
429  std::vector<struct EquationStruct*> eqs0;
430  struct EquationStruct* assign = nullptr;
431  // Which variable is this variable's derivative?
432  struct VariableStruct* der = nullptr;
433  // Which variable produces this variable by differentiation?
434  struct VariableStruct* der_inv = nullptr;
435  casadi_int i; // Position in Graph::V
436  bool visited = false;
437  bool deleted = false;
438  };
439 
440  struct EquationStruct {
441  std::vector<struct VariableStruct*> vars;
442  std::vector<struct VariableStruct*> vars0;
443  struct VariableStruct* assign = nullptr;
444  // Which equation is this equations's derivative?
445  struct EquationStruct* der = nullptr;
446  // Which equations produces this equations by differentiation?
447  struct EquationStruct* der_inv = nullptr;
448  casadi_int i; // Position in Graph::E
449  bool visited = false;
450  };
451 
452  typedef struct VariableStruct Variable;
453  typedef struct EquationStruct Equation;
454 
455  // Bipartite graph
456  struct GraphStruct {
457  std::vector<Variable*> V;
458  std::vector<Equation*> E;
459  casadi_int ncol_orig;
460  casadi_int nrow_orig;
461  };
462 
463  typedef struct GraphStruct Graph;
464 
465  void graph_add_der(Graph& G, Variable* v) {
466  // Push new equation to the graph
467  G.V.push_back(new Variable());
468  Variable* v_new = G.V.back();
469 
470  // Get a position
471  v_new->i = G.V.size()-1;
472 
473  // Add derivative relationship between new and old variable
474  v_new->der_inv = v;
475  v->der = v_new;
476  }
477 
479  auto it = std::find(e->vars0.begin(), e->vars0.end(), v);
480  if (it==e->vars0.end()) {
481  e->vars0.push_back(v);
482  if (!v->deleted) {
483  e->vars.push_back(v);
484  v->eqs.push_back(e);
485  }
486  }
487  }
488 
489  void graph_add_der(Graph& G, Equation* e, bool add_old=false) {
490  // Push new equation to the graph
491  G.E.push_back(new Equation());
492 
493  Equation* e_new = G.E.back();
494 
495  // Get a position
496  e_new->i = G.E.size()-1;
497 
498  // Add derivative relationship between new and old equation
499  e_new->der_inv = e;
500  e->der = e_new;
501 
502  // Loop over all variables in old equation
503  for (auto* v : e->vars0) {
504  // We depend on the old variable
505  if (add_old) {
506  add_variable(e_new, v);
507  }
508  // And we depend on its derivative (create if not present)
509  if (!v->der) graph_add_der(G, v);
510  add_variable(e_new, v->der);
511  }
512 
513  }
514 
516 
517  // Pantelides alg 3.2: Colour i (1)
518  i->visited = true;
519 
520  // Look for unassigned candidate variables
521  // Pantelides alg 3.2: (2) If a V-node j exists such that edge (i-j) exists...
522  for (auto* j : i->vars) {
523  // Pantelides alg 3.2: ... and ASSIGN (j)= 0 then:
524  if (j->assign==nullptr) {
525  // Found such variable
526  // Assign
527  // Pantelides alg 3.2: (2b) Set ASSIGN(j)=i
528  j->assign = i; i->assign = j;
529 
530  // Pantelides alg 3.2: (2a) set PATHFOUND = TRUE, (2c) return
531  return true; // End successfully
532  }
533  }
534 
535  // Look for assigned candidate variables
536  // Pantelides alg 3.2: (3) For every j such that edge (i-j) exists
537  for (auto j : i->vars) {
538  // Pantelides alg 3.2: ... and j is uncoloured do:
539  if (!j->visited) {
540  // Pantelides alg 3.2: (3a) Colour j
541  j->visited = true;
542 
543  // Pantelides alg 3.2: (3b) Set k = ASSIGN (j)
544  Equation* k = j->assign;
545 
546  // Pantelides alg 3.2: (3c) AUGMENTPATH (k, PATHFOUND)
547  if (dfs_match_pantelides(k)) {
548  // Re-assignment
549  // Pantelides alg 3.2: (3d-1) Set ASSIGN (j) = i
550  j->assign = i; i->assign = j;
551  // Pantelides alg 3.2: (3d-2) Return
552  return true;
553  }
554 
555  }
556  }
557 
558  // Exhausted all options; return unsuccessfully
559  return false;
560  }
561 
562 
576  std::vector<casadi_int>& var_map, std::vector<casadi_int>& eq_map,
577  casadi_int max_iter) {
578 
579  // Pantelides alg 4.1: Set N'=N do (2)
580  casadi_int Np = G.E.size();
581  // Pantelides alg 4.1: For k=1 to N' do (3)
582  for (casadi_int k=0;k<Np;++k) {
583  // Pantelides alg 4.1: Set i=k (3a)
584  Equation* i = G.E[k];
585 
586  casadi_int iter = 0;
587  // Pantelides alg 4.1: Repeat (3b)
588  while (true) {
589  if (iter++ > max_iter) return false;
590  // Pantelides alg 4.1: delete all V-nodes with A!=0 and their incident edges
591  // * from the graph (3b-1)
592  for (auto* v : G.V) {
593  if (v->der) {
594  v->deleted = true;
595  v->eqs.clear();
596  for (auto* e : G.E) {
597  auto it = std::find(e->vars.begin(), e->vars.end(), v);
598  if (it!=e->vars.end()) e->vars.erase(it);
599  }
600  }
601  }
602 
603  // Pantelides alg 4.1: Designate all nodes as "uncoloured" (3b-2)
604  for (auto* v : G.V) v->visited = false;
605  for (auto* e : G.E) e->visited = false;
606 
607  // Pantelides alg 4.1: Set PATHFOUND= FALSE (3b-3)
608  // Pantelides alg 4.1: AUGMENTPATH (i, PATHFOUND) (3b-4)
609  // Pantelides alg 4.1: Until PATHFOUND (3c)
610  if (dfs_match_pantelides(i)) break; // out of while loop
611 
612  // Pantelides alg 4.1: If PATHFOUND FALSE then (3b-5)
613  casadi_int n = G.V.size();
614  // Pantelides alg 4.1: For every coloured V-node j do (3b-5-i)
615  for (casadi_int jj=0;jj<n;++jj) {
616  Variable* j = G.V[jj];
617  if (j->visited && !j->deleted) {
618  graph_add_der(G, j);
619  }
620  }
621 
622  // Pantelides alg 4.1: For every coloured E-node l do (3b-5-ii)
623  n = G.E.size();
624  for (casadi_int ll=0;ll<n;++ll) {
625  Equation* l = G.E[ll];
626  if (l->visited) graph_add_der(G, l, true);
627  }
628 
629  for (casadi_int ll=n;ll<G.E.size();++ll) {
630  Equation* l = G.E[ll];
631  bool valid = false;
632  for (auto* v : l->vars0) {
633  if (!v->eqs.empty()) valid = true;
634  }
635  if (!valid) return false;
636  }
637 
638  // Pantelides alg 4.1: For every coloured V-node j set
639  // ASSIGN (A(j))= B(ASSIGN (j)) (3b-5-iii)
640  for (auto* j : G.V) {
641  if (j->visited && !j->deleted) {
642  j->der->assign = j->assign->der;
643  j->assign->der->assign = j->der;
644  }
645  }
646 
647  // Pantelides alg 4.1: Set i=B(i) (3b-5-iv)
648  i = i->der;
649 
650  }
651  }
652  return true;
653  }
654 
655  void dae_struct_detect(const std::string& algorithm,
656  const Sparsity& graph, std::vector<casadi_int>& var_map,
657  std::vector<casadi_int>& eq_map,
658  casadi_int max_iter) {
659  // Input sanitization
660  casadi_assert(var_map.size()==graph.size2(),
661  "var_map size must match graph columns.");
662 
663  // Graph structure
664  Graph G;
665 
666  // Allocate space for node representation
667  G.E.resize(graph.size1(), nullptr);
668  G.V.resize(graph.size2(), nullptr);
669  G.nrow_orig = graph.size1();
670  G.ncol_orig = graph.size2();
671 
672  for (auto*& e : G.E) e = new Equation();
673  for (auto*& v : G.V) v = new Variable();
674 
675  // Set positions
676  casadi_int i;
677  i=0; for (auto* e : G.E) e->i = i++;
678  i=0; for (auto* v : G.V) v->i = i++;
679 
680  // Create edges using incidence sparsity
681  const casadi_int* colind = graph.colind();
682  const casadi_int* row = graph.row();
683 
684  // Loop over incidence columns
685  for (casadi_int c=0;c<graph.size2();++c) {
686  // Loop over nonzeros
687  for (casadi_int j=colind[c]; j<colind[c+1]; ++j) {
688  casadi_int r = row[j];
689  G.V[c]->eqs.push_back(G.E[r]); // Non-monotone
690  G.E[r]->vars.push_back(G.V[c]);
691  }
692  }
693 
694  // Process var_map
695  for (casadi_int i=0;i<var_map.size();++i) {
696  Variable* v = G.V[i];
697  casadi_int i_der = var_map[i];
698  if (i_der>=0) {
699  Variable* e = G.V[i_der];
700  v->der = e;
701  e->der_inv = v;
702  }
703  }
704 
705  for (auto* v : G.V) v->eqs0 = v->eqs;
706  for (auto* e : G.E) e->vars0 = e->vars;
707 
708  bool detect = false;
709  if (algorithm=="pantelides") {
710  detect = dae_struct_detect_pantelides(G, var_map, eq_map, max_iter);
711  } else {
712  // Clean up
713  for (auto* v : G.V) delete v;
714  for (auto* e : G.E) delete e;
715  casadi_error("Algorithm '" + algorithm + "' not recognized.");
716  }
717  if (!detect) {
718  for (auto* v : G.V) delete v;
719  for (auto* e : G.E) delete e;
720  casadi_error("Structural detection failed.");
721  }
722 
723  // Prepare outputs
724  var_map.resize(G.V.size());
725  eq_map.resize(G.E.size());
726 
727  int k = 0;
728  // Populate var_map
729  for (auto* v : G.V) {
730  if (v->der) {
731  var_map[v->i] = v->der->i;
732  } else {
733  var_map[v->i] = -1;
734  }
735  casadi_assert_dev(v->i==k);
736  k++;
737  }
738 
739  k = 0;
740  // Populate eq_map
741  for (auto* e : G.E) {
742  if (e->der) {
743  eq_map[e->i] = e->der->i;
744  } else {
745  eq_map[e->i] = -1;
746  }
747  casadi_assert_dev(e->i==k);
748  k++;
749  }
750 
751  // Clean up
752  for (auto* v : G.V) delete v;
753  for (auto* e : G.E) delete e;
754 
755  }
756 } // namespace IndexReduction
757 
758 
759 using namespace IndexReduction;
760 
761 template <class X>
762 std::map<std::string, X> add_defaults(const std::map<std::string, X>& in,
763  const std::vector<std::string>& keys) {
764  std::map<std::string, X> ret;
765  for (const std::string& k : keys) {
766  auto it = in.find(k);
767  if (it==in.end()) {
768  ret[k] = X(0, 1);
769  } else {
770  ret[k] = it->second;
771  }
772  }
773  return ret;
774 }
775 
776 
777 std::vector<casadi_int> get_orders(const std::vector<casadi_int>& map) {
778  std::vector<casadi_int> ret(map.size(), 0);
779  for (casadi_int i=0;i<ret.size();++i) {
780  if (map[i]>=0) ret[map[i]] = ret[i]+1;
781  }
782  return ret;
783 }
784 
785 std::vector<casadi_int> get_inverse(const std::vector<casadi_int>& map) {
786  std::vector<casadi_int> ret(map.size(), -1);
787  for (casadi_int i=0;i<ret.size();++i) {
788  if (map[i]>=0) ret[map[i]] = i;
789  }
790  return ret;
791 }
792 
793 std::vector<casadi_int> path(const std::vector<casadi_int>& map, casadi_int i_start) {
794  std::vector<casadi_int> ret;
795  casadi_int i = i_start;
796  while (true) {
797  casadi_int i_next = map[i];
798  if (i_next==-1) break;
799  i = i_next;
800  ret.push_back(i);
801  }
802  return ret;
803 }
804 
805 template <class X>
806 const std::map<std::string, X>
807 reduce_index_gen(const std::map<std::string, X>& dae, Dict& stats, const Dict& opts) {
808  double baumgarte_pole_ = 0;
809  std::string algorithm = "pantelides";
810  casadi_int max_iter = 500;
811  // Option parsing
812  for (auto&& op : opts) {
813  if (op.first=="baumgarte_pole") {
814  baumgarte_pole_ = op.second;
815  } else if (op.first=="algorithm") {
816  algorithm = op.second.to_string();
817  } else if (op.first=="max_iter") {
818  max_iter = op.second;
819  } else {
820  casadi_error("Unknown option '" + op.first + "'.");
821  }
822  }
823 
824  // Dae input sanitization
825 
826  for (const auto& e : dae) {
827  casadi_assert(e.second.is_column() && e.second.is_dense(),
828  "Dense column vector expected for key '" + e.first + "'. "
829  "Got " + e.second.dim(true) + " instead.");
830  }
831 
832  X x = get_from_dict(dae, "x", X(0, 1));
833  X ode = get_from_dict(dae, "ode", X(0, 1));
834  casadi_assert(x.numel()==ode.numel(),
835  "Size of explicit differential state vector (x: " + str(x.numel()) + ") must "
836  "match size of ode right-hand-side (ode: " + str(ode.numel()) + ").");
837  casadi_assert(x.size1()==0, "Explicit differential states not supported yet.");
838 
839  X x_impl = get_from_dict(dae, "x_impl", X(0, 1));
840  X dx_impl = get_from_dict(dae, "dx_impl", X(0, 1));
841  casadi_assert(x_impl.numel()==dx_impl.numel(),
842  "Size of implicit differential state vector (x_impl: " + str(x_impl.numel()) + ") must "
843  "match size of implicit differential state derivative vector (dx_impl: "
844  + str(dx_impl.numel()) + ").");
845 
846  X z = get_from_dict(dae, "z", X(0, 1));
847  X alg = get_from_dict(dae, "alg", X(0, 1));
848  casadi_assert(z.numel()+x_impl.numel()==alg.numel(),
849  "Size of algebraic state vector (z: " + str(z.numel()) + ") + "
850  "size of implicit states (x_impl: " + str(x_impl.numel()) + ") must "
851  "match size of algebraic equations (alg: " + str(alg.numel()) + ").");
852 
853  X t = get_from_dict(dae, "t", X::sym("t"));
854  casadi_assert(t.is_scalar(), "Time must be scalar. Got " + t.dim() + " instead.");
855 
856  X p = get_from_dict(dae, "p", X(0, 1));
857 
858  // Check that dx_impl are classified correctly
859  std::vector<bool> dep = X::which_depends(alg, dx_impl);
860 
861  if (!all(dep)) {
862  std::vector<X> dx_impl_split = vertsplit(dx_impl);
863  std::vector<std::string> prob;
864  for (casadi_int i=0;i<dep.size();++i) {
865  if (!dep[i]) prob.push_back(dx_impl_split[i].name());
866  }
867  casadi_error("Found dx_impl variables that do not appear in alg: " + join(prob, ",") +
868  ". They should be classified as z instead.");
869  }
870 
871 
872  bool normal_order = true;
873 
874  // Determine graph structure of problem for structural index reduction
875  X V;
876  if (normal_order) {
877  V = vertcat(x_impl, dx_impl, z);
878  } else {
879  V = vertcat(x_impl, z, dx_impl);
880  }
881 
882  Function temp("temp", {V, p, t}, {alg});
883  Sparsity G = temp.jac_sparsity(0, 0);
884 
885  // Populate var_map: a list that associates variables with their derivatives
886  int nx_impl = x_impl.numel();
887  int nz = z.numel();
888  std::vector<casadi_int> var_map(V.numel(), -1);
889  for (casadi_int i=0;i<nx_impl;++i) {
890  var_map[i] = i+nx_impl+(normal_order? 0: nz);
891  }
892 
893  // Allocate space for eq_map: a list that associates equations with their derivatives
894  std::vector<casadi_int> eq_map;
895 
896  // Modifies var_map and eq_map
897  dae_struct_detect(algorithm, G, var_map, eq_map, max_iter);
898 
899  // Variables should not be removed
900  casadi_assert_dev(var_map.size()>=2*nx_impl+nz);
901 
902  // List of scalarized variables
903  std::vector<X> var_ext = vertsplit(V);
904  // Allocate extra space for extension due to index reduction
905  var_ext.resize(var_map.size());
906 
907  // Populate extra space
908  for (casadi_int i=0;i<var_map.size();++i) {
909  casadi_int i_der = var_map[i];
910  // Derivate index is in the extra space?
911  if (i_der>=V.numel())
912  // Consstruct a new symbol with derived name
913  var_ext[i_der] = X::sym("d"+var_ext[i].name());
914  }
915 
916  // Prepare arguments for jtimes
917  // In essence, this is like to the novel x_impl and dx_impl,
918  // but a subset may be promoted to x (explicit diff. state)
919  std::vector<X> xs; xs.reserve(var_map.size());
920  std::vector<X> dxs; dxs.reserve(var_map.size());
921 
922  for (casadi_int i=0;i<var_map.size();++i) {
923  casadi_int i_der = var_map[i];
924  if (i_der>=0) {
925  xs.push_back(var_ext[i]);
926  dxs.push_back(var_ext[i_der]);
927  }
928  }
929 
930  // Stack to be used as jtimes arguments
931  X x_cat = vertcat(xs);
932  X dx_cat = vertcat(dxs);
933 
934  // Don't forget to differentiate time itself
935  X j1 = vertcat(x_cat, t);
936  X j2 = vertcat(dx_cat, 1);
937 
938  // List of scalarized equations
939  std::vector<X> eq_ext = vertsplit(alg);
940  // Allocate extra space for extension due to index reduction
941  eq_ext.resize(eq_map.size());
942 
943  // For each equation, figure out how many times it was differentiated (=order)
944  std::vector<casadi_int> order = get_orders(eq_map);
945  casadi_int max_order = *std::max_element(order.begin(), order.end());
946 
947  // We lump together all relevant equations before doing differentiation in a target group,
948  // in order to make use of shared subexpressions.
949  // order -> all equations that give rise to an equation of this order
950  std::map<casadi_int, std::vector<casadi_int> > targets;
951  for (casadi_int i=0;i<eq_map.size();++i) {
952  if (eq_map[i]>=0) {
953  targets[order[eq_map[i]]].push_back(i);
954  }
955  }
956 
957  // Non-active equations will be dropped from the final set
958  std::vector<bool> active(eq_map.size(), true);
959 
960  // Equations that get dropped, but should still hold for initial guess
961  std::vector<X> dropped_constraints;
962 
963  // Loop over equation elements in increasing order
964  for (casadi_int ord=1;ord<=max_order;++ord) {
965  const std::vector<casadi_int> & target = targets[ord];
966 
967  // Remember to-be-dropped equations
968  for (casadi_int i : target) dropped_constraints.push_back(eq_ext[i]);
969 
970  // Gather target's equations
971  X block = vertcat(vector_slice(eq_ext, target));
972  // Perform differentiation
973  std::vector<X> dblocks = vertsplit(jtimes(block, j1, j2, false));
974  // Scatter computed derivatives into target resultant equation slots
975  for (casadi_int i=0;i<target.size();++i) {
976  casadi_int t = target[i];
977  eq_ext[eq_map[t]] = dblocks[i];
978  active[t] = false;
979  }
980  }
981 
982  std::vector<casadi_int> eq_map_inverse = get_inverse(eq_map);
983 
984  // Construct the new list of algebraic equations
985  // In theory, just collect all active members of eq_ext
986  // In practice, we add some Baumgarte stabilization to avoid constraint drifting
987  std::vector<X> alg_new;
988  for (casadi_int i=0;i<eq_map.size();++i) {
989  if (active[i]) {
990 
991  // Start from the pure equation
992  X new_eq = eq_ext[i];
993 
994  // Construct coefficients of the polynomial (x-gamma)^order
995  Polynomial p(1);
996  for (casadi_int k=0;k<order[i];++k) {
997  p *= Polynomial(1, -baumgarte_pole_);
998  }
999 
1000  const std::vector<double>& coeff = p.coeff();
1001 
1002  // Find indices of originating equations of equation i
1003  // e.g. if eq_i = der(eq_j), eq_j = der(eq_k), obtain [j,k]
1004  std::vector<casadi_int> gen_i = path(eq_map_inverse, i);
1005 
1006  // Add inner product of coefficients with originating equations of new_eq
1007  for (casadi_int k=0;k<gen_i.size();++k) {
1008  new_eq += coeff[k+1]*eq_ext[gen_i[k]];
1009  }
1010 
1011  // Ready to add
1012  alg_new.push_back(new_eq);
1013  }
1014  }
1015 
1016  std::vector<casadi_int> var_order = get_orders(var_map);
1017  std::vector<casadi_int> var_map_inverse = get_inverse(var_map);
1018 
1019 
1020  // Which implicit states become explicit pure integrators?
1021  std::vector<casadi_int> impl_to_expl;
1022  for (casadi_int i=0;i<var_map.size();++i) {
1023  if (var_order[i]>=2) {
1024  // Find indices of originating variables of variable i
1025  // e.g. if var_i = der(var_j), var_j = der(var_k), obtain [j,k]
1026  std::vector<casadi_int> gen_i = path(var_map_inverse, i);
1027 
1028  // Anything too deep should become explicit integrator
1029  if (gen_i.size()>1)
1030  impl_to_expl.insert(impl_to_expl.end(), gen_i.begin()+1, gen_i.end());
1031 
1032  }
1033  }
1034 
1035  // Take complement
1036  std::vector<casadi_int> impl_to_expl_compl = complement(impl_to_expl, xs.size());
1037  std::map<std::string, X> dae_result;
1038 
1039  dae_result["x"] = vertcat(vector_slice(xs, impl_to_expl));
1040  dae_result["ode"] = vertcat(vector_slice(dxs, impl_to_expl));
1041 
1042  dae_result["x_impl"] = vertcat(vector_slice(xs, impl_to_expl_compl));
1043  dae_result["dx_impl"] = vertcat(vector_slice(dxs, impl_to_expl_compl));
1044 
1045  dae_result["t"] = t;
1046  dae_result["p"] = p;
1047 
1048  // Which algebraic variables were not promoted to differential states?
1049  std::vector<X> z_orig = vertsplit(z);
1050  std::vector<X> z_new;
1051  for (casadi_int i=0;i<var_map.size();++i) {
1052  // Algebraic
1053  if (var_order[i]==0 && var_map[i]==-1) {
1054  z_new.push_back(z_orig.at(i-(normal_order?2:1)*nx_impl));
1055  }
1056  }
1057  dae_result["z"] = vertcat(z_new);
1058  dae_result["alg"] = vertcat(alg_new);
1059 
1060  dae_result["I"] = vertcat(dropped_constraints);
1061 
1062  stats["index"] = max_order+1;
1063 
1064  return dae_result;
1065 }
1066 
1067 MXDict dae_reduce_index(const MXDict& dae, Dict& stats, const Dict& opts) {
1068  return reduce_index_gen(dae, stats, opts);
1069 }
1070 
1071 SXDict dae_reduce_index(const SXDict& dae, Dict& stats, const Dict& opts) {
1072  return reduce_index_gen(dae, stats, opts);
1073 }
1074 
1075 template<class X>
1076 std::map<std::string, X> map_semi_expl(const std::map<std::string, X>& dae,
1077  const std::map<std::string, X>& dae_red,
1078  Function& state_to_orig, Function& phi) {
1079 
1080  std::map<std::string, X> dae_se;
1081  dae_se["x"] = vertcat(dae_red.at("x"), dae_red.at("x_impl"));
1082  dae_se["z"] = vertcat(dae_red.at("dx_impl"), dae_red.at("z"));
1083  dae_se["ode"] = vertcat(dae_red.at("ode"), dae_red.at("dx_impl"));
1084  dae_se["alg"] = dae_red.at("alg");
1085  dae_se["t"] = dae_red.at("t");
1086  dae_se["p"] = dae_red.at("p");
1087 
1088  X x_impl = get_from_dict(dae, "x_impl", X(0, 1));
1089  X dx_impl = get_from_dict(dae, "dx_impl", X(0, 1));
1090  X z = get_from_dict(dae, "z", X(0, 1));
1091 
1092  Sparsity var_map_sp = jacobian(vertcat(dae_red.at("x"), dae_red.at("x_impl"),
1093  dae_red.at("dx_impl"), dae_red.at("z")), vertcat(x_impl, dx_impl, z)).sparsity();
1094  DM var_map(var_map_sp, 1.0);
1095 
1096  state_to_orig = Function("state_to_orig",
1097  {dae_se["x"], dae_se["z"]},
1098  {x_impl, dx_impl, z},
1099  {"xf", "zf"},
1100  {"x_impl", "dx_impl", "z"});
1101 
1102  phi = Function("phi",
1103  {dae_se["x"], dae_se["z"], dae_se["p"], dae_se["t"]},
1104  {dae_red.at("I")},
1105  {"x", "z", "p", "t"},
1106  {"I"});
1107 
1108  return dae_se;
1109 }
1110 
1111 template<class X>
1112 Function init_gen(const std::map<std::string, X>& dae,
1113  const std::map<std::string, X>& dae_red,
1114  const std::string& init_solver, const DMDict& init_strength, const Dict& init_solver_options) {
1115 
1116  X x_impl = get_from_dict(dae, "x_impl", X(0, 1));
1117  X dx_impl = get_from_dict(dae, "dx_impl", X(0, 1));
1118  X z = get_from_dict(dae, "z", X(0, 1));
1119  X p_orig = get_from_dict(dae, "p", X(0, 1));
1120 
1121  Sparsity var_map_sp = jacobian(
1122  vertcat(dae_red.at("x"), dae_red.at("x_impl"), dae_red.at("dx_impl"), dae_red.at("z")),
1123  vertcat(x_impl, dx_impl, z)).sparsity();
1124  DM var_map(var_map_sp, 1.0);
1125 
1126  // NLP to obtain consistent initial guesses
1127  std::map<std::string, X> nlp;
1128 
1129  MX pmx = MX::sym("pmx", p_orig.sparsity());
1130  MX tmx = MX::sym("t");
1131 
1132  X init_x_impl_orig = X::sym("x_impl_init", x_impl.sparsity());
1133  X init_dx_impl_orig = X::sym("dx_impl_init", dx_impl.sparsity());
1134  X init_z_orig = X::sym("z_init", z.sparsity());
1135 
1136  MX init_x_impl_orig_mx = MX::sym("x_impl_init", x_impl.sparsity());
1137  MX init_dx_impl_orig_mx = MX::sym("dx_impl_init", dx_impl.sparsity());
1138  MX init_z_orig_mx = MX::sym("z_init", z.sparsity());
1139 
1140  X init_orig = vertcat(init_x_impl_orig, init_dx_impl_orig, init_z_orig);
1141  MX init_orig_mx = vertcat(init_x_impl_orig_mx, init_dx_impl_orig_mx, init_z_orig_mx);
1142 
1143  X init_sym_red = mtimes(X(var_map), init_orig);
1144  nlp["p"] = vertcat(init_orig, dae_red.at("p"), dae_red.at("t"));
1145  MX p = vertcat(init_orig_mx, pmx, tmx);
1146 
1147  // Dae input sanitization
1148 
1149  for (const auto& e : init_strength) {
1150  casadi_assert(e.second.is_column() && e.second.is_dense(),
1151  "Dense column vector expected for key '" + e.first + "' of init_strength. "
1152  "Got " + e.second.dim(true) + " instead.");
1153  }
1154 
1155  DM init_strength_x_impl = get_from_dict(init_strength, "x_impl", DM::zeros(x_impl.numel(), 1));
1156  casadi_assert(x_impl.numel()==init_strength_x_impl.numel(),
1157  "init_strength 'x_impl' entry should have length " + str(x_impl.numel()) + ". "
1158  "Got length " + str(init_strength_x_impl.numel()) + " instead.");
1159 
1160  DM init_strength_dx_impl = get_from_dict(init_strength, "dx_impl",
1161  DM::zeros(dx_impl.numel(), 1));
1162  casadi_assert(dx_impl.numel()==init_strength_dx_impl.numel(),
1163  "init_strength 'dx_impl' entry should have length " + str(dx_impl.numel()) + ". "
1164  "Got length " + str(init_strength_dx_impl.numel()) + " instead.");
1165 
1166  DM init_strength_z = get_from_dict(init_strength, "z", DM::zeros(z.numel(), 1));
1167  casadi_assert(z.numel()==init_strength_z.numel(),
1168  "init_strength 'z' entry should have length " + str(z.numel()) + ". "
1169  "Got length " + str(init_strength_z.numel()) + " instead.");
1170 
1171  DM init_strength_cat = vertcat(init_strength_x_impl, init_strength_dx_impl, init_strength_z);
1172 
1173  DM weights = mtimes(var_map, init_strength_cat);
1174  // Should be written more cleanly
1175  std::vector<casadi_int> ind = sparsify(weights>0).sparsity().T().get_col();
1176 
1177  std::vector<X> x_parts = {dae_red.at("x"), dae_red.at("x_impl"),
1178  dae_red.at("dx_impl"), dae_red.at("z")};
1179 
1180  nlp["x"] = vertcat(x_parts);
1181  nlp["g"] = vertcat(dae_red.at("I"), dae_red.at("alg"));
1182  nlp["f"] = sumsqr((nlp["x"](ind)-init_sym_red(ind))*X(weights(ind)));
1183 
1184 
1185  MX x0 = mtimes(var_map, init_orig_mx);
1186  MX lbx = -MX::inf(nlp.at("x").numel());
1187  MX ubx = MX::inf(lbx.size());
1188 
1189  // Should be written more cleanly
1190  ind = sparsify(mtimes(var_map, init_strength_cat)==-1).sparsity().T().get_col();
1191  lbx(ind) = ubx(ind) = x0(ind);
1192 
1193  Function solver = nlpsol("init_solver", init_solver, nlp, init_solver_options);
1194  MXDict res = solver(MXDict{{"x0", x0}, {"lbg", 0}, {"ubg", 0},
1195  {"lbx", lbx}, {"ubx", ubx}, {"p", p}});
1196 
1197  std::vector<casadi_int> x_parts_offsets = {0};
1198  for (casadi_int i=0;i<x_parts.size();i+=2) {
1199  x_parts_offsets.push_back(x_parts_offsets.back() + x_parts[i].numel() + x_parts[i+1].numel());
1200  }
1201 
1202  std::vector<MX> parts = vertsplit(res["x"], x_parts_offsets);
1203 
1204  return Function("init_gen",
1205  {init_x_impl_orig_mx, init_dx_impl_orig_mx, init_z_orig_mx, pmx, tmx},
1206  parts,
1207  {"x_impl", "dx_impl", "z", "p", "t"},
1208  {"x0", "z0"});
1209 }
1210 
1211 MXDict dae_map_semi_expl(const MXDict& dae, const MXDict& dae_red,
1212  Function& state_to_orig, Function& phi) {
1213  return map_semi_expl(dae, dae_red, state_to_orig, phi);
1214 }
1215 
1216 SXDict dae_map_semi_expl(const SXDict& dae, const SXDict& dae_red,
1217  Function& state_to_orig, Function& phi) {
1218  return map_semi_expl(dae, dae_red, state_to_orig, phi);
1219 }
1220 
1221 Function dae_init_gen(const MXDict& dae, const MXDict& dae_red,
1222  const std::string& init_solver, const DMDict& init_strength, const Dict& init_solver_options) {
1223  return init_gen(dae, dae_red, init_solver, init_strength, init_solver_options);
1224 }
1225 
1226 Function dae_init_gen(const SXDict& dae, const SXDict& dae_red,
1227  const std::string& init_solver, const DMDict& init_strength, const Dict& init_solver_options) {
1228  return init_gen(dae, dae_red, init_solver, init_strength, init_solver_options);
1229 }
1230 
1231 } // namespace casadi
Function object.
Definition: function.hpp:60
const Sparsity & sparsity_in(casadi_int ind) const
Get sparsity of a given input.
Definition: function.cpp:1015
casadi_int n_out() const
Get the number of function outputs.
Definition: function.cpp:823
casadi_int n_in() const
Get the number of function inputs.
Definition: function.cpp:819
casadi_int numel() const
Get the number of elements.
std::pair< casadi_int, casadi_int > size() const
Get the shape.
casadi_int size1() const
Get the first dimension (i.e. number of rows)
static MX sym(const std::string &name, casadi_int nrow=1, casadi_int ncol=1)
Create an nrow-by-ncol symbolic primitive.
static MatType zeros(casadi_int nrow=1, casadi_int ncol=1)
Create a dense matrix or a matrix with specified sparsity with all entries zero.
MX - Matrix expression.
Definition: mx.hpp:92
const Sparsity & sparsity() const
Get the sparsity pattern.
Definition: mx.cpp:592
static MX inf(const Sparsity &sp)
create a matrix with all inf
Definition: mx.cpp:564
Sparse matrix class. SX and DM are specializations.
Definition: matrix_decl.hpp:99
Helper class for differentiating and integrating polynomials.
Definition: polynomial.hpp:39
Polynomial anti_derivative() const
Create a new polynomial for the anti-derivative (primitive function)
Definition: polynomial.cpp:149
Polynomial derivative() const
Create a new polynomial for the derivative.
Definition: polynomial.cpp:141
const std::vector< double > & coeff() const
Coefficients of the polynomial.
Definition: polynomial.hpp:71
Class representing a Slice.
Definition: slice.hpp:48
General sparsity class.
Definition: sparsity.hpp:106
casadi_int numel() const
The total number of elements, including structural zeros, i.e. size2()*size1()
Definition: sparsity.cpp:132
casadi_int size1() const
Get the number of rows.
Definition: sparsity.cpp:124
casadi_int size2() const
Get the number of columns.
Definition: sparsity.cpp:128
const casadi_int * row() const
Get a reference to row-vector,.
Definition: sparsity.cpp:164
std::pair< casadi_int, casadi_int > size() const
Get the shape.
Definition: sparsity.cpp:152
static Sparsity scalar(bool dense_scalar=true)
Create a scalar sparsity pattern *.
Definition: sparsity.hpp:153
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
Definition: sparsity.cpp:168
Function integrator(const std::string &name, const std::string &solver, const SXDict &dae, const Dict &opts)
Definition: integrator.cpp:134
Function nlpsol(const std::string &name, const std::string &solver, const SXDict &nlp, const Dict &opts)
Definition: nlpsol.cpp:118
Function rootfinder(const std::string &name, const std::string &solver, const SXDict &rfp, const Dict &opts)
Definition: rootfinder.cpp:111
struct EquationStruct Equation
bool dae_struct_detect_pantelides(Graph &G, std::vector< casadi_int > &var_map, std::vector< casadi_int > &eq_map, casadi_int max_iter)
Perform Pantelides algorithm for DAE index reduction.
void add_variable(Equation *e, Variable *v)
void dae_struct_detect(const std::string &algorithm, const Sparsity &graph, std::vector< casadi_int > &var_map, std::vector< casadi_int > &eq_map, casadi_int max_iter)
bool dfs_match_pantelides(Equation *i)
struct GraphStruct Graph
struct VariableStruct Variable
void graph_add_der(Graph &G, Variable *v)
The casadi namespace.
Definition: archiver.cpp:28
const long double legendre_points8[]
std::map< std::string, MX > MXDict
Definition: mx.hpp:1009
void collocation_coeff(const std::vector< double > &tau, DM &C, DM &D, DM &B)
Obtain collocation interpolating matrices.
const long double radau_points9[]
T get_from_dict(const std::map< std::string, T > &d, const std::string &key, const T &default_value)
Function dae_init_gen(const MXDict &dae, const MXDict &dae_red, const std::string &init_solver, const DMDict &init_strength, const Dict &init_solver_options)
Obtain a generator Function for producing consistent initial guesses of a reduced DAE.
std::string join(const std::vector< std::string > &l, const std::string &delim)
Function simpleRK(Function f, casadi_int N, casadi_int order)
Construct an explicit Runge-Kutta integrator.
const long double legendre_points2[]
const long double legendre_points4[]
const long double radau_points3[]
std::vector< RealT > collocation_pointsGen(casadi_int order, const std::string &scheme)
const long double legendre_points6[]
std::map< std::string, X > map_semi_expl(const std::map< std::string, X > &dae, const std::map< std::string, X > &dae_red, Function &state_to_orig, Function &phi)
const long double radau_points1[]
const long double legendre_points9[]
std::map< std::string, X > add_defaults(const std::map< std::string, X > &in, const std::vector< std::string > &keys)
const long double radau_points2[]
const long double radau_points6[]
const long double legendre_points5[]
const long double * legendre_points[]
Function init_gen(const std::map< std::string, X > &dae, const std::map< std::string, X > &dae_red, const std::string &init_solver, const DMDict &init_strength, const Dict &init_solver_options)
const long double radau_points7[]
std::map< std::string, SX > SXDict
Definition: sx_fwd.hpp:40
MXDict dae_map_semi_expl(const MXDict &dae, const MXDict &dae_red, Function &state_to_orig, Function &phi)
Turn a reduced DAE into a semi explicit form suitable for CasADi integrator.
void collocation_interpolators(const std::vector< double > &tau, std::vector< std::vector< double > > &C, std::vector< double > &D)
Obtain collocation interpolating matrices.
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
const long double radau_points5[]
const long double radau_points8[]
MXDict dae_reduce_index(const MXDict &dae, Dict &stats, const Dict &opts)
Reduce index.
std::vector< casadi_int > get_inverse(const std::vector< casadi_int > &map)
const long double legendre_points1[]
std::vector< casadi_int > invert_lookup(const std::vector< casadi_int > &lookup)
Function simpleIRK(Function f, casadi_int N, casadi_int order, const std::string &scheme, const std::string &solver, const Dict &solver_options)
Construct an implicit Runge-Kutta integrator using a collocation scheme.
const long double radau_points4[]
std::vector< double > collocation_points(casadi_int order, const std::string &scheme)
Obtain collocation points of specific order and scheme.
const std::map< std::string, X > reduce_index_gen(const std::map< std::string, X > &dae, Dict &stats, const Dict &opts)
bool all(const std::vector< bool > &v)
Check if all arguments are true.
Definition: casadi_misc.cpp:77
std::vector< casadi_int > get_orders(const std::vector< casadi_int > &map)
Function simpleIntegrator(Function f, const std::string &plugin, const Dict &plugin_options)
Simplified wrapper for the Integrator class.
const long double legendre_points3[]
std::vector< T > vector_slice(const std::vector< T > &v, const std::vector< casadi_int > &i)
Slicing vector.
Matrix< double > DM
Definition: dm_fwd.hpp:33
const long double * radau_points[]
std::vector< casadi_int > complement(const std::vector< casadi_int > &v, casadi_int size)
Returns the list of all i in [0, size[ not found in supplied list.
std::vector< long double > collocation_pointsL(casadi_int order, const std::string &scheme)
Obtain collocation points of specific order and scheme.
std::vector< casadi_int > path(const std::vector< casadi_int > &map, casadi_int i_start)
const long double legendre_points7[]
std::map< std::string, DM > DMDict
Definition: dm_fwd.hpp:36