10#include <guanaqo/dl-flags.hpp>
11#include <guanaqo/io/csv.hpp>
12#include <guanaqo/not-implemented.hpp>
14#include <Eigen/Sparse>
16#if ALPAQA_WITH_EXTERNAL_CASADI
17#include <casadi/core/external.hpp>
29namespace fs = std::filesystem;
33using namespace alpaqa::casadi_loader;
64 template <
class Loader>
65 requires requires(Loader &&loader,
const char *name) {
66 { loader(name) } -> std::same_as<casadi::Function>;
67 { loader.format_name(name) } -> std::same_as<std::string>;
69 static std::unique_ptr<CasADiControlFunctionsWithParam>
74 using namespace std::literals::string_literals;
77 "Invalid number of input arguments: got "s +
78 std::to_string(ffun.
n_in()) +
", should be 3.");
79 if (ffun.
n_out() != 1)
81 "Invalid number of output arguments: got "s +
82 std::to_string(ffun.
n_in()) +
", should be 1.");
93 using namespace std::literals::string_literals;
96 "Invalid number of input arguments: got "s +
97 std::to_string(hfun.
n_in()) +
", should be 3.");
98 if (hfun.
n_out() != 1)
100 "Invalid number of output arguments: got "s +
101 std::to_string(hfun.
n_in()) +
", should be 1.");
110 using namespace std::literals::string_literals;
111 if (hfun.
n_in() != 2)
113 "Invalid number of input arguments: got "s +
114 std::to_string(hfun.
n_in()) +
", should be 2.");
115 if (hfun.
n_out() != 1)
117 "Invalid number of output arguments: got "s +
118 std::to_string(hfun.
n_in()) +
", should be 1.");
126 using namespace std::literals::string_literals;
127 if (cfun.
n_in() != 2)
129 "Invalid number of input arguments: got "s +
130 std::to_string(cfun.
n_in()) +
", should be 2.");
131 if (cfun.
n_out() != 1)
133 "Invalid number of output arguments: got "s +
134 std::to_string(cfun.
n_in()) +
", should be 1.");
142 using namespace std::literals::string_literals;
143 if (cfun.
n_in() != 2)
145 "Invalid number of input arguments: got "s +
146 std::to_string(cfun.
n_in()) +
", should be 2.");
147 if (cfun.
n_out() != 1)
149 "Invalid number of output arguments: got "s +
150 std::to_string(cfun.
n_in()) +
", should be 1.");
163 auto self = std::make_unique<CasADiControlFunctionsWithParam<Conf>>(
181 .h_N = std::move(
h_N),
219template <Config Conf>
222 DynamicLoadFlags dl_flags)
226 const std::string &filename;
227 DynamicLoadFlags dl_flags;
228 auto operator()(
const std::string &name)
const {
229#if ALPAQA_WITH_EXTERNAL_CASADI
235 auto format_name(
const std::string &name)
const {
236 return filename +
':' + name;
238 } loader{filename, dl_flags};
253 auto n_work = std::max({
254 impl->Q.fun.sparsity_out(0).nnz(),
255 impl->Q_N.fun.sparsity_out(0).nnz(),
256 impl->gn_hess_c.fun.sparsity_out(0).nnz(),
257 impl->gn_hess_c_N.fun.sparsity_out(0).nnz(),
261 auto bounds_filepath = fs::path{filename}.replace_extension(
"csv");
262 if (fs::exists(bounds_filepath))
266template <Config Conf>
268 const std::filesystem::path &filepath,
char sep) {
270 std::ifstream data_file{filepath};
272 throw std::runtime_error(
"Unable to open data file \"" +
273 filepath.string() +
'"');
277 auto wrap_data_load = [&](std::string_view name,
auto &v,
278 bool fixed_size =
true) {
279 using namespace guanaqo::io;
282 if (data_file.peek() ==
'\n')
283 return static_cast<void>(data_file.get());
285 csv_read_row(data_file,
as_span(v), sep);
287 auto s = csv_read_row_std_vector<real_t>(data_file, sep);
290 }
catch (csv_read_error &e) {
292 throw std::runtime_error(
"Unable to read " + std::string(name) +
293 " from data file \"" + filepath.string() +
294 ':' + std::to_string(line) +
299 auto read_single = [&](std::string_view name,
auto &v) {
302 throw std::runtime_error(
"Unable to read " + std::string(name) +
303 " from data file \"" + filepath.string() +
304 ':' + std::to_string(line) +
'"');
306 wrap_data_load(
"U.lower", this->
U.lower);
307 wrap_data_load(
"U.upper", this->
U.upper);
308 wrap_data_load(
"D.lower", this->
D.lower);
309 wrap_data_load(
"D.upper", this->
D.upper);
310 wrap_data_load(
"D_N.lower", this->
D_N.lower);
311 wrap_data_load(
"D_N.upper", this->
D_N.upper);
312 wrap_data_load(
"x_init", this->
x_init);
313 wrap_data_load(
"param", this->
param);
319template <Config Conf>
322template <Config Conf>
326template <Config Conf>
339 assert(x.size() ==
nx);
340 assert(u.size() ==
nu);
341 assert(fxu.size() ==
nx);
342 impl->f({x.data(), u.data(),
param.data()}, {fxu.data()});
344template <Config Conf>
347 assert(x.size() ==
nx);
348 assert(u.size() ==
nu);
349 assert(J_fxu.rows() ==
nx);
350 assert(J_fxu.cols() ==
nx +
nu);
351 impl->jac_f({x.data(), u.data(),
param.data()}, {J_fxu.data()});
353template <Config Conf>
356 rvec grad_fxu_p)
const {
357 assert(x.size() ==
nx);
358 assert(u.size() ==
nu);
359 assert(p.size() ==
nx);
360 assert(grad_fxu_p.size() ==
nx +
nu);
361 impl->grad_f_prod({x.data(), u.data(),
param.data(), p.data()},
362 {grad_fxu_p.data()});
364template <Config Conf>
367 assert(x.size() ==
nx);
368 assert(u.size() ==
nu);
369 assert(h.size() ==
nh);
370 impl->h({x.data(), u.data(),
param.data()}, {h.data()});
372template <Config Conf>
374 assert(x.size() ==
nx);
375 assert(h.size() ==
nh_N);
376 impl->h_N({x.data(),
param.data()}, {h.data()});
378template <Config Conf>
380 assert(h.size() ==
nh);
385template <Config Conf>
387 assert(h.size() ==
nh_N);
389 impl->l_N({h.data(),
param.data()}, {&l});
392template <Config Conf>
395 assert(xu.size() ==
nx +
nu);
396 assert(h.size() ==
nh);
397 assert(qr.size() ==
nx +
nu);
398 impl->qr({xu.data(), h.data(),
param.data()}, {qr.data()});
400template <Config Conf>
402 assert(x.size() ==
nx);
403 assert(h.size() ==
nh_N);
404 assert(q.size() ==
nx);
405 impl->q_N({x.data(), h.data(),
param.data()}, {q.data()});
407template <Config Conf>
410 assert(xu.size() ==
nx +
nu);
411 assert(h.size() ==
nh);
412 assert(Q.rows() ==
nx);
413 assert(Q.cols() ==
nx);
415 using spmat = Eigen::SparseMatrix<real_t, Eigen::ColMajor, casadi_int>;
416 using cmspmat = Eigen::Map<const spmat>;
417 auto &&sparse =
impl->Q.fun.sparsity_out(0);
418 if (sparse.is_dense())
424 static_cast<length_t>(sparse.nnz()),
430template <Config Conf>
432 assert(x.size() ==
nx);
433 assert(h.size() ==
nh_N);
434 assert(Q.rows() ==
nx);
435 assert(Q.cols() ==
nx);
436 impl->Q_N({x.data(), h.data(),
param.data()}, {
work.data()});
437 auto &&sparse =
impl->Q_N.fun.sparsity_out(0);
438 using spmat = Eigen::SparseMatrix<real_t, Eigen::ColMajor, casadi_int>;
439 using cmspmat = Eigen::Map<const spmat>;
440 if (sparse.is_dense())
446 static_cast<length_t>(sparse.nnz()),
453template <Config Conf>
457 auto &&sparse =
impl->R.fun.sparsity_out(0);
458 assert(xu.size() ==
nx +
nu);
459 assert(h.size() ==
nh);
460 assert(R.rows() <=
nu);
461 assert(R.cols() <=
nu);
462 assert(R.rows() == mask.size());
463 assert(R.cols() == mask.size());
464 assert(
work.size() >=
static_cast<length_t>(sparse.nnz()));
466 using spmat = Eigen::SparseMatrix<real_t, Eigen::ColMajor, casadi_int>;
467 using cmspmat = Eigen::Map<const spmat>;
468 if (sparse.is_dense()) {
470 R += R_full(mask, mask);
475 static_cast<length_t>(sparse.nnz()),
484template <Config Conf>
488 auto &&sparse =
impl->S.fun.sparsity_out(0);
489 assert(xu.size() ==
nx +
nu);
490 assert(h.size() ==
nh);
491 assert(S.rows() <=
nu);
492 assert(S.rows() == mask.size());
493 assert(S.cols() ==
nx);
494 assert(
work.size() >=
static_cast<length_t>(sparse.nnz()));
496 using spmat = Eigen::SparseMatrix<real_t, Eigen::ColMajor, casadi_int>;
497 using cmspmat = Eigen::Map<const spmat>;
498 using Eigen::indexing::all;
499 if (sparse.is_dense()) {
501 S += S_full(mask, all);
506 static_cast<length_t>(sparse.nnz()),
515template <Config Conf>
521 auto &&sparse =
impl->R.fun.sparsity_out(0);
522 assert(v.size() ==
nu);
523 assert(out.size() == mask_J.size());
524 assert(
work.size() >=
static_cast<length_t>(sparse.nnz()));
525 using spmat = Eigen::SparseMatrix<real_t, Eigen::ColMajor, casadi_int>;
526 using cmspmat = Eigen::Map<const spmat>;
527 if (sparse.is_dense()) {
529 out.noalias() += R(mask_J, mask_K) * v(mask_K);
534 static_cast<length_t>(sparse.nnz()),
544template <Config Conf>
549 auto &&sparse =
impl->S.fun.sparsity_out(0);
550 assert(v.size() ==
nu);
551 assert(out.size() ==
nx);
552 assert(
work.size() >=
static_cast<length_t>(sparse.nnz()));
553 using spmat = Eigen::SparseMatrix<real_t, Eigen::ColMajor, casadi_int>;
554 using cmspmat = Eigen::Map<const spmat>;
555 using Eigen::indexing::all;
556 if (sparse.is_dense()) {
558 out.noalias() += Sᵀ(all, mask_K) * v(mask_K);
563 static_cast<length_t>(sparse.nnz()),
573template <Config Conf>
575 auto &&sparse =
impl->R.fun.sparsity_out(0);
576 return static_cast<length_t>(sparse.nnz());
579template <Config Conf>
581 auto &&sparse =
impl->S.fun.sparsity_out(0);
582 return static_cast<length_t>(sparse.nnz());
585template <Config Conf>
589 assert(x.size() ==
nx);
590 assert(c.size() ==
nc);
591 impl->c({x.data(),
param.data()}, {c.data()});
594template <Config Conf>
597 rvec grad_cx_p)
const {
598 assert(x.size() ==
nx);
599 assert(p.size() ==
nc);
600 assert(grad_cx_p.size() ==
nx);
601 impl->grad_c_prod({x.data(),
param.data(), p.data()}, {grad_cx_p.data()});
604template <Config Conf>
608 auto &&sparse =
impl->gn_hess_c.fun.sparsity_out(0);
609 assert(x.size() ==
nx);
610 assert(M.size() ==
nc);
611 assert(out.rows() ==
nx);
612 assert(out.cols() ==
nx);
613 assert(
work.size() >=
static_cast<length_t>(sparse.nnz()));
614 impl->gn_hess_c({x.data(),
param.data(), M.data()}, {
work.data()});
615 using spmat = Eigen::SparseMatrix<real_t, Eigen::ColMajor, casadi_int>;
616 using cmspmat = Eigen::Map<const spmat>;
617 if (sparse.is_dense())
623 static_cast<length_t>(sparse.nnz()),
630template <Config Conf>
634 assert(x.size() ==
nx);
635 assert(c.size() ==
nc_N);
636 impl->c_N({x.data(),
param.data()}, {c.data()});
639template <Config Conf>
641 rvec grad_cx_p)
const {
642 assert(x.size() ==
nx);
643 assert(p.size() ==
nc_N);
644 assert(grad_cx_p.size() ==
nx);
645 impl->grad_c_prod_N({x.data(),
param.data(), p.data()}, {grad_cx_p.data()});
648template <Config Conf>
651 auto &&sparse =
impl->gn_hess_c.fun.sparsity_out(0);
652 assert(x.size() ==
nx);
653 assert(M.size() ==
nc_N);
654 assert(out.rows() ==
nx);
655 assert(out.cols() ==
nx);
656 assert(
work.size() >=
static_cast<length_t>(sparse.nnz()));
657 impl->gn_hess_c_N({x.data(),
param.data(), M.data()}, {
work.data()});
658 using spmat = Eigen::SparseMatrix<real_t, Eigen::ColMajor, casadi_int>;
659 using cmspmat = Eigen::Map<const spmat>;
660 if (sparse.is_dense())
666 static_cast<length_t>(sparse.nnz()),
#define BEGIN_ALPAQA_CASADI_LOADER_NAMESPACE
#define END_ALPAQA_CASADI_LOADER_NAMESPACE
void eval_jac_f(index_t timestep, crvec x, crvec u, rmat J_fxu) const
guanaqo::copyable_unique_ptr< Functions > impl
void eval_add_R_prod_masked(index_t timestep, crvec xu, crvec h, crindexvec mask_J, crindexvec mask_K, crvec v, rvec out, rvec work) const
CasADiControlProblem & operator=(const CasADiControlProblem &)
void eval_add_gn_hess_constr_N(crvec x, crvec M, rmat out) const
length_t get_S_work_size() const
void eval_qr(index_t timestep, crvec xu, crvec h, rvec qr) const
index_t penalty_alm_split_N
Same as penalty_alm_split, but for the terminal constraint.
void eval_add_S_prod_masked(index_t timestep, crvec xu, crvec h, crindexvec mask_K, crvec v, rvec out, rvec work) const
void eval_constr_N(crvec x, rvec c) const
void load_numerical_data(const std::filesystem::path &filepath, char sep=',')
Load the numerical problem data (bounds and parameters) from a CSV file.
void eval_grad_constr_prod_N(crvec x, crvec p, rvec grad_cx_p) const
void eval_add_R_masked(index_t timestep, crvec xu, crvec h, crindexvec mask, rmat R, rvec work) const
real_t eval_l_N(crvec h) const
real_t eval_l(index_t timestep, crvec h) const
void eval_grad_f_prod(index_t timestep, crvec x, crvec u, crvec p, rvec grad_fxu_p) const
index_t penalty_alm_split
Components of the constraint function with indices below this number are handled using a quadratic pe...
void eval_add_gn_hess_constr(index_t timestep, crvec x, crvec M, rmat out) const
void eval_constr(index_t timestep, crvec x, rvec c) const
CasADiControlProblem(const std::string &filename, length_t N, DynamicLoadFlags dl_flags={})
void eval_h(index_t timestep, crvec x, crvec u, rvec h) const
length_t get_R_work_size() const
void eval_add_S_masked(index_t timestep, crvec xu, crvec h, crindexvec mask, rmat S, rvec work) const
alpaqa::Box< config_t > Box
void eval_q_N(crvec x, crvec h, rvec q) const
void eval_add_Q_N(crvec x, crvec h, rmat Q) const
void eval_grad_constr_prod(index_t timestep, crvec x, crvec p, rvec grad_cx_p) const
void eval_add_Q(index_t timestep, crvec xu, crvec h, rmat Q) const
void eval_f(index_t timestep, crvec x, crvec u, rvec fxu) const
void eval_h_N(crvec x, rvec h) const
Class that loads and calls pre-compiled CasADi functions in a DLL/SO file.
casadi_int size1_out(casadi_int) const
casadi_int size1_in(casadi_int) const
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)
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.
void sparse_matvec_add_masked_rows_cols(const SpMat &R, const CVec &v, Vec &&out, const MaskVec &mask_J, const MaskVec &mask_K)
out += R(mask_J,mask_K) * v(mask_K);
void sparse_add_masked_rows(const SpMat &S_full, Mat &&S, const MaskVec &mask)
S += S_full(mask,:)
void sparse_add_masked(const SpMat &R_full, Mat &&R, const MaskVec &mask)
R += R_full(mask,mask)
void sparse_matvec_add_transpose_masked_rows(const SpMat &S, const CVec &v, Vec &&out, const MaskVec &mask)
out += S(mask,:)ᵀ * v(mask);
auto as_span(Eigen::DenseBase< Derived > &v)
Convert an Eigen vector view to a std::span.
typename Conf::cmmat cmmat
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
typename Conf::crindexvec crindexvec
CasADiFunctionEvaluator< Conf, 2+WithParam, 1 > qr
CasADiFunctionEvaluator< Conf, 2+WithParam, 1 > jac_f
CasADiFunctionEvaluator< Conf, 2+WithParam, 1 > f
CasADiFunctionEvaluator< Conf, 1+WithParam, 1 > c
static std::unique_ptr< CasADiControlFunctionsWithParam > load(Loader &&loader)
CasADiFunctionEvaluator< Conf, 3+WithParam, 1 > grad_f_prod
CasADiFunctionEvaluator< Conf, 2+WithParam, 1 > R
CasADiFunctionEvaluator< Conf, 2+WithParam, 1 > grad_c_prod
CasADiFunctionEvaluator< Conf, 2+WithParam, 1 > q_N
CasADiFunctionEvaluator< Conf, 2+WithParam, 1 > grad_c_prod_N
CasADiFunctionEvaluator< Conf, 1+WithParam, 1 > h_N
CasADiFunctionEvaluator< Conf, 1+WithParam, 1 > l
static constexpr bool WithParam
CasADiFunctionEvaluator< Conf, 2+WithParam, 1 > Q
CasADiFunctionEvaluator< Conf, 2+WithParam, 1 > S
CasADiFunctionEvaluator< Conf, 2+WithParam, 1 > gn_hess_c_N
CasADiFunctionEvaluator< Conf, 2+WithParam, 1 > gn_hess_c
CasADiFunctionEvaluator< Conf, 1+WithParam, 1 > l_N
CasADiFunctionEvaluator< Conf, 2+WithParam, 1 > Q_N
CasADiFunctionEvaluator< Conf, 1+WithParam, 1 > c_N
CasADiFunctionEvaluator< Conf, 2+WithParam, 1 > h