1#ifdef ALPAQA_WITH_QPALM
5#include <qpalm/constants.h>
18std::ostream *qpalm_os =
nullptr;
19int print_wrap(
const char *fmt, ...) LADEL_ATTR_PRINTF_LIKE;
20int print_wrap_noop(const
char *, ...) LADEL_ATTR_PRINTF_LIKE;
22void compress_multipliers_bounds(const
alpaqa::sets::Box<config_t> &C, rvec y,
23 crvec multipliers_bounds) {
26 for (index_t i = 0; i < C.lower.size(); ++i)
27 if (Conv::is_bound(C, i))
28 y(shift++) = multipliers_bounds(i);
32 rvec multipliers_bounds) {
35 for (index_t i = 0; i < C.
lower.size(); ++i)
36 if (Conv::is_bound(C, i))
37 multipliers_bounds(i) = y(shift++);
40SolverResults run_qpalm_solver(
auto &problem,
const qpalm::Settings &settings,
41 std::ostream &os,
unsigned N_exp) {
45 auto old_print = ladel_set_print_config_printf(&print_wrap);
46 struct PrintRestorer {
48 ~PrintRestorer() { ladel_set_print_config_printf(print); }
49 } restore_print{old_print};
53 qpalm::Solver solver{&qp, settings};
56 length_t n = problem.problem.get_num_variables(),
57 m = problem.problem.get_num_constraints();
61 vec initial_guess_mult;
62 if (
auto sz = problem.initial_guess_x.size(); sz != n)
63 throw std::invalid_argument(
64 "Invalid size for initial_guess_x (expected " + std::to_string(n) +
65 ", but got " + std::to_string(sz) +
")");
66 if (
auto sz = problem.initial_guess_y.size(); sz != m)
67 throw std::invalid_argument(
68 "Invalid size for initial_guess_y (expected " + std::to_string(m) +
69 ", but got " + std::to_string(sz) +
")");
70 if (
auto sz = problem.initial_guess_w.size(); sz > 0) {
72 throw std::invalid_argument(
73 "Invalid size for initial_guess_w (expected " +
74 std::to_string(n) +
", but got " + std::to_string(sz) +
")");
75 initial_guess_mult.resize(
static_cast<length_t>(qp.m));
76 if (problem.problem.provides_get_variable_bounds())
77 compress_multipliers_bounds(problem.problem.get_variable_bounds(),
79 problem.initial_guess_w);
81 assert(num_bounds == 0 && initial_guess_mult.size() == m);
82 initial_guess_mult.bottomRows(m) = problem.initial_guess_y;
84 auto warm_start = [&] {
85 problem.initial_guess_w.size() > 0
86 ? solver.warm_start(problem.initial_guess_x, initial_guess_mult)
87 : solver.warm_start(problem.initial_guess_x, std::nullopt);
91 auto t0 = std::chrono::steady_clock::now();
94 auto t1 = std::chrono::steady_clock::now();
95 auto info = solver.get_info();
96 vec sol_x = solver.get_solution().x, sol_y = solver.get_solution().y;
99 using ns = std::chrono::nanoseconds;
100 auto avg_duration = duration_cast<ns>(t1 - t0);
101 ladel_set_print_config_printf(&print_wrap_noop);
102 os.setstate(std::ios_base::badbit);
103 for (
unsigned i = 0; i < N_exp; ++i) {
104 auto t0 = std::chrono::steady_clock::now();
107 auto t1 = std::chrono::steady_clock::now();
108 avg_duration += duration_cast<ns>(t1 - t0);
111 avg_duration /= (N_exp + 1);
112 auto evals = *problem.evaluations;
116 .status = info.status,
117 .success = info.status_val == QPALM_SOLVED,
119 .duration = avg_duration,
122 .δ = info.pri_res_norm,
123 .ε = info.dua_res_norm,
127 .multipliers = sol_y.bottomRows(m),
128 .multipliers_bounds = vec::Zero(n),
129 .penalties = vec::Zero(n),
130 .outer_iter = info.iter_out,
131 .inner_iter = info.iter,
132 .extra = {{
"dua2_res_norm", info.dua2_res_norm}},
135 if (problem.problem.provides_get_variable_bounds())
136 expand_multipliers_bounds(problem.problem.get_variable_bounds(), sol_y,
141int print_wrap(
const char *fmt, ...) {
142 static std::vector<char> buffer(1024);
143 std::va_list args, args2;
145 va_copy(args2, args);
146 int needed = vsnprintf(buffer.data(), buffer.size(), fmt, args);
153 else if (
auto buf_needed =
static_cast<size_t>(needed) + 1;
154 buf_needed > buffer.size()) {
155 buffer.resize(buf_needed);
156 va_start(args2, fmt);
157 needed = vsnprintf(buffer.data(), buffer.size(), fmt, args2);
162 std::string_view out{buffer.data(),
static_cast<size_t>(needed)};
168int print_wrap_noop(
const char *, ...) {
return 0; }
170auto get_qpalm_settings(
Options &opts) {
171 qpalm::Settings settings;
172 settings.eps_abs = 1e-8;
173 settings.eps_rel = 1e-8;
178template <
class LoadedProblem>
181 if (!direction.empty())
182 throw std::invalid_argument(
183 "QPALM solver does not support any directions");
184 auto settings = get_qpalm_settings(opts);
187 return std::make_shared<SolverWrapper>(
190 return run_qpalm_solver(problem, settings, os, N_exp);
198 static constexpr bool valid_config =
199 std::is_same_v<LoadedProblem::config_t, alpaqa::EigenConfigd>;
200 if constexpr (valid_config)
201 return make_qpalm_drive_impl<LoadedProblem>(direction, opts);
203 throw std::invalid_argument(
204 "QPALM solver only supports double precision");
212 throw std::invalid_argument(
213 "This version of alpaqa was compiled without QPALM support.");
#define USING_ALPAQA_CONFIG(Conf)
OwningQPALMData build_qpalm_problem(const TypeErasedProblem< EigenConfigd > &problem)
typename Conf::index_t index_t
typename Conf::length_t length_t
void set_params(T &t, std::string_view prefix, Options &opts)
SharedSolverWrapper make_qpalm_driver(std::string_view, Options &)
std::shared_ptr< SolverWrapper > SharedSolverWrapper
Double-precision double configuration.