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