convexify.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 "convexify.hpp"
27 
28 namespace casadi {
29 
30  std::string strategy_to_string(casadi_convexify_strategy_t s) {
31  switch (s) {
32  case CVX_REGULARIZE: return "regularize";
33  case CVX_EIGEN_REFLECT: return "eigen-reflect";
34  case CVX_EIGEN_CLIP: return "eigen-clip";
35  }
36  return "unknown";
37  }
38 
39  Convexify::Convexify(const MX& H, const Dict& opts) {
40  set_dep(H);
41  set_sparsity(setup(convexify_data_, H.sparsity(), opts, false));
42  }
43 
44  size_t Convexify::sz_iw() const {
45  return convexify_data_.sz_iw;
46  }
47 
48  size_t Convexify::sz_w() const {
49  return convexify_data_.sz_w;
50  }
51 
52  std::string Convexify::disp(const std::vector<std::string>& arg) const {
53  return "convexify(" + arg.at(0) + ")";
54  }
55 
56  void Convexify::eval_mx(const std::vector<MX>& arg, std::vector<MX>& res,
57  const std::vector<bool>& unique) const {
58  Dict options;
59  options["strategy"] = strategy_to_string(convexify_data_.config.strategy);
60  options["margin"] = convexify_data_.config.margin;
61  options["max_iter_eig"] = convexify_data_.config.max_iter_eig;
62  res[0] = convexify(arg[0], options);
63  }
64 
65  int Convexify::eval(const double** arg, double** res, casadi_int* iw, double* w) const {
66  int ret = casadi_convexify_eval(&convexify_data_.config, arg[0], res[0], iw, w);
67  casadi_assert(!ret, "Failure in convexification.");
68  return 0;
69  }
70 
72  const ConvexifyData &d,
73  const std::string& Hin, const std::string& Hout,
74  const std::string& iw, const std::string& w) {
75  g.local("cvx_config", "struct casadi_convexify_config");
76  if (d.config.strategy==CVX_REGULARIZE) {
77  g << "cvx_config.strategy = CVX_REGULARIZE;\n";
78  } else if (d.config.strategy==CVX_EIGEN_CLIP) {
79  g << "cvx_config.strategy = CVX_EIGEN_CLIP;\n";
80  } else if (d.config.strategy==CVX_EIGEN_REFLECT) {
81  g << "cvx_config.strategy = CVX_EIGEN_REFLECT;\n";
82  }
83  if (d.config.type_in==CVX_SYMM) {
84  g << "cvx_config.type_in = CVX_SYMM;\n";
85  } else if (d.config.type_in==CVX_TRIL) {
86  g << "cvx_config.type_in = CVX_TRIL;\n";
87  } else if (d.config.type_in==CVX_TRIU) {
88  g << "cvx_config.type_in = CVX_TRIU;\n";
89  }
90  g << "cvx_config.Hsp = " << g.sparsity(d.Hsp) << ";\n";
91  g << "cvx_config.Hrsp = " << g.sparsity(d.Hrsp) << ";\n";
92  g << "cvx_config.margin = " << d.config.margin << ";\n";
93  g << "cvx_config.Hsp_project = " << d.config.Hsp_project << ";\n";
94  g << "cvx_config.scc_transform = " << d.config.scc_transform << ";\n";
95  g << "cvx_config.scc_offset = " << g.constant(d.scc_offset) << ";\n";
96  g << "cvx_config.scc_mapping = " << g.constant(d.scc_mapping) << ";\n";
97  g << "cvx_config.scc_offset_size = " << d.scc_offset.size() << ";\n";
98  g << "cvx_config.max_iter_eig = " << d.config.max_iter_eig << ";\n";
99  g << "cvx_config.verbose = " << d.config.verbose << ";\n";
100  return "casadi_convexify_eval(&cvx_config, " + Hin + "," + Hout + "," + iw + "," + "w)";
101  }
102 
105  serialize(s, "", convexify_data_);
106  }
107 
110  }
111 
112  void Convexify::serialize(SerializingStream& s, const std::string& prefix,
113  const ConvexifyData& d) {
114  s.version(prefix + "Convexify", 1);
115  s.pack(prefix + "Convexify::type_in", static_cast<int>(d.config.type_in));
116  s.pack(prefix + "Convexify::strategy", static_cast<int>(d.config.strategy));
117  s.pack(prefix + "Convexify::margin", d.config.margin);
118  s.pack(prefix + "Convexify::max_iter_eig", d.config.max_iter_eig);
119  s.pack(prefix + "Convexify::scc_offset", d.scc_offset);
120  s.pack(prefix + "Convexify::scc_mapping", d.scc_mapping);
121  s.pack(prefix + "Convexify::Hsp_project", d.config.Hsp_project);
122  s.pack(prefix + "Convexify::scc_transform", d.config.scc_transform);
123  s.pack(prefix + "Convexify::verbose", d.config.verbose);
124  s.pack(prefix + "Convexify::Hsp", d.Hsp);
125  s.pack(prefix + "Convexify::Hrsp", d.Hrsp);
126  }
127 
128  void Convexify::deserialize(DeserializingStream& s, const std::string& prefix,
129  ConvexifyData& d) {
130  s.version(prefix + "Convexify", 1);
131  int type_in;
132  s.unpack(prefix + "Convexify::type_in", type_in);
133  d.config.type_in = static_cast<casadi_convexify_type_in_t>(type_in);
134  int strategy;
135  s.unpack(prefix + "Convexify::strategy", strategy);
136  d.config.strategy = static_cast<casadi_convexify_strategy_t>(strategy);
137  s.unpack(prefix + "Convexify::margin", d.config.margin);
138  s.unpack(prefix + "Convexify::max_iter_eig", d.config.max_iter_eig);
139  s.unpack(prefix + "Convexify::scc_offset", d.scc_offset);
140  s.unpack(prefix + "Convexify::scc_mapping", d.scc_mapping);
141  s.unpack(prefix + "Convexify::Hsp_project", d.config.Hsp_project);
142  s.unpack(prefix + "Convexify::scc_transform", d.config.scc_transform);
143  s.unpack(prefix + "Convexify::verbose", d.config.verbose);
144  s.unpack(prefix + "Convexify::Hsp", d.Hsp);
145  s.unpack(prefix + "Convexify::Hrsp", d.Hrsp);
146 
147 
148  d.config.scc_offset_size = d.scc_offset.size();
149 
150  // Set pointers
151  d.config.Hsp = d.Hsp;
152  d.config.Hrsp = d.Hrsp;;
155  }
156 
158  const std::vector<casadi_int>& arg,
159  const std::vector<casadi_int>& res,
160  const std::vector<bool>& arg_is_ref,
161  std::vector<bool>& res_is_ref) const {
162  std::string ret = g.convexify_eval(convexify_data_,
163  g.work(arg[0], dep(0).nnz(), arg_is_ref[0]), g.work(res[0], nnz(), false), "iw", "w");
164  g << "if (" << ret << ") return 1;\n";
165  }
166 
167  Sparsity Convexify::setup(ConvexifyData& d, const Sparsity& H, const Dict& opts, bool inplace) {
168  // Validate and categorize matrix input sparsity
169  casadi_assert(H.is_square(), "Convexify ");
170  if (H.is_symmetric()) {
171  d.config.type_in = CVX_SYMM;
172  } else if (H.is_tril()) {
173  d.config.type_in = CVX_TRIL;
174  } else if (H.is_triu()) {
175  d.config.type_in = CVX_TRIU;
176  } else {
177  casadi_error("Convexify operation requires symmetric or triangular input");
178  }
179 
180  // Read options
181  d.config.margin = 1e-7;
182  d.config.max_iter_eig = 200;
183  std::string strategy = "eigen-clip";
184  d.config.verbose = false;
185 
186  for (auto&& op : opts) {
187  if (op.first=="strategy") {
188  strategy = op.second.to_string();
189  } else if (op.first=="margin") {
190  d.config.margin = op.second;
191  casadi_assert(d.config.margin>=0, "Margin must be >=0");
192  } else if (op.first=="max_iter_eig") {
193  d.config.max_iter_eig = op.second;
194  } else if (op.first=="verbose") {
195  d.config.verbose = op.second;
196  } else {
197  casadi_error("Unknown option '" + op.first + "'.");
198  }
199  }
200 
201  // Interpret strategy
202  if (strategy=="regularize") {
203  d.config.strategy = CVX_REGULARIZE;
204  casadi_assert(d.config.type_in==CVX_SYMM, "Only truly symmetric matrices supported");
205  } else if (strategy=="eigen-reflect") {
206  d.config.strategy = CVX_EIGEN_REFLECT;
207  } else if (strategy=="eigen-clip") {
208  d.config.strategy = CVX_EIGEN_CLIP;
209  } else {
210  casadi_error("Invalid convexify strategy. "
211  "Choose from regularize|eigen-reflect|eigen-clip. Got '" + strategy + "'.");
212  }
213 
214  d.Hrsp = H;
215 
216  d.config.scc_transform = 0;
217 
218  Sparsity Hrsp = H+H.T();
219 
220  casadi_int block_size = 0;
221  Sparsity& Hsp = d.Hsp;
222  if (d.config.strategy==CVX_EIGEN_REFLECT || d.config.strategy==CVX_EIGEN_CLIP) {
223  // Uncover strongly connected components
224  std::vector<casadi_int> scc_index;
225  casadi_int scc_nb = Hrsp.scc(scc_index, d.scc_offset);
226 
227  // Represent Hessian as block-dense in permuted space
228  std::vector<Sparsity> sp;
229  for (casadi_int i=0;i<scc_nb;++i) {
230  casadi_int block = d.scc_offset.at(i+1)-d.scc_offset.at(i);
231  Sparsity stencil;
232  if (d.config.type_in==CVX_SYMM) {
233  stencil = Sparsity::dense(block, block);
234  } else if (d.config.type_in==CVX_TRIL) {
235  stencil = Sparsity::lower(block);
236  } else {
237  stencil = Sparsity::upper(block);
238  }
239  sp.push_back(stencil);
240  }
241 
242  std::vector<casadi_int> ssc_perm = lookupvector(scc_index);
243  std::vector<casadi_int> mapping_dummy;
244  Hsp = diagcat(sp).sub(ssc_perm, ssc_perm, mapping_dummy);
245  Hsp.sub(scc_index, scc_index, d.scc_mapping);
246 
247  // Find out size of maximum block
248  for (casadi_int i=0;i<scc_nb;++i) {
249  casadi_int block = d.scc_offset.at(i+1)-d.scc_offset.at(i);
250  if (block>block_size) block_size = block;
251  }
252 
254 
255  if (d.config.verbose) casadi_message("Identified " + str(scc_nb) + " blocks "
256  "with maximum size " + str(block_size) + ".");
257  } else if (d.config.strategy==CVX_REGULARIZE) {
258  Hsp = Hrsp + Sparsity::diag(H.size1());
259  } else {
260  Hsp = Hrsp;
261  }
262  d.config.Hsp_project = Hsp!=Hrsp;
263 
264  if (d.config.type_in==CVX_TRIL) {
265  Hsp = Sparsity::tril(Hsp);
266  } else if (d.config.type_in==CVX_TRIU) {
267  Hsp = Sparsity::triu(Hsp);
268  }
269 
270  d.sz_iw = 0;
271  if (d.config.strategy==CVX_EIGEN_REFLECT || d.config.strategy==CVX_EIGEN_CLIP) {
272  d.sz_iw = 1+3* d.config.max_iter_eig;
273  }
274 
275  d.sz_w = 0;
276  if (d.config.strategy==CVX_EIGEN_REFLECT || d.config.strategy==CVX_EIGEN_CLIP) {
277  d.sz_w = std::max(block_size, 2*(block_size-1)*d.config.max_iter_eig);
278  if (d.config.Hsp_project) d.sz_w = std::max(d.sz_w, Hsp.size1());
279  if (d.config.scc_transform) d.sz_w += block_size*block_size;
280  if (inplace) d.sz_w = std::max(d.sz_w, Hsp.size1()+d.Hrsp.nnz());
281  }
282  d.sz_w = Hsp.size1()+d.sz_w;
283 
284  d.config.scc_offset_size = d.scc_offset.size();
285 
286  // Set pointers
287  d.config.Hsp = Hsp;
288  d.config.Hrsp = d.Hrsp;
291 
292  return Hsp;
293  }
294 
295 } // namespace casadi
Helper class for C code generation.
std::string work(casadi_int n, casadi_int sz, bool is_ref) const
std::string constant(const std::vector< casadi_int > &v)
Represent an array constant; adding it when new.
void local(const std::string &name, const std::string &type, const std::string &ref="")
Declare a local variable.
std::string convexify_eval(const ConvexifyData &d, const std::string &Hin, const std::string &Hout, const std::string &iw, const std::string &w)
convexify
std::string sparsity(const Sparsity &sp, bool canonical=true)
void eval_mx(const std::vector< MX > &arg, std::vector< MX > &res, const std::vector< bool > &unique={}) const override
Evaluate symbolically (MX)
Definition: convexify.cpp:56
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: convexify.cpp:103
static void serialize(SerializingStream &s, const std::string &prefix, const ConvexifyData &d)
Definition: convexify.cpp:112
std::string disp(const std::vector< std::string > &arg) const override
Print expression.
Definition: convexify.cpp:52
struct ConvexifyData convexify_data_
Definition: convexify.hpp:107
size_t sz_w() const override
Get required length of w field.
Definition: convexify.cpp:48
size_t sz_iw() const override
Get required length of iw field.
Definition: convexify.cpp:44
casadi_int op() const override
Get the operation.
Definition: convexify.hpp:95
void generate(CodeGenerator &g, const std::vector< casadi_int > &arg, const std::vector< casadi_int > &res, const std::vector< bool > &arg_is_ref, std::vector< bool > &res_is_ref) const override
Generate code for the operation.
Definition: convexify.cpp:157
Convexify(const MX &H, const Dict &opts=Dict())
Constructor.
Definition: convexify.cpp:39
int eval(const double **arg, double **res, casadi_int *iw, double *w) const override
Evaluate the function numerically.
Definition: convexify.cpp:65
static Sparsity setup(ConvexifyData &d, const Sparsity &H, const Dict &opts=Dict(), bool inplace=true)
Definition: convexify.cpp:167
static MXNode * deserialize(DeserializingStream &s)
Deserialize without type information.
Definition: convexify.hpp:105
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
Node class for MX objects.
Definition: mx_node.hpp:51
casadi_int nnz(casadi_int i=0) const
Definition: mx_node.hpp:427
const MX & dep(casadi_int ind=0) const
dependencies - functions that have to be evaluated before this one
Definition: mx_node.hpp:392
virtual void serialize_body(SerializingStream &s) const
Serialize an object without type information.
Definition: mx_node.cpp:530
void set_sparsity(const Sparsity &sparsity)
Set the sparsity.
Definition: mx_node.cpp:224
void set_dep(const MX &dep)
Set unary dependency.
Definition: mx_node.cpp:228
MX - Matrix expression.
Definition: mx.hpp:92
const Sparsity & sparsity() const
Get the sparsity pattern.
Definition: mx.cpp:612
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
General sparsity class.
Definition: sparsity.hpp:106
static Sparsity upper(casadi_int n)
Create a upper triangular square sparsity pattern *.
Definition: sparsity.cpp:1044
Sparsity sub(const std::vector< casadi_int > &rr, const std::vector< casadi_int > &cc, std::vector< casadi_int > &mapping, bool ind1=false) const
Get a submatrix.
Definition: sparsity.cpp:334
casadi_int size1() const
Get the number of rows.
Definition: sparsity.cpp:124
static Sparsity diag(casadi_int nrow)
Create diagonal sparsity pattern *.
Definition: sparsity.hpp:190
casadi_int scc(std::vector< casadi_int > &index, std::vector< casadi_int > &offset) const
Find the strongly connected components of the bigraph defined by the sparsity pattern.
Definition: sparsity.cpp:705
static Sparsity dense(casadi_int nrow, casadi_int ncol=1)
Create a dense rectangular sparsity pattern *.
Definition: sparsity.cpp:1028
static Sparsity triu(const Sparsity &x, bool includeDiagonal=true)
Enlarge matrix.
Definition: sparsity.cpp:999
Sparsity T() const
Transpose the matrix.
Definition: sparsity.cpp:394
static Sparsity tril(const Sparsity &x, bool includeDiagonal=true)
Enlarge matrix.
Definition: sparsity.cpp:995
bool is_tril(bool strictly=false) const
Is lower triangular?
Definition: sparsity.cpp:321
casadi_int nnz() const
Get the number of (structural) non-zeros.
Definition: sparsity.cpp:148
static Sparsity lower(casadi_int n)
Create a lower triangular square sparsity pattern *.
Definition: sparsity.cpp:1065
bool is_square() const
Is square?
Definition: sparsity.cpp:293
bool is_triu(bool strictly=false) const
Is upper triangular?
Definition: sparsity.cpp:325
bool is_symmetric() const
Is symmetric?
Definition: sparsity.cpp:317
The casadi namespace.
Definition: archiver.cpp:28
std::vector< casadi_int > range(casadi_int start, casadi_int stop, casadi_int step, casadi_int len)
Range function.
std::string str(const T &v)
String representation, any type.
std::vector< casadi_int > lookupvector(const std::vector< casadi_int > &v, casadi_int size)
Returns a vector for quickly looking up entries of supplied list.
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.
std::string strategy_to_string(casadi_convexify_strategy_t s)
Definition: convexify.cpp:30
std::vector< casadi_int > scc_offset
Definition: mx.hpp:57
casadi_convexify_config< double > config
Definition: mx.hpp:61
casadi_int sz_iw
Definition: mx.hpp:62
Sparsity Hrsp
Definition: mx.hpp:59
casadi_int sz_w
Definition: mx.hpp:63
Sparsity Hsp
Definition: mx.hpp:60
std::vector< casadi_int > scc_mapping
Definition: mx.hpp:57
casadi_int max_iter_eig
For eigen-* convexification strategies: maximum iterations for symmetric Schur decomposition.
casadi_convexify_strategy_t strategy
const casadi_int * Hsp
casadi_convexify_type_in_t type_in
const casadi_int * Hrsp
const casadi_int * scc_offset
Block structure of Hessian for certain convexification methods.
const casadi_int * scc_mapping