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 "dple_impl.hpp"
27 #include <typeinfo>
28 
29 namespace casadi {
30 
31  bool has_dple(const std::string& name) {
32  return Dple::has_plugin(name);
33  }
34 
35  void load_dple(const std::string& name) {
36  Dple::load_plugin(name);
37  }
38 
39  std::string doc_dple(const std::string& name) {
40  return Dple::getPlugin(name).doc;
41  }
42 
43  MX dplesol(const MX& A, const MX& V, const std::string& solver, const Dict& opts) {
44  SpDict sp;
45  sp["a"] = A.sparsity();
46  sp["v"] = V.sparsity();
47  Function f = dplesol("dplesol", solver, sp, opts);
48  MXDict f_in;
49  f_in["a"] = A;
50  f_in["v"] = V;
51  MXDict f_out = f(f_in);
52  return f_out["p"];
53  }
54 
55  CASADI_EXPORT MXVector dplesol(const MXVector& A, const MXVector& V, const std::string& solver,
56  const Dict& opts) {
57  casadi_assert(A.size()==V.size(),
58  "dplesol: sizes of A vector (" + str(A.size()) + ") and V vector "
59  "(" + str(V.size()) + ") must match.");
60  std::vector<MX> Adense, Vdense;
61 
62  for (casadi_int i=0;i<A.size();++i) {
63  Adense.push_back(densify(A[i]));
64  Vdense.push_back(densify(V[i]));
65  }
66 
67  MX ret = dplesol(diagcat(Adense), diagcat(Vdense), solver, opts);
68  return diagsplit(ret, ret.size1()/A.size());
69  }
70 
71  CASADI_EXPORT DMVector dplesol(const DMVector& A, const DMVector& V, const std::string& solver,
72  const Dict& opts) {
73  casadi_assert(A.size()==V.size(),
74  "dplesol: sizes of A vector (" + str(A.size()) + ") and V vector "
75  "(" + str(V.size()) + ") must match.");
76  std::vector<DM> Adense, Vdense;
77 
78  for (casadi_int i=0;i<A.size();++i) {
79  Adense.push_back(densify(A[i]));
80  Vdense.push_back(densify(V[i]));
81  }
82 
83  DM Afull = diagcat(Adense);
84  DM Vfull = diagcat(Vdense);
85 
86  SpDict sp;
87  sp["a"] = Afull.sparsity();
88  sp["v"] = Vfull.sparsity();
89  Function f = dplesol("dplesol", solver, sp, opts);
90  DMDict f_in;
91  f_in["a"] = Afull;
92  f_in["v"] = Vfull;
93  DMDict f_out = f(f_in);
94  return diagsplit(f_out["p"], f_out["p"].size1()/A.size());
95  }
96 
97  Function dplesol(const std::string& name, const std::string& solver,
98  const SpDict& st, const Dict& opts) {
99  return Function::create(Dple::instantiate(name, solver, st), opts);
100  }
101 
102  std::vector<std::string> dple_in() {
103  std::vector<std::string> ret(dple_n_in());
104  for (size_t i=0; i<ret.size(); ++i) ret[i]=dple_in(i);
105  return ret;
106  }
107 
108  std::vector<std::string> dple_out() {
109  std::vector<std::string> ret(dple_n_out());
110  for (size_t i=0; i<ret.size(); ++i) ret[i]=dple_out(i);
111  return ret;
112  }
113 
114  std::string dple_in(casadi_int ind) {
115  switch (static_cast<DpleInput>(ind)) {
116  case DPLE_A: return "a";
117  case DPLE_V: return "v";
118  case DPLE_NUM_IN: break;
119  }
120  return std::string();
121  }
122 
123  std::string dple_out(casadi_int ind) {
124  switch (static_cast<DpleOutput>(ind)) {
125  case DPLE_P: return "p";
126  case DPLE_NUM_OUT: break;
127  }
128  return std::string();
129  }
130 
131  casadi_int dple_n_in() {
132  return DPLE_NUM_IN;
133  }
134 
135  casadi_int dple_n_out() {
136  return DPLE_NUM_OUT;
137  }
138 
139  // Constructor
140  Dple::Dple(const std::string& name, const SpDict &st)
141  : FunctionInternal(name) {
142  for (auto i=st.begin(); i!=st.end(); ++i) {
143  if (i->first=="a") {
144  A_ = i->second;
145  } else if (i->first=="v") {
146  V_ = i->second;
147  } else {
148  casadi_error("Unrecognized field in Dple structure: " + str(i->first));
149  }
150  }
151 
152  }
153 
155  switch (static_cast<DpleInput>(i)) {
156  case DPLE_A:
157  return A_;
158  case DPLE_V:
159  return V_;
160  case DPLE_NUM_IN: break;
161  }
162  return Sparsity();
163  }
164 
166  switch (static_cast<DpleOutput>(i)) {
167  case DPLE_P:
168  return V_;
169  case DPLE_NUM_OUT: break;
170  }
171  return Sparsity();
172  }
173 
174  const Options Dple::options_
176  {{"const_dim",
177  {OT_BOOL,
178  "Assume constant dimension of P"}},
179  {"pos_def",
180  {OT_BOOL,
181  "Assume P positive definite"}},
182  {"error_unstable",
183  {OT_BOOL,
184  "Throw an exception when it is detected that Product(A_i, i=N..1)"
185  "has eigenvalues greater than 1-eps_unstable"}},
186  {"eps_unstable",
187  {OT_DOUBLE,
188  "A margin for unstability detection"}}
189  }
190  };
191 
192  void Dple::init(const Dict& opts) {
193  // Call the init method of the base class
195 
196  // Default options
197  const_dim_ = true;
198  pos_def_ = false;
199  error_unstable_ = false;
200  eps_unstable_ = 1e-4;
201 
202  // Read options
203  for (auto&& op : opts) {
204  if (op.first=="const_dim") {
205  const_dim_ = op.second;
206  } else if (op.first=="pos_def") {
207  pos_def_ = op.second;
208  } else if (op.first=="error_unstable") {
209  error_unstable_ = op.second;
210  } else if (op.first=="eps_unstable") {
211  eps_unstable_ = op.second;
212  }
213  }
214 
215  casadi_assert_dev(V_.size2() % V_.size1() == 0);
216  nrhs_ = V_.size2() / V_.size1();
217  casadi_assert_dev(nrhs_>=1);
218 
219  std::vector<Sparsity> Vs = horzsplit(V_, V_.size1());
220  Sparsity Vref = Vs[0];
221  casadi_assert(Vref.is_symmetric(),
222  "V must be symmetric but got " + Vref.dim() + ".");
223 
224  for (auto&& s : Vs)
225  casadi_assert_dev(s==Vref);
226 
227  casadi_assert(const_dim_, "Not implemented");
228 
229  casadi_int blocksize = Vref.colind()[1];
230  K_ = Vref.size1()/blocksize;
231  Sparsity block = Sparsity::dense(blocksize, blocksize);
232 
233  std::vector<Sparsity> blocks(K_, block);
234  casadi_assert(Vref==diagcat(blocks), "Structure not recognised.");
235  casadi_assert(A_==Vref, "Structure not recognised.");
236 
237 
238  }
239 
240  Function Dple::get_forward(casadi_int nfwd, const std::string& name,
241  const std::vector<std::string>& inames,
242  const std::vector<std::string>& onames,
243  const Dict& opts) const {
244  // Symbolic A
245  MX A = MX::sym("A", A_);
246  Function Vdotf;
247  {
248  MX P = MX::sym("P", A_);
249  MX Adot = MX::sym("P", A_);
250  MX Vdot = MX::sym("P", A_);
251 
252  MX temp = mtimes(std::vector<MX>{Adot, P, A.T()}) +
253  mtimes(std::vector<MX>{A, P, Adot.T()}) + Vdot;
254  Vdotf = Function("PAVbar", {A, P, Adot, Vdot},
255  { (temp+temp.T())/2});
256  }
257 
258  MX P = MX::sym("P", V_);
259  MX Adot = MX::sym("Adot", repmat(A_, 1, nfwd));
260  MX Vdot = MX::sym("Vdot", repmat(V_, 1, nfwd));
261  MX Qdot = Vdotf.map("map", "serial", nrhs_, {0, 2}, std::vector<casadi_int>{})
262  .map("map", "serial", nfwd, {0, 1}, std::vector<casadi_int>{})({A, P, Adot, Vdot})[0];
263  MX Pdot = dplesol(A, Qdot, plugin_name(), opts);
264  MX V = MX::sym("V", Sparsity(size_in(DPLE_V))); // We dont need V
265 
266  Dict options;
267  options["allow_duplicate_io_names"] = true;
268  return Function(name, {A, V, P, Adot, Vdot}, {Pdot}, inames, onames, options);
269 
270  }
271 
272  Function Dple::get_reverse(casadi_int nadj, const std::string& name,
273  const std::vector<std::string>& inames,
274  const std::vector<std::string>& onames,
275  const Dict& opts) const {
276 
277  // Symbolic A
278  MX A = MX::sym("A", A_);
279 
280  // Helper function to reverse, reverse-tranpose,
281  // and reverse-symmetrize one block-diagonal matrix
282  casadi_int n = A_.size1()/K_;
283  std::vector<MX> ret = diagsplit(A, n);
284  std::reverse(ret.begin(), ret.end());
285  std::vector<MX> retT;
286  std::vector<MX> retS;
287  for (auto & e : ret) retT.push_back(e.T());
288  for (auto & e : ret) retS.push_back((e+e.T())/2);
289  Function revS = Function("revS", {A}, {diagcat(retS)});
290  Function revT = Function("revT", {A}, {diagcat(retT)});
291  Function rev = Function("rev", {A}, {diagcat(ret)});
292 
293  // Function to compute the formula for Abar
294  Function Abarf;
295  {
296  MX P = MX::sym("P", A_);
297  MX Vbar_rev = MX::sym("Vbar", A_);
298  MX A_rev = MX::sym("A", A_);
299 
300  Abarf = Function("PAVbar", {P, A_rev, Vbar_rev},
301  {2*revT(mtimes(std::vector<MX>{rev(P)[0], A_rev, Vbar_rev}))[0]});
302  }
303 
304  // original output
305  MX P = MX::sym("P", V_);
306 
307  // Symbolic reverse seed for P
308  MX Pbar = MX::sym("Pbar", repmat(V_, 1, nadj));
309  // Symmetrize the seed
310  MX Pbar_rev = revS.map(nrhs_).map(nadj)(Pbar)[0];
311 
312  // Reverse A for new dple
313  MX A_rev = revT(A)[0];
314 
315  // Solver a dple with nrhs*nadj right-hand sides
316  MX Vbar_rev = dplesol(A_rev, Pbar_rev, plugin_name(), opts);
317 
318  // Undo the reversal for Vbar
319  MX Vbar = rev.map(nrhs_).map(nadj)(Vbar_rev)[0];
320 
321  MX Abar = Abarf.map("map", "serial", nrhs_, std::vector<casadi_int>{1}, {0}).
322  map("map", "serial", nadj, {0, 1}, std::vector<casadi_int>{})(
323  {P, A_rev, Vbar_rev})[0];
324 
325  Dict options;
326  options["allow_duplicate_io_names"] = true;
327 
328  MX V = MX::sym("V", Sparsity(size_in(DPLE_V))); // We dont need V
329  return Function(name, {A, V, P, Pbar}, {Abar, Vbar}, inames, onames, options);
330  }
331 
333  }
334 
335  std::map<std::string, Dple::Plugin> Dple::solvers_;
336 
337 #ifdef CASADI_WITH_THREADSAFE_SYMBOLICS
338  std::mutex Dple::mutex_solvers_;
339 #endif // CASADI_WITH_THREADSAFE_SYMBOLICS
340 
341  const std::string Dple::infix_ = "dple";
342 
343 } // namespace casadi
Sparsity A_
List of sparsities of A_i.
Definition: dple_impl.hpp:125
Sparsity get_sparsity_in(casadi_int i) override
Sparsities of function inputs and outputs.
Definition: dple.cpp:154
static std::map< std::string, Plugin > solvers_
Collection of solvers.
Definition: dple_impl.hpp:110
double eps_unstable_
Margin for instability detection.
Definition: dple_impl.hpp:143
static const std::string infix_
Infix.
Definition: dple_impl.hpp:117
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
Function get_forward(casadi_int nfwd, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Generate a function that calculates nfwd forward derivatives.
Definition: dple.cpp:240
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
Dple(const std::string &name, const SpDict &st)
Definition: dple.cpp:140
Sparsity get_sparsity_out(casadi_int i) override
Sparsities of function inputs and outputs.
Definition: dple.cpp:165
Sparsity V_
List of sparsities of V_i.
Definition: dple_impl.hpp:128
~Dple() override=0
Definition: dple.cpp:332
Function get_reverse(casadi_int nadj, const std::string &name, const std::vector< std::string > &inames, const std::vector< std::string > &onames, const Dict &opts) const override
Generate a function that calculates nadj adjoint derivatives.
Definition: dple.cpp:272
bool pos_def_
Assume positive definiteness of P_i.
Definition: dple_impl.hpp:137
Internal class for Function.
void init(const Dict &opts) override
Initialize.
Function map(casadi_int n, const std::string &parallelization) const
Generate/retrieve cached serial map.
std::pair< casadi_int, casadi_int > size_in(casadi_int ind) const
Input/output dimensions.
static const Options options_
Options.
Function object.
Definition: function.hpp:60
static Function create(FunctionInternal *node)
Create from node.
Definition: function.cpp:336
Function map(casadi_int n, const std::string &parallelization="serial") const
Create a mapped version of this function.
Definition: function.cpp:709
casadi_int size1() const
Get the first dimension (i.e. number of rows)
static MX sym(const std::string &name, casadi_int nrow=1, casadi_int ncol=1)
Create an nrow-by-ncol symbolic primitive.
MX - Matrix expression.
Definition: mx.hpp:92
const Sparsity & sparsity() const
Get the sparsity pattern.
Definition: mx.cpp:592
MX T() const
Transpose the matrix.
Definition: mx.cpp:1029
const Sparsity & sparsity() const
Const access the sparsity - reference to data member.
static bool has_plugin(const std::string &pname, bool verbose=false)
Check if a plugin is available or can be loaded.
static Dple * instantiate(const std::string &fname, const std::string &pname, Problem problem)
static Plugin & getPlugin(const std::string &pname)
Load and get the creator function.
virtual const char * plugin_name() const=0
static Plugin load_plugin(const std::string &pname, bool register_plugin=true, bool needs_lock=true)
Load a plugin dynamically.
General sparsity class.
Definition: sparsity.hpp:106
casadi_int size1() const
Get the number of rows.
Definition: sparsity.cpp:124
std::string dim(bool with_nz=false) const
Get the dimension as a string.
Definition: sparsity.cpp:587
static Sparsity dense(casadi_int nrow, casadi_int ncol=1)
Create a dense rectangular sparsity pattern *.
Definition: sparsity.cpp:1012
casadi_int size2() const
Get the number of columns.
Definition: sparsity.cpp:128
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
Definition: sparsity.cpp:168
bool is_symmetric() const
Is symmetric?
Definition: sparsity.cpp:317
std::vector< std::string > dple_in()
Get input scheme of DPLE solvers.
Definition: dple.cpp:102
bool has_dple(const std::string &name)
Check if a particular plugin is available.
Definition: dple.cpp:31
MX dplesol(const MX &A, const MX &V, const std::string &solver, const Dict &opts)
Definition: dple.cpp:43
std::vector< std::string > dple_out()
Get output scheme of DPLE solvers.
Definition: dple.cpp:108
void load_dple(const std::string &name)
Explicitly load a plugin dynamically.
Definition: dple.cpp:35
casadi_int dple_n_in()
Get the number of QP solver inputs.
Definition: dple.cpp:131
casadi_int dple_n_out()
Get the number of QP solver outputs.
Definition: dple.cpp:135
std::string doc_dple(const std::string &name)
Get the documentation string for a plugin.
Definition: dple.cpp:39
The casadi namespace.
Definition: archiver.cpp:28
std::map< std::string, MX > MXDict
Definition: mx.hpp:1009
DpleInput
Input arguments of a dple solver [dpleIn].
Definition: dple.hpp:119
@ DPLE_NUM_IN
Definition: dple.hpp:124
@ 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
DpleOutput
Output arguments of a dple solver [dpleOut].
Definition: dple.hpp:128
@ DPLE_NUM_OUT
Number of arguments.
Definition: dple.hpp:132
@ DPLE_P
Lyapunov matrix (horzcat when const_dim, diagcat otherwise) (Cholesky of P if pos_def) [p].
Definition: dple.hpp:130
std::vector< MX > MXVector
Definition: mx.hpp:1006
std::string str(const T &v)
String representation, any type.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
std::vector< DM > DMVector
Definition: dm_fwd.hpp:34
std::map< std::string, Sparsity > SpDict
Definition: sparsity.hpp:1502
std::map< std::string, DM > DMDict
Definition: dm_fwd.hpp:36
Options metadata for a class.
Definition: options.hpp:40