8#include <guanaqo/io/csv.hpp>
9#include <guanaqo/not-implemented.hpp>
13#if ALPAQA_WITH_EXTERNAL_CASADI
14#include <casadi/core/external.hpp>
27namespace fs = std::filesystem;
37 std::optional<CasADiFunctionEvaluator<Conf, 2, 1>>
g = std::nullopt;
38 std::optional<CasADiFunctionEvaluator<Conf, 3, 1>>
grad_g_prod =
40 std::optional<CasADiFunctionEvaluator<Conf, 2, 1>>
jac_g = std::nullopt;
41 std::optional<CasADiFunctionEvaluator<Conf, 3, 1>>
grad_L = std::nullopt;
42 std::optional<CasADiFunctionEvaluator<Conf, 5, 1>>
hess_L_prod =
44 std::optional<CasADiFunctionEvaluator<Conf, 4, 1>>
hess_L = std::nullopt;
45 std::optional<CasADiFunctionEvaluator<Conf, 6, 2>>
ψ = std::nullopt;
46 std::optional<CasADiFunctionEvaluator<Conf, 6, 2>>
ψ_grad_ψ = std::nullopt;
47 std::optional<CasADiFunctionEvaluator<Conf, 8, 1>>
hess_ψ_prod =
49 std::optional<CasADiFunctionEvaluator<Conf, 7, 1>>
hess_ψ = std::nullopt;
51#if !ALPAQA_WITH_EXTERNAL_CASADI
55 template <
class Loader>
56 requires requires(Loader &&loader,
const char *name) {
57 { loader(name) } -> std::same_as<casadi::Function>;
58 { loader.format_name(name) } -> std::same_as<std::string>;
60 static std::unique_ptr<CasADiFunctionsWithParam>
load(Loader &&loader) {
64 auto gfun = loader(
"g");
65 using namespace std::literals::string_literals;
68 "Invalid number of input arguments: got "s +
69 std::to_string(gfun.n_in()) +
", should be 2.");
72 "Invalid number of output arguments: got "s +
73 std::to_string(gfun.n_in()) +
", should be 0 or 1.");
74 if (gfun.size2_in(0) != 1)
76 "First input argument should be a column vector.");
77 if (gfun.size2_in(1) != 1)
79 "Second input argument should be a column vector.");
80 if (gfun.n_out() == 1 && gfun.size2_out(0) != 1)
82 "First output argument should be a column vector.");
83 n =
static_cast<length_t>(gfun.size1_in(0));
84 if (gfun.n_out() == 1)
85 m =
static_cast<length_t>(gfun.size1_out(0));
86 p =
static_cast<length_t>(gfun.size1_in(1));
87 if (gfun.n_out() == 0) {
90 "Function g has no outputs but m != 0");
95 return std::make_optional(std::move(
g));
100#if !ALPAQA_WITH_EXTERNAL_CASADI
107 loader(
"extra_func_" + std::to_string(i++)));
108 }
catch (dynamic_load_error &) {
113 return std::make_unique<CasADiFunctionsWithParam>(
134 loader,
"psi",
dims(
n,
p,
m,
m,
m,
m),
dims(1,
m)),
136 loader,
"psi_grad_psi",
dims(
n,
p,
m,
m,
m,
m),
dims(1,
n)),
138 loader,
"hess_psi_prod",
dims(
n,
p,
m,
m, 1,
m,
m,
n),
141 loader,
"hess_psi",
dims(
n,
p,
m,
m, 1,
m,
m),
143#
if !ALPAQA_WITH_EXTERNAL_CASADI
154template <Config Conf>
160template <Config Conf>
162 DynamicLoadFlags dl_flags)
166 const std::string &filename;
167 DynamicLoadFlags dl_flags;
168 auto operator()(
const std::string &name)
const {
169#if ALPAQA_WITH_EXTERNAL_CASADI
175 auto format_name(
const std::string &name)
const {
176 return filename +
':' + name;
178 } loader{filename, dl_flags};
181 this->num_variables = impl->n;
182 this->num_constraints = impl->m;
187 auto data_filepath = fs::path{filename}.replace_extension(
"csv");
188 if (fs::exists(data_filepath))
189 load_numerical_data(data_filepath);
192template <Config Conf>
195#if ALPAQA_WITH_EXTERNAL_CASADI
198 auto operator()(
const std::string &name)
const {
199 return casadi::Function::deserialize(
functions.functions.at(name));
201 auto format_name(
const std::string &name)
const {
202 return "SerializedCasADiFunctions['" + name +
"']";
207 this->num_variables = impl->n;
208 this->num_constraints = impl->m;
214 throw std::runtime_error(
215 "This version of alpaqa was compiled without the CasADi C++ library");
219template <Config Conf>
225 auto operator()(
const std::string &name)
const {
228 auto format_name(
const std::string &name)
const {
229 return "CasADiFunctions['" + name +
"']";
234 this->num_variables = impl->n;
235 this->num_constraints = impl->m;
241template <Config Conf>
243 const std::filesystem::path &filepath,
char sep) {
245 std::ifstream data_file{filepath};
247 throw std::runtime_error(
"Unable to open data file \"" +
248 filepath.string() +
'"');
252 auto wrap_data_load = [&](std::string_view
name,
auto &v,
bool fixed_size) {
253 using namespace guanaqo::io;
256 if (data_file.peek() ==
'\n')
257 return static_cast<void>(data_file.get());
259 csv_read_row(data_file,
as_span(v), sep);
261 auto s = csv_read_row_std_vector<real_t>(data_file, sep);
264 }
catch (csv_read_error &e) {
266 throw std::runtime_error(
"Unable to read " + std::string(
name) +
267 " from data file \"" + filepath.string() +
268 ':' + std::to_string(line) +
273 auto read_single = [&](std::string_view
name,
auto &v) {
276 throw std::runtime_error(
"Unable to read " + std::string(
name) +
277 " from data file \"" + filepath.string() +
278 ':' + std::to_string(line) +
'"');
281 wrap_data_load(
"variable_bounds.lower", this->
variable_bounds.lower,
true);
282 wrap_data_load(
"variable_bounds.upper", this->
variable_bounds.upper,
true);
283 wrap_data_load(
"general_bounds.lower", this->
general_bounds.lower,
true);
284 wrap_data_load(
"general_bounds.upper", this->
general_bounds.upper,
true);
285 wrap_data_load(
"param", this->
param,
true);
286 wrap_data_load(
"l1_reg", this->
l1_reg,
false);
293template <Config Conf>
295template <Config Conf>
298template <Config Conf>
314template <Config Conf>
317 impl->f_grad_f({x.data(),
param.data()}, {&f, grad_fx.data()});
320template <Config Conf>
324 impl->f_grad_f({x.data(),
param.data()}, {&f, grad_fx.data()});
328template <Config Conf>
333 (*
impl->g)({x.data(),
param.data()}, {g.data()});
335 throw not_implemented_error(
"CasADiProblem::eval_constraints");
338template <Config Conf>
345 if (
impl->grad_g_prod)
346 (*
impl->grad_g_prod)({x.data(),
param.data(), y.data()}, {gxy.data()});
348 throw not_implemented_error(
349 "CasADiProblem::eval_constraints_gradient_product");
352template <Config Conf>
356 impl->grad_ψ({x.data(),
param.data(), y.data(), Σ.data(),
357 this->D.lower.data(), this->D.upper.data()},
366template <Config Conf>
373 throw std::logic_error(
374 "CasADiProblem::eval_augmented_lagrangian_and_gradient");
376 (*
impl->ψ_grad_ψ)({x.data(),
param.data(), y.data(), Σ.data(),
379 {&ψ, grad_ψ.data()});
383template <Config Conf>
387 throw std::logic_error(
"CasADiProblem::eval_lagrangian_gradient");
388 (*
impl->grad_L)({x.data(),
param.data(), y.data()}, {grad_L.data()});
391template <Config Conf>
396 throw std::logic_error(
"CasADiProblem::eval_augmented_lagrangian");
398 (*
impl->ψ)({x.data(),
param.data(), y.data(), Σ.data(),
405template <Config Conf>
407 throw not_implemented_error(
"CasADiProblem::eval_grad_gi");
410template <Config Conf>
411Sparsity
convert_csc(
const auto &sp, sparsity::Symmetry symmetry) {
413 using SparseCSC = sparsity::SparseCSC<casadi_int, casadi_int>;
416 .rows =
static_cast<index_t>(sp.size1()),
417 .cols =
static_cast<index_t>(sp.size2()),
418 .symmetry = symmetry,
419 .inner_idx = span{sp.row(),
static_cast<size_t>(sp.nnz())},
420 .outer_ptr = span{sp.colind(),
static_cast<size_t>(sp.size2()) + 1},
421 .order = SparseCSC::SortedRows,
425template <Config Conf>
428 sparsity::Dense dense{
431 .symmetry = sparsity::Symmetry::Unsymmetric,
433 if (!
impl->jac_g.has_value())
435 const auto &sp =
impl->jac_g->fun.sparsity_out(0);
441template <Config Conf>
443 rvec J_values)
const {
445 throw std::logic_error(
"CasADiProblem::eval_constraints_jacobian");
446 (*
impl->jac_g)({x.data(),
param.data()}, {J_values.data()});
449template <Config Conf>
453 if (!
impl->hess_L_prod)
454 throw std::logic_error(
"CasADiProblem::eval_augmented_lagrangian");
455 (*
impl->hess_L_prod)({x.data(),
param.data(), y.data(), &scale, v.data()},
459template <Config Conf>
461 sparsity::Dense dense{
464 .symmetry = sparsity::Symmetry::Upper,
466 if (!
impl->hess_L.has_value())
468 const auto &sp =
impl->hess_L->fun.sparsity_out(0);
469 return sp.is_dense() ? Sparsity{dense}
473template <Config Conf>
476 rvec H_values)
const {
478 throw std::logic_error(
"CasADiProblem::eval_lagrangian_hessian");
479 (*
impl->hess_L)({x.data(),
param.data(), y.data(), &scale},
483template <Config Conf>
486 if (!
impl->hess_ψ_prod)
487 throw std::logic_error(
488 "CasADiProblem::eval_augmented_lagrangian_hessian_product");
489 (*
impl->hess_ψ_prod)({x.data(),
param.data(), y.data(), Σ.data(), &scale,
495template <Config Conf>
498 sparsity::Dense dense{
501 .symmetry = sparsity::Symmetry::Upper,
503 if (!
impl->hess_ψ.has_value())
505 const auto &sp =
impl->hess_ψ->fun.sparsity_out(0);
506 return sp.is_dense() ? Sparsity{dense}
510template <Config Conf>
514 throw std::logic_error(
515 "CasADiProblem::eval_augmented_lagrangian_hessian");
516 (*
impl->hess_ψ)({x.data(),
param.data(), y.data(), Σ.data(), &scale,
522template <Config Conf>
526template <Config Conf>
528 return impl->ψ.has_value();
530template <Config Conf>
532 return impl->ψ_grad_ψ.has_value();
534template <Config Conf>
537 return impl->ψ_grad_ψ.has_value();
539template <Config Conf>
541 return impl->grad_L.has_value();
543template <Config Conf>
545 return impl->jac_g.has_value();
547template <Config Conf>
549 return impl->hess_L_prod.has_value();
551template <Config Conf>
553 return impl->hess_L.has_value();
555template <Config Conf>
558 return impl->hess_ψ_prod.has_value();
560template <Config Conf>
562 return impl->hess_ψ.has_value();
565template <Config Conf>
570#if !ALPAQA_WITH_EXTERNAL_CASADI
571template <Config Conf>
573 auto iz =
static_cast<size_t>(i);
574 if (iz >=
impl->extra_functions.size())
576 return &
impl->extra_functions[iz];
#define BEGIN_ALPAQA_CASADI_LOADER_NAMESPACE
#define END_ALPAQA_CASADI_LOADER_NAMESPACE
BoxConstrProblem(length_t num_variables, length_t num_constraints)
index_t penalty_alm_split
Problem definition for a CasADi problem, loaded from a DLL.
std::string get_name() const
bool provides_eval_constraints_jacobian() const
Sparsity get_lagrangian_hessian_sparsity() const
void eval_lagrangian_hessian_product(crvec x, crvec y, real_t scale, crvec v, rvec Hv) const
guanaqo::copyable_unique_ptr< Functions > impl
void eval_lagrangian_hessian(crvec x, crvec y, real_t scale, rvec H_values) const
bool provides_eval_augmented_lagrangian() const
real_t eval_objective_and_gradient(crvec x, rvec grad_fx) const
real_t eval_augmented_lagrangian(crvec x, crvec y, crvec Σ, rvec ŷ) const
bool provides_eval_augmented_lagrangian_and_gradient() const
void eval_constraints_jacobian(crvec x, rvec J_values) 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 &)
real_t eval_objective(crvec x) const
void eval_augmented_lagrangian_hessian_product(crvec x, crvec y, crvec Σ, real_t scale, crvec v, rvec Hv) const
bool provides_eval_lagrangian_hessian_product() const
void eval_lagrangian_gradient(crvec x, crvec y, rvec grad_L, rvec work_n) const
CasADiProblem(const std::string &filename, DynamicLoadFlags dl_flags={})
Load a problem generated by CasADi (with parameters).
real_t eval_augmented_lagrangian_and_gradient(crvec x, crvec y, crvec Σ, rvec grad_ψ, rvec work_n, rvec work_m) const
Sparsity get_constraints_jacobian_sparsity() const
void eval_augmented_lagrangian_gradient(crvec x, crvec y, crvec Σ, rvec grad_ψ, rvec work_n, rvec work_m) const
void eval_augmented_lagrangian_hessian(crvec x, crvec y, crvec Σ, real_t scale, rvec H_values) const
casadi::Function * extra_function(index_t i)
bool provides_eval_augmented_lagrangian_hessian() const
bool provides_eval_augmented_lagrangian_gradient() const
void eval_constraints(crvec x, rvec g) const
bool provides_eval_grad_gi() const
void eval_objective_gradient(crvec x, rvec grad_fx) const
bool provides_eval_augmented_lagrangian_hessian_product() const
void eval_grad_gi(crvec x, index_t i, rvec grad_i) const
bool provides_eval_lagrangian_hessian() const
void eval_constraints_gradient_product(crvec x, crvec y, rvec grad_gxy) const
Sparsity get_augmented_lagrangian_hessian_sparsity() const
bool provides_eval_lagrangian_gradient() const
Class that loads and calls pre-compiled CasADi functions in a DLL/SO file.
Class for evaluating CasADi functions, allocating the necessary workspace storage in advance for allo...
#define USING_ALPAQA_CONFIG(Conf)
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)
Function external(const std::string &name, const std::string &bin_name, DynamicLoadFlags dl_flags)
Load the given CasADi function from the given DLL/SO file.
auto casadi_to_index(casadi_int i) -> index_t< Conf >
auto as_span(Eigen::DenseBase< Derived > &v)
Convert an Eigen vector view to a std::span.
Sparsity convert_csc(const auto &sp, sparsity::Symmetry symmetry)
typename Conf::real_t real_t
typename Conf::index_t index_t
auto as_vec(std::span< T, E > s)
Convert a std::span to an Eigen::Vector view.
typename Conf::length_t length_t
typename Conf::crvec crvec
std::vector< casadi::Function > extra_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