9#include <casadi/core/external.hpp>
21namespace fs = std::filesystem;
23namespace casadi_loader {
37 std::optional<ConstrFun>
constr = std::nullopt;
38 std::optional<CasADiFunctionEvaluator<Conf, 5, 1>>
hess_L_prod =
40 std::optional<CasADiFunctionEvaluator<Conf, 4, 1>>
hess_L = std::nullopt;
41 std::optional<CasADiFunctionEvaluator<Conf, 8, 1>>
hess_ψ_prod =
43 std::optional<CasADiFunctionEvaluator<Conf, 7, 1>>
hess_ψ = std::nullopt;
44 std::optional<CasADiFunctionEvaluator<Conf, 2, 1>>
jac_g = std::nullopt;
62 using namespace casadi_loader;
64 auto load_g_unknown_dims =
65 [&]() -> std::optional<CasADiFunctionEvaluator<Conf, 2, 1>> {
66 casadi::Function gfun = casadi::external(
"g", so_name);
67 using namespace std::literals::string_literals;
69 throw std::invalid_argument(
70 "Invalid number of input arguments: got "s +
71 std::to_string(gfun.n_in()) +
", should be 2.");
73 throw std::invalid_argument(
74 "Invalid number of output arguments: got "s +
75 std::to_string(gfun.n_in()) +
", should be 0 or 1.");
76 if (gfun.size2_in(0) != 1)
77 throw std::invalid_argument(
78 "First input argument should be a column vector.");
79 if (gfun.size2_in(1) != 1)
80 throw std::invalid_argument(
81 "Second input argument should be a column vector.");
82 if (gfun.n_out() == 1 && gfun.size2_out(0) != 1)
83 throw std::invalid_argument(
84 "First output argument should be a column vector.");
85 n =
static_cast<length_t>(gfun.size1_in(0));
86 if (gfun.n_out() == 1)
87 m =
static_cast<length_t>(gfun.size1_out(0));
88 p =
static_cast<length_t>(gfun.size1_in(1));
89 if (gfun.n_out() == 0) {
91 throw std::invalid_argument(
92 "Function g has no outputs but m != 0");
95 CasADiFunctionEvaluator<Conf, 2, 1> g{std::move(gfun)};
96 g.validate_dimensions({
dim(n, 1),
dim(p, 1)}, {
dim(m, 1)});
97 return std::make_optional(std::move(g));
100 auto g =
wrap_load(so_name,
"g", load_g_unknown_dims);
104 this->param = vec::Constant(p, alpaqa::NaN<Conf>);
105 this->C = Box<config_t>{n};
106 this->D = Box<config_t>{m};
108 impl = std::make_unique<CasADiFunctionsWithParam<Conf>>(
109 CasADiFunctionsWithParam<Conf>{
110 .f = wrapped_load<CasADiFunctionEvaluator<Conf, 2, 1>>(
112 .f_grad_f = wrapped_load<CasADiFunctionEvaluator<Conf, 2, 2>>(
113 so_name,
"f_grad_f",
dims(n, p),
dims(1, n)),
116 .ψ_grad_ψ = wrapped_load<CasADiFunctionEvaluator<Conf, 6, 2>>(
117 so_name,
"psi_grad_psi",
dims(n, p, m, m, m, m),
dims(1, n)),
123 wrapped_load<CasADiFunctionEvaluator<Conf, 3, 1>>(
124 so_name,
"grad_L",
dims(n, p, m),
dims(n)),
125 wrapped_load<CasADiFunctionEvaluator<Conf, 6, 2>>(
126 so_name,
"psi",
dims(n, p, m, m, m, m),
dims(1, m)),
129 impl->hess_L_prod = try_load<CasADiFunctionEvaluator<Conf, 5, 1>>(
130 so_name,
"hess_L_prod",
dims(n, p, m, 1, n),
dims(n));
131 impl->hess_L = try_load<CasADiFunctionEvaluator<Conf, 4, 1>>(
132 so_name,
"hess_L",
dims(n, p, m, 1),
dims(
dim(n, n)));
133 impl->hess_ψ_prod = try_load<CasADiFunctionEvaluator<Conf, 8, 1>>(
134 so_name,
"hess_psi_prod",
dims(n, p, m, m, 1, m, m, n),
dims(n));
135 impl->hess_ψ = try_load<CasADiFunctionEvaluator<Conf, 7, 1>>(
136 so_name,
"hess_psi",
dims(n, p, m, m, 1, m, m),
dims(
dim(n, n)));
137 impl->jac_g = try_load<CasADiFunctionEvaluator<Conf, 2, 1>>(
138 so_name,
"jacobian_g",
dims(n, p),
dims(
dim(m, n)));
140 auto data_filepath = fs::path{so_name}.replace_extension(
"csv");
141 if (fs::exists(data_filepath))
142 load_numerical_data(data_filepath);
145template <Config Conf>
147 const std::filesystem::path &filepath,
char sep) {
149 std::ifstream data_file{filepath};
151 throw std::runtime_error(
"Unable to open data file \"" +
152 filepath.string() +
'"');
156 auto wrap_data_load = [&](std::string_view name,
auto &v,
bool fixed_size) {
159 if (data_file.peek() ==
'\n')
160 return static_cast<void>(data_file.get());
164 auto s = csv::read_row_std_vector<real_t>(data_file, sep);
169 throw std::runtime_error(
"Unable to read " + std::string(name) +
170 " from data file \"" + filepath.string() +
171 ':' + std::to_string(line) +
176 auto read_single = [&](std::string_view name,
auto &v) {
179 throw std::runtime_error(
"Unable to read " + std::string(name) +
180 " from data file \"" + filepath.string() +
181 ':' + std::to_string(line) +
'"');
184 wrap_data_load(
"C.lowerbound", this->C.lowerbound,
true);
185 wrap_data_load(
"C.upperbound", this->C.upperbound,
true);
186 wrap_data_load(
"D.lowerbound", this->D.lowerbound,
true);
187 wrap_data_load(
"D.upperbound", this->D.upperbound,
true);
188 wrap_data_load(
"param", this->param,
true);
189 wrap_data_load(
"l1_reg", this->l1_reg,
false);
191 read_single(
"penalty_alm_split", this->penalty_alm_split);
194template <Config Conf>
196template <Config Conf>
199template <Config Conf>
211 impl->f({x.data(), param.data()}, {&f});
215template <Config Conf>
218 impl->f_grad_f({x.data(), param.data()}, {&f, grad_fx.data()});
221template <Config Conf>
224 impl->f_grad_f({x.data(), param.data()}, {&f, grad_fx.data()});
228template <Config Conf>
231 impl->constr->g({x.data(), param.data()}, {g.data()});
234template <Config Conf>
239template <Config Conf>
243 impl->grad_ψ({x.data(), param.data(), y.data(), Σ.data(),
244 this->D.lowerbound.data(), this->D.upperbound.data()},
250 impl->ψ_grad_ψ({x.data(), param.data(), y.data(), Σ.data(),
251 this->D.lowerbound.data(), this->D.upperbound.data()},
252 {&ψ, grad_ψ.data()});
256template <Config Conf>
261 impl->ψ_grad_ψ({x.data(), param.data(), y.data(), Σ.data(),
262 this->D.lowerbound.data(), this->D.upperbound.data()},
263 {&ψ, grad_ψ.data()});
267template <Config Conf>
271 impl->constr->grad_L({x.data(), param.data(), y.data()},
274 eval_f_grad_f(x, grad_L);
277template <Config Conf>
282 impl->constr->ψ({x.data(), param.data(), y.data(), Σ.data(),
283 this->D.lowerbound.data(), this->D.upperbound.data()},
286 impl->f({x.data(), param.data()}, {&ψ});
290template <Config Conf>
295template <Config Conf>
297 if (!impl->jac_g.has_value())
299 auto &&sparsity = impl->jac_g->fun.sparsity_out(0);
300 return sparsity.is_dense() ? -1 :
static_cast<length_t>(sparsity.nnz());
303template <Config Conf>
306 assert(impl->jac_g.has_value());
307 if (J_values.size() > 0) {
308 (*impl->jac_g)({x.data(), param.data()}, {J_values.data()});
310 auto &&sparsity = impl->jac_g->fun.sparsity_out(0);
312 if (!sparsity.is_dense()) {
313 std::transform(sparsity.row(), sparsity.row() + sparsity.nnz(),
314 inner_idx.begin(), casadi_to_index<config_t>);
315 std::transform(sparsity.colind(),
316 sparsity.colind() + this->get_n() + 1,
317 outer_ptr.begin(), casadi_to_index<config_t>);
322template <Config Conf>
325 assert(impl->hess_L_prod.has_value());
326 (*impl->hess_L_prod)({x.data(), param.data(), y.data(), &scale, v.data()},
330template <Config Conf>
332 if (!impl->hess_L.has_value())
334 auto &&sparsity = impl->hess_L->fun.sparsity_out(0);
335 return sparsity.is_dense() ? -1 :
static_cast<length_t>(sparsity.nnz());
338template <Config Conf>
341 rvec H_values)
const {
342 assert(impl->hess_L.has_value());
343 if (H_values.size() > 0) {
344 (*impl->hess_L)({x.data(), param.data(), y.data(), &scale},
347 auto &&sparsity = impl->hess_L->fun.sparsity_out(0);
349 if (!sparsity.is_dense()) {
350 std::transform(sparsity.row(), sparsity.row() + sparsity.nnz(),
351 inner_idx.begin(), casadi_to_index<config_t>);
352 std::transform(sparsity.colind(),
353 sparsity.colind() + this->get_n() + 1,
354 outer_ptr.begin(), casadi_to_index<config_t>);
359template <Config Conf>
363 assert(impl->hess_ψ_prod.has_value());
364 (*impl->hess_ψ_prod)({x.data(), param.data(), y.data(), Σ.data(), &scale,
365 this->D.lowerbound.data(), this->D.upperbound.data(),
370template <Config Conf>
372 if (!impl->hess_ψ.has_value())
374 auto &&sparsity = impl->hess_ψ->fun.sparsity_out(0);
375 return sparsity.is_dense() ? 0 :
static_cast<length_t>(sparsity.nnz());
378template <Config Conf>
381 rvec H_values)
const {
382 assert(impl->hess_ψ.has_value());
383 if (H_values.size() > 0) {
384 (*impl->hess_ψ)({x.data(), param.data(), y.data(), Σ.data(), &scale,
385 this->D.lowerbound.data(), this->D.upperbound.data()},
388 auto &&sparsity = impl->hess_ψ->fun.sparsity_out(0);
390 if (!sparsity.is_dense()) {
391 std::transform(sparsity.row(), sparsity.row() + sparsity.nnz(),
392 inner_idx.begin(), casadi_to_index<config_t>);
393 std::transform(sparsity.colind(),
394 sparsity.colind() + this->get_n() + 1,
395 outer_ptr.begin(), casadi_to_index<config_t>);
400template <Config Conf>
404template <Config Conf>
406 return impl->jac_g.has_value();
408template <Config Conf>
410 return impl->hess_L_prod.has_value();
412template <Config Conf>
414 return impl->hess_L.has_value();
416template <Config Conf>
418 return impl->hess_ψ_prod.has_value();
420template <Config Conf>
422 return impl->hess_ψ.has_value();
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
void eval_g(crvec x, rvec g) const
void eval_hess_ψ(crvec x, crvec y, crvec Σ, real_t scale, rindexvec inner_idx, rindexvec outer_ptr, rvec H_values) const
bool provides_eval_hess_ψ_prod() const
bool provides_eval_jac_g() 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
void eval_hess_L(crvec x, crvec y, real_t scale, rindexvec inner_idx, rindexvec outer_ptr, rvec H_values) 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
void eval_grad_gi(crvec x, index_t i, rvec grad_i) const
length_t get_jac_g_num_nonzeros() const
bool provides_eval_hess_ψ() const
void eval_jac_g(crvec x, rindexvec inner_idx, rindexvec outer_ptr, rvec J_values) const
void eval_grad_ψ(crvec x, crvec y, crvec Σ, rvec grad_ψ, rvec work_n, rvec work_m) const
void eval_hess_ψ_prod(crvec x, crvec y, crvec Σ, real_t scale, crvec v, rvec Hv) const
length_t get_hess_ψ_num_nonzeros() const
length_t get_hess_L_num_nonzeros() const
Class for evaluating CasADi functions, allocating the necessary workspace storage in advance for allo...
#define USING_ALPAQA_CONFIG(Conf)
CasADiFunctionEvaluator< Conf, 3, 1 > grad_L
std::optional< CasADiFunctionEvaluator< Conf, 5, 1 > > hess_L_prod
std::optional< CasADiFunctionEvaluator< Conf, 7, 1 > > hess_ψ
constexpr auto dims(auto... a)
std::optional< CasADiFunctionEvaluator< Conf, 4, 1 > > hess_L
std::pair< casadi_int, casadi_int > dim
CasADiFunctionEvaluator< Conf, 2, 1 > f
auto wrap_load(const std::string &so_name, const char *name, F f)
std::optional< ConstrFun > constr
std::optional< CasADiFunctionEvaluator< Conf, 8, 1 > > hess_ψ_prod
CasADiFunctionEvaluator< Conf, 6, 2 > ψ_grad_ψ
CasADiFunctionEvaluator< Conf, 6, 2 > ψ
std::optional< CasADiFunctionEvaluator< Conf, 2, 1 > > jac_g
CasADiFunctionEvaluator< Conf, 2, 2 > f_grad_f
CasADiFunctionEvaluator< Conf, 2, 1 > g
void read_row(std::istream &is, Eigen::Ref< Eigen::VectorX< float > > v, char sep)
auto casadi_to_index(casadi_int i) -> index_t< Conf >
typename Conf::indexvec indexvec
typename Conf::real_t real_t
typename Conf::rindexvec rindexvec
typename Conf::index_t index_t
typename Conf::length_t length_t
typename Conf::cmvec cmvec
typename Conf::crvec crvec