alpaqa 1.0.0a17
Nonconvex constrained optimization
Loading...
Searching...
No Matches
CasADiProblem.tpp
Go to the documentation of this file.
1#pragma once
2
8
9#include <casadi/core/external.hpp>
10
11#include <algorithm>
12#include <cassert>
13#include <filesystem>
14#include <fstream>
15#include <memory>
16#include <optional>
17#include <stdexcept>
18
19namespace alpaqa {
20
21namespace fs = std::filesystem;
22
23namespace casadi_loader {
24
25template <Config Conf>
31 std::optional<CasADiFunctionEvaluator<Conf, 2, 1>> g = std::nullopt;
32 std::optional<CasADiFunctionEvaluator<Conf, 3, 1>> grad_g_prod =
33 std::nullopt;
34 std::optional<CasADiFunctionEvaluator<Conf, 2, 1>> jac_g = std::nullopt;
35 std::optional<CasADiFunctionEvaluator<Conf, 3, 1>> grad_L = std::nullopt;
36 std::optional<CasADiFunctionEvaluator<Conf, 5, 1>> hess_L_prod =
37 std::nullopt;
38 std::optional<CasADiFunctionEvaluator<Conf, 4, 1>> hess_L = std::nullopt;
39 std::optional<CasADiFunctionEvaluator<Conf, 6, 2>> ψ = std::nullopt;
40 std::optional<CasADiFunctionEvaluator<Conf, 6, 2>> ψ_grad_ψ = std::nullopt;
41 std::optional<CasADiFunctionEvaluator<Conf, 8, 1>> hess_ψ_prod =
42 std::nullopt;
43 std::optional<CasADiFunctionEvaluator<Conf, 7, 1>> hess_ψ = std::nullopt;
44
45 template <class Loader>
46 requires requires(Loader &&loader, const char *name) {
47 { loader(name) } -> std::same_as<casadi::Function>;
48 { loader.format_name(name) } -> std::same_as<std::string>;
49 }
50 static std::unique_ptr<CasADiFunctionsWithParam> load(Loader &&loader) {
51 length_t n = 0, m = 0, p = 0;
52 auto load_g =
53 [&]() -> std::optional<CasADiFunctionEvaluator<Conf, 2, 1>> {
54 casadi::Function gfun = loader("g");
55 using namespace std::literals::string_literals;
56 if (gfun.n_in() != 2)
58 "Invalid number of input arguments: got "s +
59 std::to_string(gfun.n_in()) + ", should be 2.");
60 if (gfun.n_out() > 1)
62 "Invalid number of output arguments: got "s +
63 std::to_string(gfun.n_in()) + ", should be 0 or 1.");
64 if (gfun.size2_in(0) != 1)
66 "First input argument should be a column vector.");
67 if (gfun.size2_in(1) != 1)
69 "Second input argument should be a column vector.");
70 if (gfun.n_out() == 1 && gfun.size2_out(0) != 1)
72 "First output argument should be a column vector.");
73 n = static_cast<length_t>(gfun.size1_in(0));
74 if (gfun.n_out() == 1)
75 m = static_cast<length_t>(gfun.size1_out(0));
76 p = static_cast<length_t>(gfun.size1_in(1));
77 if (gfun.n_out() == 0) {
78 if (m != 0)
80 "Function g has no outputs but m != 0");
81 return std::nullopt;
82 }
84 g.validate_dimensions({dim(n, 1), dim(p, 1)}, {dim(m, 1)});
85 return std::make_optional(std::move(g));
86 };
87
88 auto g = wrap_load(loader, "g", load_g);
89
90 return std::make_unique<CasADiFunctionsWithParam>(
92 .n = n,
93 .m = m,
94 .p = p,
96 loader, "f", dims(n, p), dims(1)),
98 loader, "f_grad_f", dims(n, p), dims(1, n)),
99 .g = std::move(g),
101 loader, "grad_g_prod", dims(n, p, m), dims(n)),
103 loader, "jacobian_g", dims(n, p), dims(dim(m, n))),
105 loader, "grad_L", dims(n, p, m), dims(n)),
107 loader, "hess_L_prod", dims(n, p, m, 1, n), dims(n)),
109 loader, "hess_L", dims(n, p, m, 1), dims(dim(n, n))),
111 loader, "psi", dims(n, p, m, m, m, m), dims(1, m)),
113 loader, "psi_grad_psi", dims(n, p, m, m, m, m), dims(1, n)),
115 loader, "hess_psi_prod", dims(n, p, m, m, 1, m, m, n),
116 dims(n)),
118 loader, "hess_psi", dims(n, p, m, m, 1, m, m),
119 dims(dim(n, n))),
120 });
121 }
122};
123
124} // namespace casadi_loader
125
126namespace detail {
127
128template <Config Conf>
130 return static_cast<index_t<Conf>>(i);
131}
132} // namespace detail
133
134template <Config Conf>
136 : BoxConstrProblem<Conf>{0, 0} {
137
138 struct {
139 const std::string &filename;
140 auto operator()(const std::string &name) const {
141 return casadi::external(name, filename);
142 }
143 auto format_name(const std::string &name) const {
144 return filename + ':' + name;
145 }
146 } loader{filename};
148
149 this->n = impl->n;
150 this->m = impl->m;
151 this->param = vec::Constant(impl->p, alpaqa::NaN<Conf>);
152 this->C = Box<config_t>{impl->n};
153 this->D = Box<config_t>{impl->m};
154
155 auto data_filepath = fs::path{filename}.replace_extension("csv");
156 if (fs::exists(data_filepath))
157 load_numerical_data(data_filepath);
158}
159
160template <Config Conf>
162 : BoxConstrProblem<Conf>{0, 0} {
163
164 struct {
165 const SerializedCasADiFunctions &functions;
166 auto operator()(const std::string &name) const {
167 return casadi::Function::deserialize(functions.functions.at(name));
168 }
169 auto format_name(const std::string &name) const {
170 return "SerializedCasADiFunctions['" + name + "']";
171 }
172 } loader{functions};
174
175 this->n = impl->n;
176 this->m = impl->m;
177 this->param = vec::Constant(impl->p, alpaqa::NaN<Conf>);
178 this->C = Box<config_t>{impl->n};
179 this->D = Box<config_t>{impl->m};
180}
181
182template <Config Conf>
184 : BoxConstrProblem<Conf>{0, 0} {
185
186 struct {
187 const CasADiFunctions &functions;
188 auto operator()(const std::string &name) const {
189 return functions.functions.at(name);
190 }
191 auto format_name(const std::string &name) const {
192 return "CasADiFunctions['" + name + "']";
193 }
194 } loader{functions};
196
197 this->n = impl->n;
198 this->m = impl->m;
199 this->param = vec::Constant(impl->p, alpaqa::NaN<Conf>);
200 this->C = Box<config_t>{impl->n};
201 this->D = Box<config_t>{impl->m};
202}
203
204template <Config Conf>
206 const std::filesystem::path &filepath, char sep) {
207 // Open data file
208 std::ifstream data_file{filepath};
209 if (!data_file)
210 throw std::runtime_error("Unable to open data file \"" +
211 filepath.string() + '"');
212
213 // Helper function for reading single line of (float) data
214 index_t line = 0;
215 auto wrap_data_load = [&](std::string_view name, auto &v, bool fixed_size) {
216 try {
217 ++line;
218 if (data_file.peek() == '\n') // Ignore empty lines
219 return static_cast<void>(data_file.get());
220 if (fixed_size) {
222 } else { // Dynamic size
223 auto s = csv::read_row_std_vector<real_t>(data_file, sep);
224 v = cmvec{s.data(), static_cast<index_t>(s.size())};
225 }
226 } catch (csv::read_error &e) {
227 // Transform any errors in something more readable
228 throw std::runtime_error("Unable to read " + std::string(name) +
229 " from data file \"" + filepath.string() +
230 ':' + std::to_string(line) +
231 "\": " + e.what());
232 }
233 };
234 // Helper function for reading a single value
235 auto read_single = [&](std::string_view name, auto &v) {
236 data_file >> v;
237 if (!data_file)
238 throw std::runtime_error("Unable to read " + std::string(name) +
239 " from data file \"" + filepath.string() +
240 ':' + std::to_string(line) + '"');
241 };
242 // Read the bounds, parameter value, and regularization
243 wrap_data_load("C.lowerbound", this->C.lowerbound, true);
244 wrap_data_load("C.upperbound", this->C.upperbound, true);
245 wrap_data_load("D.lowerbound", this->D.lowerbound, true);
246 wrap_data_load("D.upperbound", this->D.upperbound, true);
247 wrap_data_load("param", this->param, true);
248 wrap_data_load("l1_reg", this->l1_reg, false);
249 // Penalty/ALM split is a single integer
250 read_single("penalty_alm_split", this->penalty_alm_split);
251 // Name is a string
252 data_file >> name;
253}
254
255template <Config Conf>
257template <Config Conf>
260template <Config Conf>
265
268
271 real_t f;
272 impl->f({x.data(), param.data()}, {&f});
273 return f;
274}
275
276template <Config Conf>
278 real_t f;
279 impl->f_grad_f({x.data(), param.data()}, {&f, grad_fx.data()});
280}
281
282template <Config Conf>
284 real_t f;
285 impl->f_grad_f({x.data(), param.data()}, {&f, grad_fx.data()});
286 return f;
287}
288
289template <Config Conf>
291 if (impl->m == 0)
292 return;
293 if (impl->g)
294 (*impl->g)({x.data(), param.data()}, {g.data()});
295 else
296 throw not_implemented_error("CasADiProblem::eval_g");
297}
298
299template <Config Conf>
301 if (impl->m == 0) {
302 gxy.setZero();
303 return;
304 }
305 if (impl->grad_g_prod)
306 (*impl->grad_g_prod)({x.data(), param.data(), y.data()}, {gxy.data()});
307 else
308 throw not_implemented_error("CasADiProblem::eval_grad_g_prod"); // TODO
309}
310
311template <Config Conf>
313 rvec work_n, rvec work_m) const {
314#if 0
315 impl->grad_ψ({x.data(), param.data(), y.data(), Σ.data(),
316 this->D.lowerbound.data(), this->D.upperbound.data()},
317 {grad_ψ.data()});
318#else
319 // This seems to be faster than having a specialized function. Possibly
320 // cache-related?
321 eval_ψ_grad_ψ(x, y, Σ, grad_ψ, work_n, work_m);
322#endif
323}
324
325template <Config Conf>
328 rvec) const {
329 if (!impl->ψ_grad_ψ)
330 throw std::logic_error("CasADiProblem::eval_ψ_grad_ψ");
331 real_t ψ;
332 (*impl->ψ_grad_ψ)({x.data(), param.data(), y.data(), Σ.data(),
333 this->D.lowerbound.data(), this->D.upperbound.data()},
334 {&ψ, grad_ψ.data()});
335 return ψ;
336}
337
338template <Config Conf>
340 rvec) const {
341 if (!impl->grad_L)
342 throw std::logic_error("CasADiProblem::eval_grad_L");
343 (*impl->grad_L)({x.data(), param.data(), y.data()}, {grad_L.data()});
344}
345
346template <Config Conf>
349 if (!impl->ψ)
350 throw std::logic_error("CasADiProblem::eval_ψ");
351 real_t ψ;
352 (*impl->ψ)({x.data(), param.data(), y.data(), Σ.data(),
353 this->D.lowerbound.data(), this->D.upperbound.data()},
354 {&ψ, ŷ.data()});
355 return ψ;
356}
357
358template <Config Conf>
360 throw not_implemented_error("CasADiProblem::eval_grad_gi"); // TODO
361}
362
363template <Config Conf>
367 using map_t = typename SparseCSC::index_vector_map_t;
368 return SparseCSC{
369 .rows = static_cast<index_t>(sp.size1()),
370 .cols = static_cast<index_t>(sp.size2()),
371 .symmetry = symmetry,
372 .inner_idx = map_t{sp.row(), static_cast<index_t>(sp.nnz())},
373 .outer_ptr = map_t{sp.colind(), static_cast<index_t>(sp.size2()) + 1},
374 .order = SparseCSC::SortedRows,
375 };
376}
377
378template <Config Conf>
381 .rows = this->m,
382 .cols = this->n,
384 };
385 if (!impl->jac_g.has_value())
386 return dense;
387 const auto &sp = impl->jac_g->fun.sparsity_out(0); // Reference!
388 return sp.is_dense()
389 ? Sparsity{dense}
391}
392
393template <Config Conf>
395 if (!impl->jac_g)
396 throw std::logic_error("CasADiProblem::eval_jac_g");
397 (*impl->jac_g)({x.data(), param.data()}, {J_values.data()});
398}
399
400template <Config Conf>
402 crvec v, rvec Hv) const {
403 if (!impl->hess_L_prod)
404 throw std::logic_error("CasADiProblem::eval_ψ");
405 (*impl->hess_L_prod)({x.data(), param.data(), y.data(), &scale, v.data()},
406 {Hv.data()});
407}
408
409template <Config Conf>
412 .rows = this->n,
413 .cols = this->n,
414 .symmetry = sparsity::Symmetry::Upper,
415 };
416 if (!impl->hess_L.has_value())
417 return dense;
418 const auto &sp = impl->hess_L->fun.sparsity_out(0); // Reference!
419 return sp.is_dense() ? Sparsity{dense}
421}
422
423template <Config Conf>
425 rvec H_values) const {
426 if (!impl->hess_L)
427 throw std::logic_error("CasADiProblem::eval_hess_L");
428 (*impl->hess_L)({x.data(), param.data(), y.data(), &scale},
429 {H_values.data()});
430}
431
432template <Config Conf>
435 rvec Hv) const {
436 if (!impl->hess_ψ_prod)
437 throw std::logic_error("CasADiProblem::eval_hess_ψ_prod");
438 (*impl->hess_ψ_prod)({x.data(), param.data(), y.data(), Σ.data(), &scale,
439 this->D.lowerbound.data(), this->D.upperbound.data(),
440 v.data()},
441 {Hv.data()});
442}
443
444template <Config Conf>
447 .rows = this->n,
448 .cols = this->n,
449 .symmetry = sparsity::Symmetry::Upper,
450 };
451 if (!impl->hess_ψ.has_value())
452 return dense;
453 const auto &sp = impl->hess_ψ->fun.sparsity_out(0); // Reference!
454 return sp.is_dense() ? Sparsity{dense}
456}
457
458template <Config Conf>
460 rvec H_values) const {
461 if (!impl->hess_ψ)
462 throw std::logic_error("CasADiProblem::eval_hess_ψ");
463 (*impl->hess_ψ)({x.data(), param.data(), y.data(), Σ.data(), &scale,
464 this->D.lowerbound.data(), this->D.upperbound.data()},
465 {H_values.data()});
466}
467
468template <Config Conf>
470 return false; // TODO
471}
472template <Config Conf>
474 return impl->ψ.has_value();
475}
476template <Config Conf>
478 return impl->ψ_grad_ψ.has_value();
479}
480template <Config Conf>
482 return impl->ψ_grad_ψ.has_value();
483}
484template <Config Conf>
486 return impl->grad_L.has_value();
487}
488template <Config Conf>
490 return impl->jac_g.has_value();
491}
492template <Config Conf>
494 return impl->hess_L_prod.has_value();
495}
496template <Config Conf>
498 return impl->hess_L.has_value();
499}
500template <Config Conf>
502 return impl->hess_ψ_prod.has_value();
503}
504template <Config Conf>
506 return impl->hess_ψ.has_value();
507}
508
509template <Config Conf>
510std::string CasADiProblem<Conf>::get_name() const {
511 return name;
512}
513
514} // namespace alpaqa
Implements common problem functions for minimization problems with box constraints.
Problem definition for a CasADi problem, loaded from a DLL.
bool provides_eval_hess_L() const
std::string get_name() const
void eval_g(crvec x, rvec g) const
bool provides_eval_hess_ψ_prod() const
bool provides_eval_ψ_grad_ψ() const
void eval_jac_g(crvec x, rvec J_values) const
Sparsity get_jac_g_sparsity() const
Sparsity get_hess_ψ_sparsity() const
bool provides_eval_jac_g() const
void eval_hess_ψ(crvec x, crvec y, crvec Σ, real_t scale, rvec H_values) const
Sparsity get_hess_L_sparsity() const
void load_numerical_data(const std::filesystem::path &filepath, char sep=',')
Load the numerical problem data (bounds and parameters) from a CSV file.
CasADiProblem & operator=(const CasADiProblem &)
void eval_grad_L(crvec x, crvec y, rvec grad_L, rvec work_n) const
real_t eval_ψ(crvec x, crvec y, crvec Σ, rvec ŷ) const
CasADiProblem(const std::string &filename)
Load a problem generated by CasADi (with parameters).
bool provides_eval_hess_L_prod() const
real_t eval_f_grad_f(crvec x, rvec grad_fx) const
void eval_grad_g_prod(crvec x, crvec y, rvec grad_gxy) const
void eval_hess_L_prod(crvec x, crvec y, real_t scale, crvec v, rvec Hv) const
bool provides_eval_grad_gi() const
void eval_grad_f(crvec x, rvec grad_fx) const
real_t eval_ψ_grad_ψ(crvec x, crvec y, crvec Σ, rvec grad_ψ, rvec work_n, rvec work_m) const
bool provides_eval_grad_L() const
bool provides_eval_grad_ψ() const
void eval_grad_gi(crvec x, index_t i, rvec grad_i) const
void eval_hess_L(crvec x, crvec y, real_t scale, rvec H_values) const
bool provides_eval_hess_ψ() const
void eval_grad_ψ(crvec x, crvec y, crvec Σ, rvec grad_ψ, rvec work_n, rvec work_m) const
bool provides_eval_ψ() const
void eval_hess_ψ_prod(crvec x, crvec y, crvec Σ, real_t scale, crvec v, rvec Hv) const
Class for evaluating CasADi functions, allocating the necessary workspace storage in advance for allo...
static void validate_dimensions(const casadi::Function &fun, const std::array< casadi_dim, N_in > &dim_in={}, const std::array< casadi_dim, N_out > &dim_out={})
#define USING_ALPAQA_CONFIG(Conf)
Definition config.hpp:77
auto wrapped_load(Loader &&loader, const char *name, Args &&...args)
std::optional< T > try_load(Loader &&loader, const char *name, Args &&...args)
constexpr auto dims(auto... a)
std::pair< casadi_int, casadi_int > dim
auto wrap_load(Loader &&loader, const char *name, F f)
void read_row(std::istream &is, Eigen::Ref< Eigen::VectorX< Eigen::Index > > v, char sep)
Definition csv.hpp:37
auto casadi_to_index(casadi_int i) -> index_t< Conf >
Symmetry
Describes the symmetry of matrices.
Definition sparsity.hpp:12
@ Upper
Symmetric, upper-triangular part is stored.
Dense matrix structure.
Definition sparsity.hpp:21
typename Conf::real_t real_t
Definition config.hpp:86
typename Conf::index_t index_t
Definition config.hpp:104
Sparsity< Conf > convert_csc(const auto &sp, sparsity::Symmetry symmetry)
typename Conf::length_t length_t
Definition config.hpp:103
typename Conf::cmvec cmvec
Definition config.hpp:90
constexpr const auto inf
Definition config.hpp:112
typename Conf::rvec rvec
Definition config.hpp:91
typename Conf::crvec crvec
Definition config.hpp:92
std::map< std::string, std::string > functions
std::map< std::string, casadi::Function > functions
std::optional< CasADiFunctionEvaluator< Conf, 5, 1 > > hess_L_prod
std::optional< CasADiFunctionEvaluator< Conf, 7, 1 > > hess_ψ
static std::unique_ptr< CasADiFunctionsWithParam > load(Loader &&loader)
std::optional< CasADiFunctionEvaluator< Conf, 6, 2 > > ψ_grad_ψ
std::optional< CasADiFunctionEvaluator< Conf, 4, 1 > > hess_L
CasADiFunctionEvaluator< Conf, 2, 1 > f
std::optional< CasADiFunctionEvaluator< Conf, 8, 1 > > hess_ψ_prod
std::optional< CasADiFunctionEvaluator< Conf, 3, 1 > > grad_L
std::optional< CasADiFunctionEvaluator< Conf, 2, 1 > > jac_g
std::optional< CasADiFunctionEvaluator< Conf, 3, 1 > > grad_g_prod
std::optional< CasADiFunctionEvaluator< Conf, 2, 1 > > g
std::optional< CasADiFunctionEvaluator< Conf, 6, 2 > > ψ
CasADiFunctionEvaluator< Conf, 2, 2 > f_grad_f
Sparse compressed-column structure (CCS or CSC).
Definition sparsity.hpp:29
Stores any of the supported sparsity patterns.
Definition sparsity.hpp:106