4#include <qpalm/constants.h>
17std::ostream *qpalm_os =
nullptr;
18int print_wrap(
const char *fmt, ...) LADEL_ATTR_PRINTF_LIKE;
19int print_wrap_noop(const
char *, ...) LADEL_ATTR_PRINTF_LIKE;
21void compress_multipliers_bounds(length_t n, length_t m,
22 const
alpaqa::OwningQPALMData &qp,
23 crvec multipliers_bounds, rvec y) {
24 for (index_t col = 0; col < n; ++col) {
26 auto outer = qp.A->p[col];
27 auto nnz = qp.A->p[col + 1] - outer;
30 auto row = qp.A->i[outer];
31 if (row + m >= y.size())
35 auto val_lb = multipliers_bounds(col);
36 auto val_ub = multipliers_bounds(col + n);
37 y(row) = val_lb < 0 ? val_lb : val_ub;
41void expand_multipliers_bounds(length_t n, length_t m,
43 rvec multipliers_bounds) {
44 for (index_t col = 0; col < n; ++col) {
46 auto outer = qp.A->p[col];
47 auto nnz = qp.A->p[col + 1] - outer;
50 auto row = qp.A->i[outer];
51 if (row + m >= sol_y.size())
55 auto value = sol_y(row);
56 index_t offset = value > 0 ? n : 0;
57 multipliers_bounds(col + offset) = value;
61SolverResults run_qpalm_solver(
auto &problem,
const qpalm::Settings &settings,
62 std::ostream &os,
unsigned N_exp) {
66 auto old_print = ladel_set_print_config_printf(&print_wrap);
67 struct PrintRestorer {
69 ~PrintRestorer() { ladel_set_print_config_printf(print); }
70 } restore_print{old_print};
73 length_t n = problem.problem.get_n(), m = problem.problem.get_m();
77 qpalm::Solver solver{&qp, settings};
80 vec initial_guess_mult;
81 if (
auto sz = problem.initial_guess_x.size(); sz != n)
82 throw std::invalid_argument(
83 "Invalid size for initial_guess_x (expected " + std::to_string(n) +
84 ", but got " + std::to_string(sz) +
")");
85 if (
auto sz = problem.initial_guess_y.size(); sz != m)
86 throw std::invalid_argument(
87 "Invalid size for initial_guess_y (expected " + std::to_string(m) +
88 ", but got " + std::to_string(sz) +
")");
89 if (
auto sz = problem.initial_guess_w.size(); sz > 0) {
91 throw std::invalid_argument(
92 "Invalid size for initial_guess_w (expected " +
93 std::to_string(n * 2) +
", but got " + std::to_string(sz) +
95 initial_guess_mult.resize(
static_cast<length_t>(qp.m));
96 compress_multipliers_bounds(n, m, qp, problem.initial_guess_w,
98 initial_guess_mult.bottomRows(m) = problem.initial_guess_y;
100 auto warm_start = [&] {
101 problem.initial_guess_w.size() > 0
102 ? solver.warm_start(problem.initial_guess_x, initial_guess_mult)
103 : solver.warm_start(problem.initial_guess_x, std::nullopt);
107 auto t0 = std::chrono::steady_clock::now();
110 auto t1 = std::chrono::steady_clock::now();
111 auto evals = *problem.evaluations;
112 auto info = solver.get_info();
113 vec sol_x = solver.get_solution().x, sol_y = solver.get_solution().y;
116 using ns = std::chrono::nanoseconds;
117 auto avg_duration = duration_cast<ns>(t1 - t0);
118 ladel_set_print_config_printf(&print_wrap_noop);
119 os.setstate(std::ios_base::badbit);
120 for (
unsigned i = 0; i < N_exp; ++i) {
121 auto t0 = std::chrono::steady_clock::now();
124 auto t1 = std::chrono::steady_clock::now();
125 avg_duration += duration_cast<ns>(t1 - t0);
128 avg_duration /= (N_exp + 1);
133 .success = info.status_val == QPALM_SOLVED,
135 .duration = avg_duration,
138 .δ = info.dua_res_norm,
139 .ε = info.pri_res_norm,
143 .multipliers = sol_y.bottomRows(m),
144 .multipliers_bounds = vec::Zero(n * 2),
145 .penalties = vec::Zero(n),
146 .outer_iter = info.iter_out,
147 .inner_iter = info.iter,
148 .extra = {{
"dua2_res_norm", info.dua2_res_norm}},
151 expand_multipliers_bounds(n, m, qp, sol_y, results.multipliers_bounds);
155int print_wrap(
const char *fmt, ...) {
156 static std::vector<char> buffer(1024);
157 std::va_list args, args2;
159 va_copy(args2, args);
160 int needed = vsnprintf(buffer.data(), buffer.size(), fmt, args);
167 else if (
auto buf_needed =
static_cast<size_t>(needed) + 1;
168 buf_needed > buffer.size()) {
169 buffer.resize(buf_needed);
170 va_start(args2, fmt);
171 needed = vsnprintf(buffer.data(), buffer.size(), fmt, args2);
176 std::string_view out{buffer.data(),
static_cast<size_t>(needed)};
182int print_wrap_noop(
const char *, ...) {
return 0; }
184auto get_qpalm_settings(
Options &opts) {
185 qpalm::Settings settings;
186 settings.eps_abs = 1e-8;
187 settings.eps_rel = 1e-8;
192template <
class LoadedProblem>
195 if (!direction.empty())
196 throw std::invalid_argument(
197 "QPALM solver does not support any directions");
198 auto settings = get_qpalm_settings(opts);
201 return std::make_shared<SolverWrapper>(
204 return run_qpalm_solver(problem, settings, os, N_exp);
212 static constexpr bool valid_config =
213 std::is_same_v<LoadedProblem::config_t, alpaqa::EigenConfigd>;
214 if constexpr (valid_config)
215 return make_qpalm_drive_impl<LoadedProblem>(direction, opts);
217 throw std::invalid_argument(
218 "QPALM solver only supports double precision");
226 throw std::invalid_argument(
227 "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
decltype(auto) 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.