csparse_interface.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  * Copyright (C) 2018 Robert Bosch GmbH
9  *
10  * CasADi is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 3 of the License, or (at your option) any later version.
14  *
15  * CasADi is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with CasADi; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23  *
24  */
25 
26 
27 #include "csparse_interface.hpp"
28 #include "casadi/core/global_options.hpp"
29 
30 namespace casadi {
31 
32  extern "C"
33  int CASADI_LINSOL_CSPARSE_EXPORT
34  casadi_register_linsol_csparse(LinsolInternal::Plugin* plugin) {
35  plugin->creator = CsparseInterface::creator;
36  plugin->name = "csparse";
37  plugin->doc = CsparseInterface::meta_doc.c_str();
38  plugin->version = CASADI_VERSION;
39  plugin->options = &CsparseInterface::options_;
40  plugin->deserialize = &CsparseInterface::deserialize;
41  return 0;
42  }
43 
44  extern "C"
45  void CASADI_LINSOL_CSPARSE_EXPORT casadi_load_linsol_csparse() {
47  }
48 
49  CsparseInterface::CsparseInterface(const std::string& name, const Sparsity& sp)
50  : LinsolInternal(name, sp) {
51  }
52 
54  clear_mem();
55  }
56 
58  if (this->S) cs_sfree(this->S);
59  if (this->N) cs_nfree(this->N);
60  }
61 
62  void CsparseInterface::init(const Dict& opts) {
63  // Call the init method of the base class
65  }
66 
67  int CsparseInterface::init_mem(void* mem) const {
68  if (LinsolInternal::init_mem(mem)) return 1;
69  auto m = static_cast<CsparseMemory*>(mem);
70 
71  m->N = nullptr;
72  m->S = nullptr;
73  m->A.nzmax = this->nnz(); // maximum number of entries
74  m->A.m = this->nrow(); // number of rows
75  m->A.n = this->ncol(); // number of columns
76  m->colind.resize(this->ncol()+1);
77  m->row.resize(this->nnz());
78  copy_vector(this->colind(), m->colind);
79  copy_vector(this->row(), m->row);
80  m->A.p = get_ptr(m->colind); // row pointers (size n+1)
81  m->A.i = get_ptr(m->row); // row pointers (size n+1)
82  m->A.x = nullptr; // numerical values, size nzmax
83  m->A.nz = -1; // of entries in triplet matrix, -1 for compressed-column
84 
85  // Temporary
86  m->temp_.resize(m->A.n);
87  return 0;
88  }
89 
90  int CsparseInterface::sfact(void* mem, const double* A) const {
91  auto m = static_cast<CsparseMemory*>(mem);
92 
93  // Set the nonzeros of the matrix
94  m->A.x = const_cast<double*>(A);
95 
96  // ordering and symbolic analysis
97  casadi_int order = 0; // ordering?
98  if (m->S) cs_sfree(m->S);
99  m->S = cs_sqr(order, &m->A, 0);
100  return 0;
101  }
102 
103  int CsparseInterface::nfact(void* mem, const double* A) const {
104  auto m = static_cast<CsparseMemory*>(mem);
105 
106  // Set the nonzeros of the matrix
107  m->A.x = const_cast<double*>(A);
108 
109  // Make sure that all entries of the linear system are valid
110  for (casadi_int k=0; k<this->nnz(); ++k) {
111  casadi_assert(!isnan(A[k]),
112  "Nonzero " + str(k) + " is not-a-number");
113  casadi_assert(!isinf(A[k]),
114  "Nonzero " + str(k) + " is infinite");
115  }
116 
117  if (verbose_) {
118  uout() << "CsparseInterface::prepare: numeric factorization" << std::endl;
119  uout() << "linear system to be factorized = " << std::endl;
120  DM(sp_, std::vector<double>(A, A+nnz())).print_sparse(uout());
121  }
122 
123  double tol = 1e-8;
124 
125  if (m->N) cs_nfree(m->N);
126  m->N = cs_lu(&m->A, m->S, tol) ; // numeric LU factorization
127  if (m->N==nullptr) {
128  DM temp(sp_, std::vector<double>(A, A+nnz()));
129  temp = sparsify(temp);
130  if (temp.sparsity().is_singular()) {
131  std::stringstream ss;
132  ss << "CsparseInterface::prepare: factorization failed due to matrix"
133  " being singular. Matrix contains numerical zeros which are "
134  "structurally non-zero. Promoting these zeros to be structural "
135  "zeros, the matrix was found to be structurally rank deficient."
136  " sprank: " << sprank(temp.sparsity()) << " <-> " << temp.size2() << std::endl;
137  if (verbose_) {
138  ss << "Sparsity of the linear system: " << std::endl;
139  sp_.disp(ss, true); // print detailed
140  }
141  throw CasadiException(ss.str());
142  } else {
143  std::stringstream ss;
144  ss << "CsparseInterface::prepare: factorization failed, check if Jacobian is singular"
145  << std::endl;
146  if (verbose_) {
147  ss << "Sparsity of the linear system: " << std::endl;
148  sp_.disp(ss, true); // print detailed
149  }
150  throw CasadiException(ss.str());
151  }
152  }
153  casadi_assert_dev(m->N!=nullptr);
154  return 0;
155  }
156 
157  int CsparseInterface::solve(void* mem, const double* A, double* x,
158  casadi_int nrhs, bool tr) const {
159  auto m = static_cast<CsparseMemory*>(mem);
160  casadi_assert_dev(m->N!=nullptr);
161 
162  double *t = &m->temp_.front();
163 
164  for (casadi_int k=0; k<nrhs; ++k) {
165  if (tr) {
166  cs_pvec(m->S->q, x, t, m->A.n) ; // t = P2*b
167  casadi_assert_dev(m->N->U!=nullptr);
168  cs_utsolve(m->N->U, t) ; // t = U'\t
169  cs_ltsolve(m->N->L, t) ; // t = L'\t
170  cs_pvec(m->N->pinv, t, x, m->A.n) ; // x = P1*t
171  } else {
172  cs_ipvec(m->N->pinv, x, t, m->A.n) ; // t = P1\b
173  cs_lsolve(m->N->L, t) ; // t = L\t
174  cs_usolve(m->N->U, t) ; // t = U\t
175  cs_ipvec(m->S->q, t, x, m->A.n) ; // x = P2\t
176  }
177  x += ncol();
178  }
179  return 0;
180  }
181 
182  double CsparseInterface::det(void* mem, const double* A) const {
196  auto m = static_cast<CsparseMemory*>(mem);
197 
198  // Determinant of L
199  double detL = 1;
200  for (int i=0; i<m->N->L->n; ++i) {
201  for (int ii=m->N->L->p[i]; ii < m->N->L->p[i+1]; ++ii) {
202  if (i == m->N->L->i[ii]) detL *= m->N->L->x[ii];
203  }
204  }
205 
206  // Determinant of U
207  double detU = 1;
208  for (int i=0; i<m->N->U->n; ++i) {
209  for (int ii=m->N->U->p[i]; ii < m->N->U->p[i+1]; ++ii) {
210  if (i == m->N->U->i[ii]) detU *= m->N->U->x[ii];
211  }
212  }
213 
214  return detL*detU;
215  }
216 
217 } // namespace casadi
Casadi exception class.
Definition: exception.hpp:77
static const std::string meta_doc
A documentation string.
int init_mem(void *mem) const override
Initalize memory block.
int sfact(void *mem, const double *A) const override
int solve(void *mem, const double *A, double *x, casadi_int nrhs, bool tr) const override
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize with type disambiguation.
void init(const Dict &opts) override
Initialize.
CsparseInterface(const std::string &name, const Sparsity &sp)
static LinsolInternal * creator(const std::string &name, const Sparsity &sp)
Create a new LinsolInternal.
int nfact(void *mem, const double *A) const override
Numeric factorization.
double det(void *mem, const double *A) const override
Determinant.
casadi_int size2() const
Get the second dimension (i.e. number of columns)
const casadi_int * colind() const
void init(const Dict &opts) override
Initialize.
const casadi_int * row() const
casadi_int nnz() const
casadi_int nrow() const
Get sparsity pattern.
casadi_int ncol() const
int init_mem(void *mem) const override
Initalize memory block.
void print_sparse(std::ostream &stream, bool truncate=true) const
Print sparse matrix style.
const Sparsity & sparsity() const
Const access the sparsity - reference to data member.
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
bool verbose_
Verbose printout.
static const Options options_
Options.
void clear_mem()
Clear all memory (called from destructor)
void disp(std::ostream &stream, bool more=false) const
Print a description of the object.
General sparsity class.
Definition: sparsity.hpp:106
bool is_singular() const
Check whether the sparsity-pattern indicates structural singularity.
Definition: sparsity.cpp:1315
The casadi namespace.
Definition: archiver.cpp:28
void copy_vector(const std::vector< S > &s, std::vector< D > &d)
int CASADI_LINSOL_CSPARSE_EXPORT casadi_register_linsol_csparse(LinsolInternal::Plugin *plugin)
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.
Matrix< double > DM
Definition: dm_fwd.hpp:33
void CASADI_LINSOL_CSPARSE_EXPORT casadi_load_linsol_csparse()
std::ostream & uout()