dm_instantiator.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 #define CASADI_DM_INSTANTIATOR_CPP
26 #include "matrix_impl.hpp"
27 
28 #include "blas_impl.hpp"
29 #include "filesystem_impl.hpp"
30 namespace casadi {
31 
32 
33  template<>
34  DM CASADI_EXPORT DM::
35  solve(const DM& A, const DM& b,
36  const std::string& lsolver, const Dict& dict) {
37  Linsol mysolver("tmp_solve", lsolver, A.sparsity(), dict);
38  return mysolver.solve(A, b, false);
39  }
40 
41  template<>
42  DM CASADI_EXPORT DM::
43  inv(const DM& A,
44  const std::string& lsolver, const Dict& dict) {
45  return solve(A, DM::eye(A.size1()), lsolver, dict);
46  }
47 
48  template<>
49  DM CASADI_EXPORT DM::
50  det(const DM& A, const std::string& lsolver, const Dict& dict) {
51  Linsol mysolver("tmp_det", lsolver, A.sparsity(), dict);
52  return mysolver.det(A);
53  }
54 
55  template<>
56  DM CASADI_EXPORT DM::
57  pinv(const DM& A, const std::string& lsolver,
58  const Dict& dict) {
59  if (A.size1()>=A.size2()) {
60  return solve(mtimes(A.T(), A), A.T(), lsolver, dict);
61  } else {
62  return solve(mtimes(A, A.T()), A, lsolver, dict).T();
63  }
64  }
65 
66  template<>
67  DM CASADI_EXPORT DM::
68  rand(const Sparsity& sp) { // NOLINT(runtime/threadsafe_fn)
69  // C++11 random number generator
70  std::uniform_real_distribution<double> distribution(0., 1.);
71  // Nonzeros
72  std::vector<double> nz(sp.nnz());
73  for (double& e : nz) e = distribution(rng_);
74  // Construct return object
75  return DM(sp, nz, false);
76  }
77 
78  template<>
79  DM CASADI_EXPORT DM::
80  expm(const DM& A) {
81  Function ret = expmsol("mysolver", "slicot", A.sparsity());
82  return ret(std::vector<DM>{A, 1})[0];
83  }
84 
85  template<>
86  DM CASADI_EXPORT DM::
87  expm_const(const DM& A, const DM& t) {
88  return expm(A*t);
89  }
90 
91  template<>
92  DM CASADI_EXPORT DM::
93  _logsumexp(const DM& A) {
94  return casadi_logsumexp(A.ptr(), A.numel());
95  }
96 
97  template<>
98  std::vector<DM> CASADI_EXPORT DM::
99  cse(const std::vector<DM>& e) {
100  return e;
101  }
102 
103  template<>
104  std::vector<double> CASADI_EXPORT DM::
105  call(const Function& f, const std::vector<double>& dep) {
106  casadi_error("Not implemented");
107  }
108 
109  template<> void CASADI_EXPORT DM::export_code(const std::string& lang,
110  std::ostream &stream, const Dict& options) const {
111 
112  casadi_assert(lang=="matlab", "Only matlab language supported for now.");
113 
114  // Default values for options
115  bool opt_inline = false;
116  std::string name = "m";
117  casadi_int indent_level = 0;
118  bool spoof_zero = false;
119 
120  // Read options
121  for (auto&& op : options) {
122  if (op.first=="inline") {
123  opt_inline = op.second;
124  } else if (op.first=="name") {
125  name = op.second.to_string();
126  } else if (op.first=="indent_level") {
127  indent_level = op.second;
128  } else if (op.first=="spoof_zero") {
129  spoof_zero = op.second;
130  } else {
131  casadi_error("Unknown option '" + op.first + "'.");
132  }
133  }
134 
135  // Construct indent string
136  std::string indent;
137  for (casadi_int i=0;i<indent_level;++i) {
138  indent += " ";
139  }
140 
141  casadi_assert(!opt_inline, "Inline not supported for now.");
142 
143  // Prepare stream for emitting full precision
144  std::ios_base::fmtflags fmtfl = stream.flags();
145  stream << std::scientific << std::setprecision(std::numeric_limits<double>::digits10 + 1);
146 
147  // Obtain nonzeros of matrix
148  std::vector<double> d = nonzeros();
149 
150  // Spoof numericals
151  if (spoof_zero) {
152  for (double& e : d) {
153  if (e==0) e=1e-200;
154  }
155  }
156 
157  // Short-circuit for (dense) scalars
158  if (is_scalar(true)) {
159  stream << indent << name << " = " << d[0] << ";" << std::endl;
160  stream.flags(fmtfl);
161  return;
162  }
163 
164  // Are all nonzeros equal?
165  bool all_equal = true;
166  for (double e : d) {
167  if (e!=d[0]) {
168  all_equal = false;
169  break;
170  }
171  }
172 
173  if (all_equal && !d.empty()) {
174  // No need to export all individual nonzeros if they are all equal
175  stream << indent << name << "_nz = ones(1, " << d.size() << ")*" << d[0] << ";" << std::endl;
176  } else {
177  // Export nonzeros
178  stream << indent << name << "_nz = [";
179  for (casadi_int i=0;i<d.size();++i) {
180  stream << d[i] << " ";
181  if ((i+1)%20 == 0) stream << "..." << std::endl << indent << " ";
182  }
183  stream << "];" << std::endl;
184  }
185 
186  // Reset stream properties
187  stream.flags(fmtfl);
188 
189  // Cast nonzeros in correct shape
190  if (is_dense()) {
191  // Special case for dense (for readibility of exported code)
192  stream << indent << name << " = reshape(";
193  stream << name << "_nz, ";
194  stream << size1() << ", " << size2() << ");" << std::endl;
195  } else {
196  // For sparse matrices, export Sparsity and use sparse constructor
197  Dict opts;
198  opts["as_matrix"] = false;
199  opts["indent_level"] = indent_level;
200  opts["name"] = name;
201  opts["indent_level"] = opt_inline;
202  sparsity().export_code(lang, stream, opts);
203  stream << indent << name << " = sparse(" << name << "_i, " << name << "_j, ";
204  stream << name << "_nz, ";
205  stream << size1() << ", " << size2() << ");" << std::endl;
206  }
207  }
208 
209  template<>
210  Dict CASADI_EXPORT DM::info() const {
211  return {{"sparsity", sparsity().info()}, {"data", nonzeros()}};
212  }
213 
214  template<>
215  void CASADI_EXPORT DM::to_file(const std::string& filename,
216  const Sparsity& sp, const double* nonzeros,
217  const std::string& format_hint) {
218  std::string format = Sparsity::file_format(filename, format_hint, {"mtx", "txt"});
219  auto out_ptr = Filesystem::ofstream_ptr(filename);
220  std::ostream& out = *out_ptr;
221  if (format=="mtx") {
222  normalized_setup(out);
223  out << "%%MatrixMarket matrix coordinate real general" << std::endl;
224  out << sp.size1() << " " << sp.size2() << " " << sp.nnz() << std::endl;
225  std::vector<casadi_int> row = sp.get_row();
226  std::vector<casadi_int> col = sp.get_col();
227 
228  for (casadi_int k=0;k<row.size();++k) {
229  out << row[k]+1 << " " << col[k]+1 << " ";
230  normalized_out(out, nonzeros ? nonzeros[k]: casadi::nan);
231  out << std::endl;
232  }
233  } else if (format=="txt") {
234  normalized_setup(out);
235  out << std::left;
236  // Access data structures
237  casadi_int size1 = sp.size1();
238  casadi_int size2 = sp.size2();
239  const casadi_int* colind = sp.colind();
240  const casadi_int* row = sp.row();
241 
242  // Index counter for each column
243  std::vector<casadi_int> ind(colind, colind+size2+1);
244 
245  // Make enough room to put -3.3e-310
246  casadi_int w = std::numeric_limits<double>::digits10 + 9;
247 
248  // Loop over rows
249  for (casadi_int rr=0; rr<size1; ++rr) {
250  // Loop over columns
251  for (casadi_int cc=0; cc<size2; ++cc) {
252  // Set filler execptfor last column
253  if (cc<size2-1) out << std::setw(w);
254  // String representation of element
255  if (ind[cc]<colind[cc+1] && row[ind[cc]]==rr) {
256  normalized_out(out, nonzeros ? nonzeros[ind[cc]++]: casadi::nan);
257  } else {
258  out << std::setw(w) << "00";
259  }
260  if (cc<size2-1) out << " ";
261  }
262  out << std::endl;
263  }
264  } else {
265  casadi_error("Unknown format '" + format + "'");
266  }
267  }
268 
269  template<>
270  DM CASADI_EXPORT DM::from_file(const std::string& filename, const std::string& format_hint) {
271  std::string format = Sparsity::file_format(filename, format_hint, {"mtx", "txt"});
272  auto in_ptr = Filesystem::ifstream_ptr(filename);
273  std::istream& in = *in_ptr;
274 
275  if (format=="txt") {
276  std::string line;
277  std::vector<double> values;
278  casadi_int n_row = 0;
279  casadi_int n_col = 0;
280  bool first_line = true;
281  std::istringstream stream;
282 
283  std::vector<casadi_int> row;
284  std::vector<casadi_int> col;
285 
286  normalized_setup(stream);
287 
288  // Read line-by-line
289  while (std::getline(in, line)) {
290  // Ignore empty lines
291  if (line.empty()) continue;
292 
293  // Ignore lines with comments
294  if (line[0]=='%' || line[0]=='#' || line[0]=='/') continue;
295 
296  // Populate a stream for pulling doubles
297  stream.clear();
298  stream.str(line);
299 
300  // Keep pulling doubles from line
301  double val;
302  casadi_int i=0;
303  for (i=0; !stream.eof(); ++i) {
304  casadi_int start = stream.tellg();
305  int ret = normalized_in(stream, val);
306 
307  if (ret==-1) break; // EOL reached
308  casadi_assert(ret==0, "Parsing error on line " + str(i+1) + ", column " + str(start+1));
309  casadi_int stop = line.size();
310  if (!stream.eof()) stop = stream.tellg();
311 
312  // Check if structural zero
313  bool structural_zero = false;
314  if (val==0) {
315  // Check if stream contained '00'
316  casadi_int n_zeros = 0;
317  for (casadi_int k=start;k<stop;++k) {
318  char c = line.at(k);
319  if (c==' ' || c=='\t') continue;
320  if (c=='0') {
321  n_zeros++;
322  } else {
323  break;
324  }
325  }
326  if (n_zeros==2) structural_zero = true;
327  }
328 
329  if (!structural_zero) {
330  row.push_back(n_row);
331  col.push_back(i);
332  values.push_back(val);
333  }
334  if (first_line) n_col++;
335  }
336 
337  // Dimension check
338  casadi_assert(i==n_col, "Inconsistent dimensions. "
339  "File started with " + str(n_col) + ", while line " + str(n_row+1) +
340  " has " + str(i) + ".");
341 
342  first_line = false;
343  n_row++;
344  }
345  return DM::triplet(row, col, values, n_row, n_col);
346  } else if (format=="mtx") {
347  std::string line;
348  bool first_line = true;
349  std::istringstream stream;
350 
351  casadi_int n_row=0, n_col=0, nnz=0;
352  std::vector<double> values;
353  std::vector<casadi_int> row, col;
354  normalized_setup(stream);
355 
356  // Read line-by-line
357  while (std::getline(in, line)) {
358  // Ignore empty lines
359  if (line.empty()) continue;
360 
361  // Ignore lines with comments
362  if (line[0]=='%' || line[0]=='#' || line[0]=='/') continue;
363 
364  // Populate a stream for pulling doubles
365  stream.clear();
366  stream.str(line);
367 
368  if (first_line) {
369  stream >> n_row;
370  stream >> n_col;
371  stream >> nnz;
372  casadi_assert(!stream.fail(), "Could not parse first line");
373  values.reserve(nnz);
374  row.reserve(nnz);
375  col.reserve(nnz);
376  first_line = false;
377 
378  } else {
379  casadi_int r, c;
380  double val;
381  stream >> r;
382  stream >> c;
383  casadi_assert(normalized_in(stream, val)==0, "Parse error");
384  row.push_back(r-1);
385  col.push_back(c-1);
386  values.push_back(val);
387  }
388  }
389  return DM::triplet(row, col, values, n_row, n_col);
390  } else {
391  casadi_error("Unknown format '" + format + "'");
392  }
393  }
394 
395  // double specialization of mtimes_dense_dispatch (declared in
396  // matrix_impl.hpp). Routes the dense fast path of Matrix<double>::mac
397  // through the BLAS plugin: shorthand 0 (reference) inlines
398  // casadi_mtimes_dense; external plugins (classic, blasfeo, ...) call
399  // dgemm via dispatch_.
400  template<>
401  void CASADI_EXPORT mtimes_dense_dispatch<double>(
402  const std::string& blas,
403  const double* A, casadi_int m, casadi_int k,
404  const double* B, casadi_int n, double* C) {
405  Blas::mtimes(Blas::shorthand_for(blas), A, m, k, B, n, C);
406  }
407 
408  // Instantiate templates
409  template class CASADI_EXPORT casadi_limits<double>;
410  #if __GNUC__
411  #pragma GCC diagnostic push
412  #pragma GCC diagnostic ignored "-Wattributes"
413  #endif
414  template class CASADI_EXPORT Matrix<double>;
415  #if __GNUC__
416  #pragma GCC diagnostic pop
417  #endif
418 
419 } // namespace casadi
static Matrix< double > solve(const Matrix< double > &A, const Matrix< double > &b)
casadi_limits class
Function expmsol(const std::string &name, const std::string &solver, const Sparsity &A, const Dict &opts)
Definition: expm.cpp:44
The casadi namespace.
Definition: archiver.cpp:28
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
void normalized_setup(std::istream &stream)
const double nan
Not a number.
Definition: calculus.hpp:53
Matrix< double > DM
Definition: dm_fwd.hpp:33
int normalized_in(std::istream &stream, double &ret)
void normalized_out(std::ostream &stream, double val)