slicot_dple.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 "slicot_dple.hpp"
27 #include "slicot_layer.hpp"
28 #include "slicot_la.hpp"
29 
30 #include "../../core/casadi_misc.hpp"
31 #include "../../core/mx_function.hpp"
32 #include "../../core/sx_function.hpp"
33 
34 #include <cassert>
35 #include <ctime>
36 #include <numeric>
37 
38 namespace casadi {
39 
40  extern "C"
41  int CASADI_DPLE_SLICOT_EXPORT
42  casadi_register_dple_slicot(Dple::Plugin* plugin) {
43  plugin->creator = SlicotDple::creator;
44  plugin->name = "slicot";
45  plugin->doc = SlicotDple::meta_doc.c_str();
46  plugin->version = CASADI_VERSION;
47  plugin->options = &SlicotDple::options_;
48  return 0;
49  }
50 
51  extern "C"
52  void CASADI_DPLE_SLICOT_EXPORT casadi_load_dple_slicot() {
54  }
55 
56  const Options SlicotDple::options_
57  = {{&Dple::options_},
58  {{"linear_solver",
59  {OT_STRING,
60  "User-defined linear solver class. Needed for sensitivities."}},
61  {"linear_solver_options",
62  {OT_DICT,
63  "Options to be passed to the linear solver."}},
64  {"psd_num_zero",
65  {OT_DOUBLE,
66  "Numerical zero used in Periodic Schur decomposition with slicot."
67  "This option is needed when your systems has Floquet multipliers"
68  "zero or close to zero"}}
69  }
70  };
71 
72 
73  SlicotDple::SlicotDple(const std::string& name, const SpDict & st) : Dple(name, st) {
74 
75  }
76 
78  clear_mem();
79  }
80 
81  bool SlicotDple::has_loaded_ = false;
82 
83  void SlicotDple::init(const Dict& opts) {
84 
85  if (!has_loaded_) {
86  has_loaded_ = true;
87  casadi_warning("Loaded plugin with GPL license.");
88  }
89 
90  Dple::init(opts);
91 
92  linear_solver_ = "csparse";
93  psd_num_zero_ = 1e-12;
94 
95  // Read user options
96  for (auto&& op : opts) {
97  if (op.first=="linear_solver") {
98  linear_solver_ = op.second.as_string();
99  } else if (op.first=="linear_solver_options") {
100  linear_solver_options_ = op.second;
101  } else if (op.first=="psd_num_zero") {
102  psd_num_zero_ = op.second;
103  }
104  }
105 
106  casadi_assert(!pos_def_,
107  "pos_def option set to True: Solver only handles the indefinite case.");
108  casadi_assert(const_dim_,
109  "const_dim option set to False: Solver only handles the True case.");
110 
111  //for (casadi_int k=0;k<K_;k++) {
112  // casadi_assert(A_[k].isdense(), "Solver requires arguments to be dense.");
113  // casadi_assert(V_[k].isdense(), "Solver requires arguments to be dense.");
114  //}
115 
116 
117  n_ = V_.colind()[1];
118 
119  alloc_w(n_*n_*K_, true); // VZ_
120  alloc_w(n_*n_*K_, true); // T_
121  alloc_w(n_*n_*K_, true); // Z_
122  alloc_w(n_*n_*K_, true); // X_
123 
124  alloc_w(n_*n_*K_, true); // Xbar_
125 
126  alloc_w(n_*n_*K_, true); // nnKa_
127  alloc_w(n_*n_*K_, true); // nnKb_
128 
129  alloc_w(n_, true); // eig_real_
130  alloc_w(n_, true); // eig_imag_
131 
132  alloc_w(2*2*n_*K_, true); // F_
133  alloc_w(2*2*K_, true); // FF_
134 
135  // There can be at most n partitions
136  alloc_iw(n_+1, true); // partition_
137 
138  alloc_w(std::max(n_+K_-2, 4*n_)+(n_-1)*K_+2*n_); // dwork_
139  alloc_w(n_*K_);
140  alloc_iw(n_*K_);
141  alloc_w(4*K_*4+4*K_, true); // A_
142  alloc_w(4*K_, true); // B_
143  }
144 
145 
146  void SlicotDple::set_work(void* mem, const double**& arg, double**& res,
147  casadi_int*& iw, double*& w) const {
148  auto m = static_cast<SlicotDpleMemory*>(mem);
149 
150  // Set work in base classes
151  Dple::set_work(mem, arg, res, iw, w);
152 
153  // Lagrange multipliers of the NLP
154  m->VZ = w; w += n_*n_*K_;
155  m->T = w; w += n_*n_*K_;
156  m->Z = w; w += n_*n_*K_;
157  m->X = w; w += n_*n_*K_;
158 
159  m->Xbar = w; w += n_*n_*K_;
160  m->nnKa = w; w += n_*n_*K_;
161  m->nnKb = w; w += n_*n_*K_;
162 
163  m->eig_real = w; w += n_;
164  m->eig_imag = w; w += n_;
165 
166  m->F = w; w += 2*2*n_*K_;
167  m->FF = w; w += 2*2*K_;
168 
169  m->A = w; w += 4*K_*4+4*K_;
170  m->B = w; w += 4*K_;
171  m->dwork = w;
172  m->wruntime = w;
173  m->partition = iw; iw+= n_+1;
174  m->iwruntime = iw;
175  }
176 
177 
179  int SlicotDple::init_mem(void* mem) const {
180  if (Dple::init_mem(mem)) return 1;
181  auto m = static_cast<SlicotDpleMemory*>(mem);
182 
183  // Construct linear solvers for low-order Discrete Periodic Sylvester Equations
184  // IX00
185  // 0IX0
186  // 00IX
187  // X00I
188  // Special case K=1
189  // I+X
190  // Solver complexity: K
191  m->dpse_solvers.resize(3);
192  for (casadi_int i=0;i<3;++i) {
193  casadi_int np = std::pow(2, i); // NOLINT
194 
195  Sparsity sp = Sparsity::dense(np, np);
196  if (K_>1)
197  sp = kron(Sparsity::band(K_, -1)+Sparsity::band(K_, K_-1), sp) + Sparsity::diag(np*K_);
198 
199  m->dpse_solvers[i].reserve(n_*(n_+1)/2);
200  for (casadi_int k=0;k<n_*(n_+1)/2;++k) {
201  m->dpse_solvers[i].push_back(Linsol("solver", linear_solver_, sp));
202  }
203  }
204  return 0;
205  }
206 
208  inline casadi_int SlicotDple::partindex(const SlicotDpleMemory* m,
209  casadi_int i, casadi_int j, casadi_int k, casadi_int r, casadi_int c) const {
210  return k*n_*n_+(m->partition[i]+r)*n_ + m->partition[j]+c;
211  }
213 
214  int SlicotDple::eval(const double** arg, double** res,
215  casadi_int* iw, double* w, void* mem) const {
216  auto m = static_cast<SlicotDpleMemory*>(mem);
217 
218  setup(mem, arg, res, iw, w);
219 
220  // Transpose operation (after #554)
221  casadi_trans(arg[DPLE_A], A_, m->X, A_, m->iwruntime);
222 
223  slicot_periodic_schur(n_, K_, m->X, m->T, m->Z,
224  m->dwork, m->eig_real, m->eig_imag, psd_num_zero_);
225 
226  if (error_unstable_) {
227  for (casadi_int i=0;i<n_;++i) {
228  double modulus = sqrt(m->eig_real[i]*m->eig_real[i]+m->eig_imag[i]*m->eig_imag[i]);
229  casadi_assert(modulus+eps_unstable_ <= 1,
230  "SlicotDple: system is unstable."
231  "Found an eigenvalue " + str(m->eig_real[i]) + " + " +
232  str(m->eig_imag[i]) + "j, with modulus " + str(modulus) +
233  " (corresponding eps= " + str(1-modulus) + ").\n" +
234  "Use options and 'error_unstable' and 'eps_unstable' to influence this message.");
235  }
236  }
237 
238  // Find a block partition of the T hessenberg form
239  casadi_int* p = m->partition;
240  p[0] = 0;
241  casadi_int p_i = 1;
242  casadi_int i = 0, j = 0;
243  while (j<n_) {
244  while (i<n_ && m->T[i+n_*j]!=0) i+=1;
245  j = i;
246  p[p_i++] = i;
247  i += 1;
248  }
249 
250  // Main loops to loop over blocks of the block-upper triangular A
251  // Outer main loop
252  for (casadi_int l=0;l<p_i-1;++l) {
253 
254  // Inner main loop
255  for (casadi_int r=0;r<l+1;++r) {
256 
257  casadi_int n1 = p[r+1]-p[r];
258  casadi_int n2 = p[l+1]-p[l];
259  casadi_int np = n1*n2;
260 
261  casadi_assert_dev(n1-1+n2-1>=0);
262 
263  Linsol & solver = m->dpse_solvers[n1-1+n2-1][((l+1)*l)/2+r];
264 
265  // ********** START ***************
266  double * A = m->A;
267  std::fill(A, A+4*K_*4+4*K_, 1);
268  double * T = m->T;
269 
270  if (K_==1) { // Special case if K==1
271  dense_kron_stride(np, n2, T+p[r]*n_ + p[r], T+p[l]*n_ + p[l], A, n_, n_, np);
272  for (casadi_int ll=0;ll<np;++ll)
273  A[ll*np+ll]+= 1;
274  } else { // Other cases
275  for (casadi_int k=0;k<K_-1;++k) {
276  dense_kron_stride(np, n2,
277  T+p[r]*n_ + p[r], T+p[l]*n_ + p[l], A+np*(np+1)*((k+1)%K_), n_, n_, np+1);
278  T+= n_*n_;
279  }
280 
281  dense_kron_stride(np, n2, T+p[r]*n_ + p[r], T+p[l]*n_ + p[l], A+1, n_, n_, np+1);
282  }
283  // ********** STOP ***************
284  // Solve Discrete Periodic Sylvester Equation Solver
285 
286  solver.sfact(m->A);
287  solver.nfact(m->A);
288 
289  }
290  }
291 
292  for (casadi_int d=0;d<nrhs_;++d) {
293 
294  // V = blocks([mul([sZ[k].T, V[k], sZ[k]]) for k in range(p)])
295  for (casadi_int k=0;k<K_;++k) { // K
296  double * nnKa = m->nnKa+k*n_*n_, * nnKb = m->nnKb+k*n_*n_;
297  // n^2 K
298 
299  std::fill(nnKa, nnKa+n_*n_, 0);
300  // nnKa[k] <- V[k]*Z[k+1]
301  // n^3 K
302  dense_mul_nt(n_, n_, n_, arg[DPLE_V]+d*n_*n_*K_ + k*n_*n_, m->Z+((k+1) % K_)*n_*n_, nnKa);
303  std::fill(nnKb, nnKb+n_*n_, 0);
304  // nnKb[k] <- Z[k+1]'*V[k]*Z[k+1]
305  dense_mul_nn(n_, n_, n_, m->Z + ((k+1) % K_)*n_*n_, nnKa, nnKb);
306  }
307 
308  std::fill(m->X, m->X+n_*n_*K_, 0);
309 
310  // Main loops to loop over blocks of the block-upper triangular A
311  // Outer main loop
312  for (casadi_int l=0;l<p_i-1;++l) { // n
313  casadi_int n2 = p[l+1]-p[l];
314 
315  // F serves an an accumulator for intermediate summation results
316  // n^2 K
317  std::fill(m->F, m->F+2*2*n_*K_, 0);
318 
319  //for i in range(l):
320  // F[i] = [sum(mul(X[i][j][k], A[l][j][k].T) for j in range(l)) for k in range(p) ]
321  for (casadi_int k=0;k<K_;++k) {
322  double *X = m->X+k*n_*n_, *T = m->T+ k*n_*n_;
323  for (casadi_int i=0;i<l;++i) // n^2
324  for (casadi_int j=0;j<l;++j) // n^3
325  dense_mul_nt_stride(p[i+1]-p[i], n2, p[j+1]-p[j],
326  X+ p[i]*n_+ p[j], T+p[l]*n_+ p[j], m->F + k*4*n_+4*i, n_, n_, 2);
327  }
328 
329  // Inner main loop
330  for (casadi_int r=0;r<l+1;++r) { // n^2
331  casadi_int n1 = p[r+1]-p[r];
332  casadi_int np = n1*n2;
333 
334  // F[r] = [sum(mul(X[r][j][k], A[l][j][k].T) for j in range(l)) for k in range(p) ]
335  if (r==l) {
336  for (casadi_int k=0;k<K_;++k) { // n^3 K
337  double *X = m->X+k*n_*n_, *T = m->T+ k*n_*n_;
338  for (casadi_int j=0;j<l;++j) // n^3
339  dense_mul_nt_stride(n1, n2, p[j+1]-p[j],
340  X+ p[r]*n_+ p[j], T+p[l]*n_+ p[j], m->F + k*4*n_+4*r, n_, n_, 2);
341  }
342  }
343 
344  // FF = [sum(mul(A[r][i][k], X[i][l][k]) for i in range(r)) for k in range(p)]
345  // Each entry of FF is na1-by-na2
346  // n^2 K
347  std::fill(m->FF, m->FF+2*2*K_, 0);
348  for (casadi_int k=0;k<K_;++k) { // n^3 K
349  double *X = m->X+k*n_*n_, *T = m->T+ k*n_*n_;
350  for (casadi_int i=0;i<r;++i) // n^3
351  dense_mul_nn_stride(n1, n2, p[i+1]-p[i],
352  T+p[r]*n_ + p[i], X+p[i]*n_ + p[l], m->FF+k*4, n_, n_, 2);
353  }
354 
355  Linsol & solver = m->dpse_solvers[n1-1+n2-1][((l+1)*l)/2+r];
356 
357  // M <- V
358  for (casadi_int k=0;k<K_;++k)
359  dense_copy_stride(n1, n2, m->nnKb+ k*n_*n_+ p[r]*n_ + p[l], m->B+np*((k+1)%K_), n_, n2);
360 
361  // M+= [sum(mul(A[r][i][k], F[i][k]) for i in range(r+1)) for k in rang(p)]
362  for (casadi_int k=0;k<K_;++k) { // n^3 K
363  double *B = m->B + np*((k+1)%K_), *T = m->T+ k*n_*n_;
364  for (casadi_int i=0;i<r+1;++i) // n^3
365  dense_mul_nn_stride(n1, n2, p[i+1]-p[i],
366  T+p[r]*n_+ p[i], m->F+k*4*n_+4*i, B, n_, 2, n2);
367  }
368 
369  // M+= [mul(FF[k], A[l][l][k].T) for k in rang(p)]
370  for (casadi_int k=0;k<K_;++k) // n^2 K
371  dense_mul_nt_stride(n1, n2, n2,
372  m->FF+k*4, m->T + k*n_*n_+p[l]*n_+ p[l], m->B+np*((k+1)%K_), 2, n_, n2);
373 
374  // Critical observation: Prepare step is not needed
375  // n^2 K
376  solver.solve(m->A, m->B, 1, true);
377 
378  // Extract solution and store it in X
379  double * sol = m->B;
380 
381  for (casadi_int k=0;k<K_;++k) {
382  double *X = m->X+ k*n_*n_, *S = sol+ n1*n2*k;
383  dense_copy_stride(p[r+1]-p[r], p[l+1]-p[l], S, X+ p[r]*n_ + p[l], n2, n_);
384  dense_copy_t_stride(p[r+1]-p[r], p[l+1]-p[l], S, X+ p[l]*n_ + p[r], n2, n_);
385  }
386 
387  }
388 
389  // n^3 K
390  std::fill(res[DPLE_P]+d*n_*n_*K_, res[DPLE_P]+(d+1)*n_*n_*K_, 0);
391  }
392 
393 
394  for (casadi_int k=0;k<K_;++k) {
395  std::fill(m->nnKa+k*n_*n_, m->nnKa+(k+1)*n_*n_, 0);
396  // nnKa[k] <- V[k]*Z[k]'
397  // n^3 K
398  dense_mul_nn(n_, n_, n_, m->X + k*n_*n_, m->Z+ k*n_*n_, m->nnKa+ k*n_*n_);
399  // output <- Z[k]*V[k]*Z[k]'
400  dense_mul_tn(n_, n_, n_, m->Z + k*n_*n_, m->nnKa+ k*n_*n_,
401  res[DPLE_P]+d*n_*n_*K_+ k*n_*n_);
402  }
403 
404  }
405  return 0;
406  }
407 
408 
409  void slicot_periodic_schur(casadi_int n, casadi_int K, const double* a,
410  double* t, double * z,
411  double* dwork, double* eig_real,
412  double *eig_imag, double num_zero) {
413  casadi_int mem_base = std::max(n+K-2, 4*n);
414  casadi_int mem_needed = mem_base+(n-1)*K;
415 
416  // a is immutable, we need a mutable pointer, so we use available buffer
417  std::copy(a, a+n*n*K, z);
418 
419  casadi_int ret;
420 
421  ret = slicot_mb03vd(n, K, 1, n, z, n, n, dwork+mem_base, n-1, dwork);
422  casadi_assert(ret==0, "mb03vd return code " + str(ret));
423  std::copy(z, z+n*n*K, t);
424 
425  ret = slicot_mb03vy(n, K, 1, n, z, n, n, dwork+mem_base, n-1, dwork, mem_needed);
426  casadi_assert(ret==0, "mb03vy return code " + str(ret));
427  // Set numerical zeros to zero
428  if (num_zero>0) {
429  for (casadi_int k = 0;k<n*n*K;++k) {
430  double &r = t[k];
431  if (fabs(r)<num_zero) r = 0.0;
432  }
433  }
434 
435  ret = slicot_mb03wd('S', 'V', n, K, 1, n, 1, n, t, n, n, z, n, n,
436  eig_real, eig_imag, dwork, mem_needed);
437  casadi_assert(ret==0, "mb03wd return code " + str(ret));
438  }
439 
440 } // namespace casadi
Internal class.
Definition: dple_impl.hpp:36
Sparsity A_
List of sparsities of A_i.
Definition: dple_impl.hpp:125
double eps_unstable_
Margin for instability detection.
Definition: dple_impl.hpp:143
bool error_unstable_
Throw an error when system is unstable.
Definition: dple_impl.hpp:140
void init(const Dict &opts) override
Initialize.
Definition: dple.cpp:192
casadi_int K_
Period.
Definition: dple_impl.hpp:131
bool const_dim_
Constant dimensions.
Definition: dple_impl.hpp:134
casadi_int nrhs_
Number of right hand sides.
Definition: dple_impl.hpp:146
static const Options options_
Options.
Definition: dple_impl.hpp:73
Sparsity V_
List of sparsities of V_i.
Definition: dple_impl.hpp:128
bool pos_def_
Assume positive definiteness of P_i.
Definition: dple_impl.hpp:137
void alloc_iw(size_t sz_iw, bool persistent=false)
Ensure required length of iw field.
virtual void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const
Set the (persistent) work vectors.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
void setup(void *mem, const double **arg, double **res, casadi_int *iw, double *w) const
Set the (persistent and temporary) work vectors.
Linear solver.
Definition: linsol.hpp:55
void nfact(const DM &A) const
Numeric factorization of the linear system.
Definition: linsol.cpp:127
DM solve(const DM &A, const DM &B, bool tr=false) const
Definition: linsol.cpp:73
void sfact(const DM &A) const
Symbolic factorization of the linear system, e.g. selecting pivots.
Definition: linsol.cpp:105
GenericMatrix< Matrix< Scalar > > B
Base class.
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
virtual int init_mem(void *mem) const
Initalize memory block.
void clear_mem()
Clear all memory (called from destructor)
static const std::string meta_doc
A documentation string.
~SlicotDple() override
Destructor.
Definition: slicot_dple.cpp:77
int init_mem(void *mem) const override
Initalize memory block.
static Dple * creator(const std::string &name, const SpDict &st)
Create a new QP Solver.
SlicotDple()
Constructor.
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
int eval(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Evaluate numerically.
static const Options options_
Options.
void init(const Dict &opts) override
Initialize.
Definition: slicot_dple.cpp:83
General sparsity class.
Definition: sparsity.hpp:106
static Sparsity diag(casadi_int nrow)
Create diagonal sparsity pattern *.
Definition: sparsity.hpp:190
static Sparsity dense(casadi_int nrow, casadi_int ncol=1)
Create a dense rectangular sparsity pattern *.
Definition: sparsity.cpp:1012
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
Definition: sparsity.cpp:168
static Sparsity band(casadi_int n, casadi_int p)
Create a single band in a square sparsity pattern.
Definition: sparsity.cpp:1070
The casadi namespace.
Definition: archiver.cpp:28
int CASADI_DPLE_SLICOT_EXPORT casadi_register_dple_slicot(Dple::Plugin *plugin)
Definition: slicot_dple.cpp:42
int slicot_mb03wd(char job, char compz, int n, int p, int ilo, int ihi, int iloz, int ihiz, double *h, int ldh1, int ldh2, double *z, int ldz1, int ldz2, double *wr, double *wi, double *dwork, int ldwork)
void slicot_periodic_schur(casadi_int n, casadi_int K, const double *a, double *t, double *z, double *dwork, double *eig_real, double *eig_imag, double num_zero)
int slicot_mb03vy(int n, int p, int ilo, int ihi, double *a, int lda1, int lda2, const double *tau, int ldtau, double *dwork, int ldwork)
int slicot_mb03vd(int n, int p, int ilo, int ihi, double *a, int lda1, int lda2, double *tau, int ldtau, double *dwork)
@ DPLE_A
A matrices (horzcat when const_dim, diagcat otherwise) [a].
Definition: dple.hpp:121
@ DPLE_V
V matrices (horzcat when const_dim, diagcat otherwise) [v].
Definition: dple.hpp:123
void dense_mul_nn(casadi_int n, casadi_int m, casadi_int l, const double *A, const double *B, double *C)
Definition: slicot_la.hpp:93
@ DPLE_P
Lyapunov matrix (horzcat when const_dim, diagcat otherwise) (Cholesky of P if pos_def) [p].
Definition: dple.hpp:130
void dense_mul_tn(casadi_int n, casadi_int m, casadi_int l, const double *A, const double *B, double *C)
Definition: slicot_la.hpp:114
void dense_mul_nt(casadi_int n, casadi_int m, casadi_int l, const double *A, const double *B, double *C)
Definition: slicot_la.hpp:56
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
void dense_copy_t_stride(casadi_int n, casadi_int m, const double *A, double *B, casadi_int strideA, casadi_int strideB)
Definition: slicot_la.hpp:82
void CASADI_DPLE_SLICOT_EXPORT casadi_load_dple_slicot()
Definition: slicot_dple.cpp:52
void dense_kron_stride(casadi_int n, casadi_int m, const double *A, const double *B, double *C, casadi_int strideA, casadi_int strideB, casadi_int strideC)
Definition: slicot_la.hpp:32
std::map< std::string, Sparsity > SpDict
Definition: sparsity.hpp:1502
void dense_mul_nn_stride(casadi_int n, casadi_int m, casadi_int l, const double *A, const double *B, double *C, casadi_int strideA, casadi_int strideB, casadi_int strideC)
Definition: slicot_la.hpp:61
void dense_copy_stride(casadi_int n, casadi_int m, const double *A, double *B, casadi_int strideA, casadi_int strideB)
Definition: slicot_la.hpp:73
void dense_mul_nt_stride(casadi_int n, casadi_int m, casadi_int l, const double *A, const double *B, double *C, casadi_int strideA, casadi_int strideB, casadi_int strideC)
Definition: slicot_la.hpp:42
void casadi_trans(const T1 *x, const casadi_int *sp_x, T1 *y, const casadi_int *sp_y, casadi_int *tmp)
TRANS: y <- trans(x) , w work vector (length >= rows x)
std::vector< std::vector< Linsol > > dpse_solvers
Solvers for low-order Discrete Periodic Sylvester Equations.
Definition: slicot_dple.hpp:79