solve_impl.hpp
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 #ifndef CASADI_SOLVE_IMPL_HPP
27 #define CASADI_SOLVE_IMPL_HPP
28 
29 #include "solve.hpp"
30 #include "linsol_internal.hpp"
31 
32 namespace casadi {
33 
34  template<bool Tr>
35  Solve<Tr>::Solve(const MX& r, const MX& A) {
36  casadi_assert(r.size1() == A.size2(),
37  "Solve::Solve: dimension mismatch. Got r " + r.dim() + " and A " + A.dim());
38  set_dep(r, A);
39  set_sparsity(r.sparsity());
40  }
41 
42  template<bool Tr>
43  std::string Solve<Tr>::disp(const std::vector<std::string>& arg) const {
44  std::stringstream ss;
45  ss << "(" << mod_prefix() << arg.at(1) << mod_suffix();
46  if (Tr) ss << "'";
47  ss << "\\" << arg.at(0) << ")";
48  return ss.str();
49  }
50 
51  template<bool Tr>
52  LinsolCall<Tr>::LinsolCall(const MX& r, const MX& A, const Linsol& linear_solver) :
53  Solve<Tr>(r, A), linsol_(linear_solver) {
54  }
55 
56  template<bool Tr>
57  int LinsolCall<Tr>::eval(const double** arg, double** res, casadi_int* iw, double* w) const {
58  if (arg[0] != res[0]) std::copy(arg[0], arg[0] + this->dep(0).nnz(), res[0]);
59  scoped_checkout<Linsol> mem(linsol_);
60 
61  auto m = static_cast<LinsolMemory*>(linsol_->memory(mem));
62  // Reset statistics
63  for (auto&& s : m->fstats) s.second.reset();
64  if (m->t_total) m->t_total->tic();
65 
66  if (linsol_.sfact(arg[1], mem)) return 1;
67  if (linsol_.nfact(arg[1], mem)) return 1;
68  if (linsol_.solve(arg[1], res[0], this->dep(0).size2(), Tr, mem)) return 1;
69 
70  linsol_->print_time(m->fstats);
71 
72  return 0;
73  }
74 
75  template<bool Tr>
76  int LinsolCall<Tr>::eval_sx(const SXElem** arg, SXElem** res, casadi_int* iw, SXElem* w) const {
77  linsol_->linsol_eval_sx(arg, res, iw, w, linsol_->memory(0), Tr, this->dep(0).size2());
78  return 0;
79  }
80 
81  template<bool Tr>
82  void Solve<Tr>::eval_mx(const std::vector<MX>& arg, std::vector<MX>& res) const {
83  if (arg[0].is_zero()) {
84  res[0] = MX(arg[0].size());
85  } else {
86  res[0] = solve(arg[1], arg[0], Tr);
87  }
88  }
89 
90  template<bool Tr>
91  void Solve<Tr>::ad_forward(const std::vector<std::vector<MX> >& fseed,
92  std::vector<std::vector<MX> >& fsens) const {
93  // Nondifferentiated inputs and outputs
94  std::vector<MX> arg(this->n_dep());
95  for (casadi_int i=0; i<arg.size(); ++i) arg[i] = this->dep(i);
96  std::vector<MX> res(this->nout());
97  for (casadi_int i=0; i<res.size(); ++i) res[i] = this->get_output(i);
98 
99  // Number of derivatives
100  casadi_int nfwd = fseed.size();
101  const MX& A = arg[1];
102  const MX& X = res[0];
103 
104  // Solve for all directions at once
105  std::vector<MX> rhs(nfwd);
106  std::vector<casadi_int> col_offset(nfwd+1, 0);
107  for (casadi_int d=0; d<nfwd; ++d) {
108  const MX& B_hat = fseed[d][0];
109  const MX& A_hat = fseed[d][1];
110  rhs[d] = Tr ? B_hat - mtimes(A_hat.T(), X) : B_hat - mtimes(A_hat, X);
111  col_offset[d+1] = col_offset[d] + rhs[d].size2();
112  }
113  rhs = horzsplit(solve(A, horzcat(rhs), Tr), col_offset);
114 
115  // Fetch result
116  fsens.resize(nfwd);
117  for (casadi_int d=0; d<nfwd; ++d) {
118  fsens[d].resize(1);
119  fsens[d][0] = rhs[d];
120  }
121  }
122 
123  template<bool Tr>
124  void Solve<Tr>::ad_reverse(const std::vector<std::vector<MX> >& aseed,
125  std::vector<std::vector<MX> >& asens) const {
126  // Nondifferentiated inputs and outputs
127  std::vector<MX> arg(this->n_dep());
128  for (casadi_int i=0; i<arg.size(); ++i) arg[i] = this->dep(i);
129  std::vector<MX> res(this->nout());
130  for (casadi_int i=0; i<res.size(); ++i) res[i] = this->get_output(i);
131 
132  // Number of derivatives
133  casadi_int nadj = aseed.size();
134  const MX& A = arg[1];
135  const MX& X = res[0];
136 
137  // Solve for all directions at once
138  std::vector<MX> rhs(nadj);
139  std::vector<casadi_int> col_offset(nadj+1, 0);
140  for (casadi_int d=0; d<nadj; ++d) {
141  rhs[d] = aseed[d][0];
142  col_offset[d+1] = col_offset[d] + rhs[d].size2();
143  }
144  rhs = horzsplit(solve(A, horzcat(rhs), !Tr), col_offset);
145 
146  // Collect sensitivities
147  asens.resize(nadj);
148  for (casadi_int d=0; d<nadj; ++d) {
149  asens[d].resize(2);
150 
151  // Propagate to A
152  MX a;
153  if (!Tr) {
154  a = -mac(rhs[d], X.T(), MX::zeros(A.sparsity()));
155  } else {
156  a = -mac(X, rhs[d].T(), MX::zeros(A.sparsity()));
157  }
158  if (asens[d][1].is_empty(true)) {
159  asens[d][1] = a;
160  } else {
161  asens[d][1] += a;
162  }
163 
164  // Propagate to B
165  if (asens[d][0].is_empty(true)) {
166  asens[d][0] = rhs[d];
167  } else {
168  asens[d][0] += rhs[d];
169  }
170  }
171  }
172 
173  template<bool Tr>
174  int Solve<Tr>::sp_forward(const bvec_t** arg, bvec_t** res, casadi_int* iw, bvec_t* w) const {
175  // Number of right-hand-sides
176  casadi_int nrhs = dep(0).size2();
177 
178  // Sparsities
179  const Sparsity& A_sp = this->A_sp();
180  const casadi_int* A_colind = A_sp.colind();
181  const casadi_int* A_row = A_sp.row();
182  casadi_int n = A_sp.size1();
183 
184  // Get pointers to data
185  const bvec_t *B=arg[0], *A = arg[1];
186  bvec_t* X = res[0];
187  bvec_t* tmp = w;
188 
189  // For all right-hand-sides
190  for (casadi_int r=0; r<nrhs; ++r) {
191  // Copy B to a temporary vector
192  std::copy(B, B+n, tmp);
193 
194  // Add A_hat contribution to tmp
195  for (casadi_int cc=0; cc<n; ++cc) {
196  for (casadi_int k=A_colind[cc]; k<A_colind[cc+1]; ++k) {
197  casadi_int rr = A_row[k];
198  tmp[Tr ? cc : rr] |= A[k];
199  }
200  }
201 
202  // Propagate to X
203  std::fill(X, X+n, 0);
204  A_sp.spsolve(X, tmp, Tr);
205 
206  // Continue to the next right-hand-side
207  B += n;
208  X += n;
209  }
210  return 0;
211  }
212 
213  template<bool Tr>
214  int Solve<Tr>::sp_reverse(bvec_t** arg, bvec_t** res, casadi_int* iw, bvec_t* w) const {
215  // Number of right-hand-sides
216  casadi_int nrhs = dep(0).size2();
217 
218  // Sparsities
219  const Sparsity& A_sp = this->A_sp();
220  const casadi_int* A_colind = A_sp.colind();
221  const casadi_int* A_row = A_sp.row();
222  casadi_int n = A_sp.size1();
223 
224  // Get pointers to data
225  bvec_t *B=arg[0], *A=arg[1], *X=res[0];
226  bvec_t* tmp = w;
227 
228  // For all right-hand-sides
229  for (casadi_int r=0; r<nrhs; ++r) {
230  // Solve transposed
231  std::fill(tmp, tmp+n, 0);
232  A_sp.spsolve(tmp, X, !Tr);
233 
234  // Clear seeds
235  std::fill(X, X+n, 0);
236 
237  // Propagate to B
238  for (casadi_int i=0; i<n; ++i) B[i] |= tmp[i];
239 
240  // Propagate to A
241  for (casadi_int cc=0; cc<n; ++cc) {
242  for (casadi_int k=A_colind[cc]; k<A_colind[cc+1]; ++k) {
243  casadi_int rr = A_row[k];
244  A[k] |= tmp[Tr ? cc : rr];
245  }
246  }
247 
248  // Continue to the next right-hand-side
249  B += n;
250  X += n;
251  }
252  return 0;
253  }
254 
255  template<bool Tr>
256  size_t LinsolCall<Tr>::sz_w() const {
257  return this->sparsity().size1();
258  }
259 
260  template<bool Tr>
262  const std::vector<casadi_int>& arg,
263  const std::vector<casadi_int>& res) const {
264  // Number of right-hand-sides
265  casadi_int nrhs = this->dep(0).size2();
266 
267  // Array for x
268  g.local("rr", "casadi_real", "*");
269  g << "rr = " << g.work(res[0], this->nnz()) << ";\n";
270 
271  // Array for A
272  g.local("ss", "casadi_real", "*");
273  g << "ss = " << g.work(arg[1], this->dep(1).nnz()) << ";\n";
274 
275  // Copy b to x if not inplace
276  if (arg[0]!=res[0]) {
277  g << g.copy(g.work(arg[0], this->nnz()), this->nnz(), "rr") << '\n';
278  }
279  // Solver specific codegen
280  linsol_->generate(g, "ss", "rr", nrhs, Tr);
281  }
282 
283  template<bool Tr>
286  }
287 
288  template<bool Tr>
291  s.pack("Solve::Tr", Tr);
292  }
293 
294  template<bool Tr>
296  }
297 
298  template<bool Tr>
300  bool tr;
301  s.unpack("Solve::Tr", tr);
302  casadi_error("Not implemented");
303  }
304 
305  template<bool Tr>
308  s.pack("Solve::Linsol", linsol_);
309  }
310 
311  template<bool Tr>
314  }
315 
316  template<bool Tr>
318  s.unpack("Solve::Linsol", linsol_);
319  }
320 
321  template<bool Tr>
323  bool tr;
324  s.unpack("Solve::Tr", tr);
325 
326  if (tr) {
327  return new LinsolCall<true>(s);
328  } else {
329  return new LinsolCall<false>(s);
330  }
331  }
332 
333  template<bool Tr>
334  TriuSolve<Tr>::TriuSolve(const MX& r, const MX& A) : Solve<Tr>(r, A) {
335  }
336 
337  template<bool Tr>
338  int TriuSolve<Tr>::eval(const double** arg, double** res, casadi_int* iw, double* w) const {
339  if (arg[0] != res[0]) std::copy(arg[0], arg[0] + this->dep(0).nnz(), res[0]);
340  casadi_triusolve(this->dep(1).sparsity(), arg[1], res[0], Tr, false, this->dep(0).size2());
341  return 0;
342  }
343 
344  template<bool Tr>
345  int TriuSolve<Tr>::eval_sx(const SXElem** arg, SXElem** res, casadi_int* iw, SXElem* w) const {
346  if (arg[0] != res[0]) std::copy(arg[0], arg[0] + this->dep(0).nnz(), res[0]);
347  casadi_triusolve(this->dep(1).sparsity(), arg[1], res[0], Tr, false, this->dep(0).size2());
348  return 0;
349  }
350 
351  template<bool Tr>
352  TrilSolve<Tr>::TrilSolve(const MX& r, const MX& A) : Solve<Tr>(r, A) {
353  }
354 
355  template<bool Tr>
356  int TrilSolve<Tr>::eval(const double** arg, double** res, casadi_int* iw, double* w) const {
357  if (arg[0] != res[0]) std::copy(arg[0], arg[0] + this->dep(0).nnz(), res[0]);
358  casadi_trilsolve(this->dep(1).sparsity(), arg[1], res[0], Tr, false, this->dep(0).size2());
359  return 0;
360  }
361 
362  template<bool Tr>
363  int TrilSolve<Tr>::eval_sx(const SXElem** arg, SXElem** res, casadi_int* iw, SXElem* w) const {
364  if (arg[0] != res[0]) std::copy(arg[0], arg[0] + this->dep(0).nnz(), res[0]);
365  casadi_trilsolve(this->dep(1).sparsity(), arg[1], res[0], Tr, false, this->dep(0).size2());
366  return 0;
367  }
368 
369  template<bool Tr>
370  SolveUnity<Tr>::SolveUnity(const MX& r, const MX& A) : Solve<Tr>(r, A) {
371  }
372 
373  template<bool Tr>
375  // Create on first call
376  if (A_sp_.is_null()) {
377  const Sparsity& no_diag = this->dep(1).sparsity();
378  A_sp_ = no_diag + Sparsity::diag(no_diag.size1());
379  }
380  // Return reference
381  return A_sp_;
382  }
383 
384  template<bool Tr>
386  : SolveUnity<Tr>(r, A) {
387  }
388 
389  template<bool Tr>
390  int TriuSolveUnity<Tr>::eval(const double** arg, double** res, casadi_int* iw, double* w) const {
391  if (arg[0] != res[0]) std::copy(arg[0], arg[0] + this->dep(0).nnz(), res[0]);
392  casadi_triusolve(this->dep(1).sparsity(), arg[1], res[0], Tr, true, this->dep(0).size2());
393  return 0;
394  }
395 
396  template<bool Tr>
397  int TriuSolveUnity<Tr>::eval_sx(const SXElem** arg, SXElem** res, casadi_int* iw,
398  SXElem* w) const {
399  if (arg[0] != res[0]) std::copy(arg[0], arg[0] + this->dep(0).nnz(), res[0]);
400  casadi_triusolve(this->dep(1).sparsity(), arg[1], res[0], Tr, true, this->dep(0).size2());
401  return 0;
402  }
403 
404  template<bool Tr>
406  : SolveUnity<Tr>(r, A) {
407  }
408 
409  template<bool Tr>
410  int TrilSolveUnity<Tr>::eval(const double** arg, double** res, casadi_int* iw, double* w) const {
411  if (arg[0] != res[0]) std::copy(arg[0], arg[0] + this->dep(0).nnz(), res[0]);
412  casadi_trilsolve(this->dep(1).sparsity(), arg[1], res[0], Tr, true, this->dep(0).size2());
413  return 0;
414  }
415 
416  template<bool Tr>
417  int TrilSolveUnity<Tr>::eval_sx(const SXElem** arg, SXElem** res, casadi_int* iw,
418  SXElem* w) const {
419  if (arg[0] != res[0]) std::copy(arg[0], arg[0] + this->dep(0).nnz(), res[0]);
420  casadi_trilsolve(this->dep(1).sparsity(), arg[1], res[0], Tr, true, this->dep(0).size2());
421  return 0;
422  }
423 
424  template<bool Tr>
425  void TriuSolve<Tr>::generate(CodeGenerator& g, const std::vector<casadi_int>& arg,
426  const std::vector<casadi_int>& res) const {
427  // Number of right-hand-sides
428  casadi_int nrhs = this->dep(0).size2();
429  // Copy first argument if not inplace
430  if (arg[0]!=res[0]) {
431  g << g.copy(g.work(arg[0], this->nnz()), this->nnz(), g.work(res[0], this->nnz())) << '\n';
432  }
433  // Perform sparse matrix multiplication
434  g << g.triusolve(this->dep(1).sparsity(), g.work(arg[1], this->dep(1).nnz()),
435  g.work(res[0], this->nnz()), Tr, false, nrhs) << '\n';
436  }
437 
438  template<bool Tr>
439  void TrilSolve<Tr>::generate(CodeGenerator& g, const std::vector<casadi_int>& arg,
440  const std::vector<casadi_int>& res) const {
441  // Number of right-hand-sides
442  casadi_int nrhs = this->dep(0).size2();
443  // Copy first argument if not inplace
444  if (arg[0]!=res[0]) {
445  g << g.copy(g.work(arg[0], this->nnz()), this->nnz(), g.work(res[0], this->nnz())) << '\n';
446  }
447  // Perform sparse matrix multiplication
448  g << g.trilsolve(this->dep(1).sparsity(), g.work(arg[1], this->dep(1).nnz()),
449  g.work(res[0], this->nnz()), Tr, false, nrhs) << '\n';
450  }
451 
452  template<bool Tr>
453  void TriuSolveUnity<Tr>::generate(CodeGenerator& g, const std::vector<casadi_int>& arg,
454  const std::vector<casadi_int>& res) const {
455  // Number of right-hand-sides
456  casadi_int nrhs = this->dep(0).size2();
457  // Copy first argument if not inplace
458  if (arg[0]!=res[0]) {
459  g << g.copy(g.work(arg[0], this->nnz()), this->nnz(), g.work(res[0], this->nnz())) << '\n';
460  }
461  // Perform sparse matrix multiplication
462  g << g.triusolve(this->dep(1).sparsity(), g.work(arg[1], this->dep(1).nnz()),
463  g.work(res[0], this->nnz()), Tr, true, nrhs) << '\n';
464  }
465 
466  template<bool Tr>
467  void TrilSolveUnity<Tr>::generate(CodeGenerator& g, const std::vector<casadi_int>& arg,
468  const std::vector<casadi_int>& res) const {
469  // Number of right-hand-sides
470  casadi_int nrhs = this->dep(0).size2();
471  // Copy first argument if not inplace
472  if (arg[0]!=res[0]) {
473  g << g.copy(g.work(arg[0], this->nnz()), this->nnz(), g.work(res[0], this->nnz())) << '\n';
474  }
475  // Perform sparse matrix multiplication
476  g << g.trilsolve(this->dep(1).sparsity(), g.work(arg[1], this->dep(1).nnz()),
477  g.work(res[0], this->nnz()), Tr, true, nrhs) << '\n';
478  }
479 
480 } // namespace casadi
481 
482 #endif // CASADI_SOLVE_IMPL_HPP
Helper class for C code generation.
std::string generate(const std::string &prefix="")
Generate file(s)
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
Sparsity sparsity() const
Get the sparsity pattern.
casadi_int size2() const
Get the second dimension (i.e. number of columns)
casadi_int size1() const
Get the first dimension (i.e. number of rows)
std::string dim(bool with_nz=false) const
Get string representation of dimensions.
static MX zeros(casadi_int nrow=1, casadi_int ncol=1)
Create a dense matrix or a matrix with specified sparsity with all entries zero.
Linear solve operation with a linear solver instance.
Definition: solve.hpp:148
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: solve_impl.hpp:306
int eval_sx(const SXElem **arg, SXElem **res, casadi_int *iw, SXElem *w) const override
Evaluate the function symbolically (SX)
Definition: solve_impl.hpp:76
size_t sz_w() const override
Get required length of w field.
Definition: solve_impl.hpp:256
void generate(CodeGenerator &g, const std::vector< casadi_int > &arg, const std::vector< casadi_int > &res) const override
Generate code for the operation.
Definition: solve_impl.hpp:261
static MXNode * deserialize(DeserializingStream &s)
Deserialize with type disambiguation.
Definition: solve_impl.hpp:322
void serialize_type(SerializingStream &s) const override
Serialize type information.
Definition: solve_impl.hpp:312
int eval(const double **arg, double **res, casadi_int *iw, double *w) const override
Evaluate the function numerically.
Definition: solve_impl.hpp:57
LinsolCall(const MX &r, const MX &A, const Linsol &linear_solver)
Constructor.
Definition: solve_impl.hpp:52
Linsol linsol_
Linear solver (may be shared between multiple nodes)
Definition: solve.hpp:180
Linear solver.
Definition: linsol.hpp:55
Node class for MX objects.
Definition: mx_node.hpp:50
virtual void serialize_type(SerializingStream &s) const
Serialize type information.
virtual void serialize_body(SerializingStream &s) const
Serialize an object without type information.
MX - Matrix expression.
Definition: mx.hpp:84
MX T() const
Transpose the matrix.
Helper class for Serialization.
void pack(const Sparsity &e)
Serializes an object to the output stream.
Linear solve with unity diagonal added.
Definition: solve.hpp:291
const Sparsity & A_sp() const override
Sparsity pattern for the linear system.
Definition: solve_impl.hpp:374
SolveUnity(const MX &r, const MX &A)
Constructor.
Definition: solve_impl.hpp:370
An MX atomic for linear solver solution: x = r * A^-1 or x = r * A^-T.
Definition: solve.hpp:47
static MXNode * deserialize(DeserializingStream &s)
Deserialize with type disambiguation.
Definition: solve_impl.hpp:299
Solve(const MX &r, const MX &A)
Constructor.
Definition: solve_impl.hpp:35
void ad_forward(const std::vector< std::vector< MX > > &fseed, std::vector< std::vector< MX > > &fsens) const override
Calculate forward mode directional derivatives.
Definition: solve_impl.hpp:91
std::string disp(const std::vector< std::string > &arg) const override
Print expression.
Definition: solve_impl.hpp:43
void ad_reverse(const std::vector< std::vector< MX > > &aseed, std::vector< std::vector< MX > > &asens) const override
Calculate reverse mode directional derivatives.
Definition: solve_impl.hpp:124
void eval_mx(const std::vector< MX > &arg, std::vector< MX > &res) const override
Evaluate symbolically (MX)
Definition: solve_impl.hpp:82
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: solve_impl.hpp:284
int sp_reverse(bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w) const override
Propagate sparsity backwards.
Definition: solve_impl.hpp:214
int sp_forward(const bvec_t **arg, bvec_t **res, casadi_int *iw, bvec_t *w) const override
Propagate sparsity forward.
Definition: solve_impl.hpp:174
void serialize_type(SerializingStream &s) const override
Serialize type information.
Definition: solve_impl.hpp:289
General sparsity class.
Definition: sparsity.hpp:99
casadi_int colind(casadi_int cc) const
Get a reference to the colindex of column cc (see class description)
casadi_int size1() const
Get the number of rows.
static Sparsity diag(casadi_int nrow)
Create diagonal sparsity pattern *.
Definition: sparsity.hpp:183
casadi_int row(casadi_int el) const
Get the row of a non-zero element.
TrilSolveUnity(const MX &r, const MX &A)
Constructor.
Definition: solve_impl.hpp:405
int eval(const double **arg, double **res, casadi_int *iw, double *w) const override
Evaluate the function numerically.
Definition: solve_impl.hpp:410
int eval_sx(const SXElem **arg, SXElem **res, casadi_int *iw, SXElem *w) const override
Evaluate the function symbolically (SX)
Definition: solve_impl.hpp:417
void generate(CodeGenerator &g, const std::vector< casadi_int > &arg, const std::vector< casadi_int > &res) const override
Generate code for the operation.
Definition: solve_impl.hpp:467
TrilSolve(const MX &r, const MX &A)
Constructor.
Definition: solve_impl.hpp:352
int eval(const double **arg, double **res, casadi_int *iw, double *w) const override
Evaluate the function numerically.
Definition: solve_impl.hpp:356
int eval_sx(const SXElem **arg, SXElem **res, casadi_int *iw, SXElem *w) const override
Evaluate the function symbolically (SX)
Definition: solve_impl.hpp:363
void generate(CodeGenerator &g, const std::vector< casadi_int > &arg, const std::vector< casadi_int > &res) const override
Generate code for the operation.
Definition: solve_impl.hpp:439
TriuSolveUnity(const MX &r, const MX &A)
Constructor.
Definition: solve_impl.hpp:385
void generate(CodeGenerator &g, const std::vector< casadi_int > &arg, const std::vector< casadi_int > &res) const override
Generate code for the operation.
Definition: solve_impl.hpp:453
int eval_sx(const SXElem **arg, SXElem **res, casadi_int *iw, SXElem *w) const override
Evaluate the function symbolically (SX)
Definition: solve_impl.hpp:397
int eval(const double **arg, double **res, casadi_int *iw, double *w) const override
Evaluate the function numerically.
Definition: solve_impl.hpp:390
void generate(CodeGenerator &g, const std::vector< casadi_int > &arg, const std::vector< casadi_int > &res) const override
Generate code for the operation.
Definition: solve_impl.hpp:425
int eval(const double **arg, double **res, casadi_int *iw, double *w) const override
Evaluate the function numerically.
Definition: solve_impl.hpp:338
TriuSolve(const MX &r, const MX &A)
Constructor.
Definition: solve_impl.hpp:334
int eval_sx(const SXElem **arg, SXElem **res, casadi_int *iw, SXElem *w) const override
Evaluate the function symbolically (SX)
Definition: solve_impl.hpp:345
The casadi namespace.
bool is_zero(const T &x)