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) const {
57  Dict options;
58  options["strategy"] = strategy_to_string(convexify_data_.config.strategy);
59  options["margin"] = convexify_data_.config.margin;
60  options["max_iter_eig"] = convexify_data_.config.max_iter_eig;
61  res[0] = convexify(arg[0], options);
62  }
63 
64  int Convexify::eval(const double** arg, double** res, casadi_int* iw, double* w) const {
65  int ret = convexify_eval(&convexify_data_.config, arg[0], res[0], iw, w);
66  casadi_assert(!ret, "Failure in convexification.");
67  return 0;
68  }
69 
71  const ConvexifyData &d,
72  const std::string& Hin, const std::string& Hout,
73  const std::string& iw, const std::string& w) {
74  g.local("cvx_config", "struct casadi_convexify_config");
75  if (d.config.strategy==CVX_REGULARIZE) {
76  g << "cvx_config.strategy = CVX_REGULARIZE;\n";
77  } else if (d.config.strategy==CVX_EIGEN_CLIP) {
78  g << "cvx_config.strategy = CVX_EIGEN_CLIP;\n";
79  } else if (d.config.strategy==CVX_EIGEN_REFLECT) {
80  g << "cvx_config.strategy = CVX_EIGEN_REFLECT;\n";
81  }
82  if (d.config.type_in==CVX_SYMM) {
83  g << "cvx_config.type_in = CVX_SYMM;\n";
84  } else if (d.config.type_in==CVX_TRIL) {
85  g << "cvx_config.type_in = CVX_TRIL;\n";
86  } else if (d.config.type_in==CVX_TRIU) {
87  g << "cvx_config.type_in = CVX_TRIU;\n";
88  }
89  g << "cvx_config.Hsp = " << g.sparsity(d.Hsp) << ";\n";
90  g << "cvx_config.Hrsp = " << g.sparsity(d.Hrsp) << ";\n";
91  g << "cvx_config.margin = " << d.config.margin << ";\n";
92  g << "cvx_config.Hsp_project = " << d.config.Hsp_project << ";\n";
93  g << "cvx_config.scc_transform = " << d.config.scc_transform << ";\n";
94  g << "cvx_config.scc_offset = " << g.constant(d.scc_offset) << ";\n";
95  g << "cvx_config.scc_mapping = " << g.constant(d.scc_mapping) << ";\n";
96  g << "cvx_config.scc_offset_size = " << d.scc_offset.size() << ";\n";
97  g << "cvx_config.max_iter_eig = " << d.config.max_iter_eig << ";\n";
98  g << "cvx_config.verbose = " << d.config.verbose << ";\n";
99  return "convexify_eval(&cvx_config, " + Hin + "," + Hout + "," + iw + "," + "w)";
100  }
101 
104  serialize(s, "", convexify_data_);
105  }
106 
109  }
110 
111  void Convexify::serialize(SerializingStream& s, const std::string& prefix,
112  const ConvexifyData& d) {
113  s.version(prefix + "Convexify", 1);
114  s.pack(prefix + "Convexify::type_in", static_cast<int>(d.config.type_in));
115  s.pack(prefix + "Convexify::strategy", static_cast<int>(d.config.strategy));
116  s.pack(prefix + "Convexify::margin", d.config.margin);
117  s.pack(prefix + "Convexify::max_iter_eig", d.config.max_iter_eig);
118  s.pack(prefix + "Convexify::scc_offset", d.scc_offset);
119  s.pack(prefix + "Convexify::scc_mapping", d.scc_mapping);
120  s.pack(prefix + "Convexify::Hsp_project", d.config.Hsp_project);
121  s.pack(prefix + "Convexify::scc_transform", d.config.scc_transform);
122  s.pack(prefix + "Convexify::verbose", d.config.verbose);
123  s.pack(prefix + "Convexify::Hsp", d.Hsp);
124  s.pack(prefix + "Convexify::Hrsp", d.Hrsp);
125  }
126 
127  void Convexify::deserialize(DeserializingStream& s, const std::string& prefix,
128  ConvexifyData& d) {
129  s.version(prefix + "Convexify", 1);
130  int type_in;
131  s.unpack(prefix + "Convexify::type_in", type_in);
132  d.config.type_in = static_cast<casadi_convexify_type_in_t>(type_in);
133  int strategy;
134  s.unpack(prefix + "Convexify::strategy", strategy);
135  d.config.strategy = static_cast<casadi_convexify_strategy_t>(strategy);
136  s.unpack(prefix + "Convexify::margin", d.config.margin);
137  s.unpack(prefix + "Convexify::max_iter_eig", d.config.max_iter_eig);
138  s.unpack(prefix + "Convexify::scc_offset", d.scc_offset);
139  s.unpack(prefix + "Convexify::scc_mapping", d.scc_mapping);
140  s.unpack(prefix + "Convexify::Hsp_project", d.config.Hsp_project);
141  s.unpack(prefix + "Convexify::scc_transform", d.config.scc_transform);
142  s.unpack(prefix + "Convexify::verbose", d.config.verbose);
143  s.unpack(prefix + "Convexify::Hsp", d.Hsp);
144  s.unpack(prefix + "Convexify::Hrsp", d.Hrsp);
145 
146 
147  d.config.scc_offset_size = d.scc_offset.size();
148 
149  // Set pointers
150  d.config.Hsp = d.Hsp;
151  d.config.Hrsp = d.Hrsp;;
154  }
155 
157  const std::vector<casadi_int>& arg,
158  const std::vector<casadi_int>& res,
159  const std::vector<bool>& arg_is_ref,
160  std::vector<bool>& res_is_ref) const {
161  std::string ret = g.convexify_eval(convexify_data_,
162  g.work(arg[0], dep(0).nnz(), arg_is_ref[0]), g.work(res[0], nnz(), false), "iw", "w");
163  g << "if (" << ret << ") return 1;\n";
164  }
165 
166  Sparsity Convexify::setup(ConvexifyData& d, const Sparsity& H, const Dict& opts, bool inplace) {
167  // Validate and categorize matrix input sparsity
168  casadi_assert(H.is_square(), "Convexify ");
169  if (H.is_symmetric()) {
170  d.config.type_in = CVX_SYMM;
171  } else if (H.is_tril()) {
172  d.config.type_in = CVX_TRIL;
173  } else if (H.is_triu()) {
174  d.config.type_in = CVX_TRIU;
175  } else {
176  casadi_error("Convexify operation requires symmetric or triangular input");
177  }
178 
179  // Read options
180  d.config.margin = 1e-7;
181  d.config.max_iter_eig = 200;
182  std::string strategy = "eigen-clip";
183  d.config.verbose = false;
184 
185  for (auto&& op : opts) {
186  if (op.first=="strategy") {
187  strategy = op.second.to_string();
188  } else if (op.first=="margin") {
189  d.config.margin = op.second;
190  casadi_assert(d.config.margin>=0, "Margin must be >=0");
191  } else if (op.first=="max_iter_eig") {
192  d.config.max_iter_eig = op.second;
193  } else if (op.first=="verbose") {
194  d.config.verbose = op.second;
195  } else {
196  casadi_error("Unknown option '" + op.first + "'.");
197  }
198  }
199 
200  // Interpret strategy
201  if (strategy=="regularize") {
202  d.config.strategy = CVX_REGULARIZE;
203  casadi_assert(d.config.type_in==CVX_SYMM, "Only truly symmetric matrices supported");
204  } else if (strategy=="eigen-reflect") {
205  d.config.strategy = CVX_EIGEN_REFLECT;
206  } else if (strategy=="eigen-clip") {
207  d.config.strategy = CVX_EIGEN_CLIP;
208  } else {
209  casadi_error("Invalid convexify strategy. "
210  "Choose from regularize|eigen-reflect|eigen-clip. Got '" + strategy + "'.");
211  }
212 
213  d.Hrsp = H;
214 
215  d.config.scc_transform = 0;
216 
217  Sparsity Hrsp = H+H.T();
218 
219  casadi_int block_size = 0;
220  Sparsity& Hsp = d.Hsp;
221  if (d.config.strategy==CVX_EIGEN_REFLECT || d.config.strategy==CVX_EIGEN_CLIP) {
222  // Uncover strongly connected components
223  std::vector<casadi_int> scc_index;
224  casadi_int scc_nb = Hrsp.scc(scc_index, d.scc_offset);
225 
226  // Represent Hessian as block-dense in permuted space
227  std::vector<Sparsity> sp;
228  for (casadi_int i=0;i<scc_nb;++i) {
229  casadi_int block = d.scc_offset.at(i+1)-d.scc_offset.at(i);
230  Sparsity stencil;
231  if (d.config.type_in==CVX_SYMM) {
232  stencil = Sparsity::dense(block, block);
233  } else if (d.config.type_in==CVX_TRIL) {
234  stencil = Sparsity::lower(block);
235  } else {
236  stencil = Sparsity::upper(block);
237  }
238  sp.push_back(stencil);
239  }
240 
241  std::vector<casadi_int> ssc_perm = lookupvector(scc_index);
242  std::vector<casadi_int> mapping_dummy;
243  Hsp = diagcat(sp).sub(ssc_perm, ssc_perm, mapping_dummy);
244  Hsp.sub(scc_index, scc_index, d.scc_mapping);
245 
246  // Find out size of maximum block
247  for (casadi_int i=0;i<scc_nb;++i) {
248  casadi_int block = d.scc_offset.at(i+1)-d.scc_offset.at(i);
249  if (block>block_size) block_size = block;
250  }
251 
253 
254  if (d.config.verbose) casadi_message("Identified " + str(scc_nb) + " blocks "
255  "with maximum size " + str(block_size) + ".");
256  } else if (d.config.strategy==CVX_REGULARIZE) {
257  Hsp = Hrsp + Sparsity::diag(H.size1());
258  } else {
259  Hsp = Hrsp;
260  }
261  d.config.Hsp_project = Hsp!=Hrsp;
262 
263  if (d.config.type_in==CVX_TRIL) {
264  Hsp = Sparsity::tril(Hsp);
265  } else if (d.config.type_in==CVX_TRIU) {
266  Hsp = Sparsity::triu(Hsp);
267  }
268 
269  d.sz_iw = 0;
270  if (d.config.strategy==CVX_EIGEN_REFLECT || d.config.strategy==CVX_EIGEN_CLIP) {
271  d.sz_iw = 1+3* d.config.max_iter_eig;
272  }
273 
274  d.sz_w = 0;
275  if (d.config.strategy==CVX_EIGEN_REFLECT || d.config.strategy==CVX_EIGEN_CLIP) {
276  d.sz_w = std::max(block_size, 2*(block_size-1)*d.config.max_iter_eig);
277  if (d.config.Hsp_project) d.sz_w = std::max(d.sz_w, Hsp.size1());
278  if (d.config.scc_transform) d.sz_w += block_size*block_size;
279  if (inplace) d.sz_w = std::max(d.sz_w, Hsp.size1()+d.Hrsp.nnz());
280  }
281  d.sz_w = Hsp.size1()+d.sz_w;
282 
283  d.config.scc_offset_size = d.scc_offset.size();
284 
285  // Set pointers
286  d.config.Hsp = Hsp;
287  d.config.Hrsp = d.Hrsp;
290 
291  return Hsp;
292  }
293 
294 } // 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 serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: convexify.cpp:102
static void serialize(SerializingStream &s, const std::string &prefix, const ConvexifyData &d)
Definition: convexify.cpp:111
std::string disp(const std::vector< std::string > &arg) const override
Print expression.
Definition: convexify.cpp:52
struct ConvexifyData convexify_data_
Definition: convexify.hpp:106
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:94
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:156
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:64
void eval_mx(const std::vector< MX > &arg, std::vector< MX > &res) const override
Evaluate symbolically (MX)
Definition: convexify.cpp:56
static Sparsity setup(ConvexifyData &d, const Sparsity &H, const Dict &opts=Dict(), bool inplace=true)
Definition: convexify.cpp:166
static MXNode * deserialize(DeserializingStream &s)
Deserialize without type information.
Definition: convexify.hpp:104
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:389
const MX & dep(casadi_int ind=0) const
dependencies - functions that have to be evaluated before this one
Definition: mx_node.hpp:354
virtual void serialize_body(SerializingStream &s) const
Serialize an object without type information.
Definition: mx_node.cpp:523
void set_sparsity(const Sparsity &sparsity)
Set the sparsity.
Definition: mx_node.cpp:222
void set_dep(const MX &dep)
Set unary dependency.
Definition: mx_node.cpp:226
MX - Matrix expression.
Definition: mx.hpp:92
const Sparsity & sparsity() const
Get the sparsity pattern.
Definition: mx.cpp:592
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:1028
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:703
static Sparsity dense(casadi_int nrow, casadi_int ncol=1)
Create a dense rectangular sparsity pattern *.
Definition: sparsity.cpp:1012
static Sparsity triu(const Sparsity &x, bool includeDiagonal=true)
Enlarge matrix.
Definition: sparsity.cpp:983
Sparsity T() const
Transpose the matrix.
Definition: sparsity.cpp:394
static Sparsity tril(const Sparsity &x, bool includeDiagonal=true)
Enlarge matrix.
Definition: sparsity.cpp:979
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:1049
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