alpaqa pi-pico
Nonconvex constrained optimization
Loading...
Searching...
No Matches
qpalm-driver.cpp
Go to the documentation of this file.
1#ifdef ALPAQA_WITH_QPALM
2
5#include <qpalm/constants.h>
6
7#include <cstdarg>
8#include <stdexcept>
9#include <string>
10
11#include "results.hpp"
12#include "solver-driver.hpp"
13
14namespace {
15
17
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;
21
22void compress_multipliers_bounds(const alpaqa::sets::Box<config_t> &C, rvec y,
23 crvec multipliers_bounds) {
25 index_t shift = 0;
26 for (index_t i = 0; i < C.lowerbound.size(); ++i)
27 if (Conv::is_bound(C, i))
28 y(shift++) = multipliers_bounds(i);
29}
30
31void expand_multipliers_bounds(const alpaqa::sets::Box<config_t> &C, crvec y,
32 rvec multipliers_bounds) {
34 index_t shift = 0;
35 for (index_t i = 0; i < C.lowerbound.size(); ++i)
36 if (Conv::is_bound(C, i))
37 multipliers_bounds(i) = y(shift++);
38}
39
40SolverResults run_qpalm_solver(auto &problem, const qpalm::Settings &settings,
41 std::ostream &os, unsigned N_exp) {
42
43 // Set up output stream
44 qpalm_os = &os;
45 auto old_print = ladel_set_print_config_printf(&print_wrap);
46 struct PrintRestorer {
47 printf_sig *print;
48 ~PrintRestorer() { ladel_set_print_config_printf(print); }
49 } restore_print{old_print};
50
51 // Adapt problem
52 auto qp = alpaqa::build_qpalm_problem(problem.problem);
53 qpalm::Solver solver{&qp, settings};
54
55 // Dimensions
56 length_t n = problem.problem.get_n(), m = problem.problem.get_m();
57 [[maybe_unused]] length_t num_bounds = static_cast<length_t>(qp.m) - m;
58
59 // Initial guess
60 vec initial_guess_mult;
61 if (auto sz = problem.initial_guess_x.size(); sz != n)
62 throw std::invalid_argument(
63 "Invalid size for initial_guess_x (expected " + std::to_string(n) +
64 ", but got " + std::to_string(sz) + ")");
65 if (auto sz = problem.initial_guess_y.size(); sz != m)
66 throw std::invalid_argument(
67 "Invalid size for initial_guess_y (expected " + std::to_string(m) +
68 ", but got " + std::to_string(sz) + ")");
69 if (auto sz = problem.initial_guess_w.size(); sz > 0) {
70 if (sz != n)
71 throw std::invalid_argument(
72 "Invalid size for initial_guess_w (expected " +
73 std::to_string(n) + ", but got " + std::to_string(sz) + ")");
74 initial_guess_mult.resize(static_cast<length_t>(qp.m));
75 if (problem.problem.provides_get_box_C())
76 compress_multipliers_bounds(problem.problem.get_box_C(),
77 initial_guess_mult,
78 problem.initial_guess_w);
79 else
80 assert(num_bounds == 0 && initial_guess_mult.size() == m);
81 initial_guess_mult.bottomRows(m) = problem.initial_guess_y;
82 }
83 auto warm_start = [&] {
84 problem.initial_guess_w.size() > 0
85 ? solver.warm_start(problem.initial_guess_x, initial_guess_mult)
86 : solver.warm_start(problem.initial_guess_x, std::nullopt);
87 };
88
89 // Solve the problem
90 auto t0 = std::chrono::steady_clock::now();
91 warm_start();
92 solver.solve();
93 auto t1 = std::chrono::steady_clock::now();
94 auto info = solver.get_info();
95 vec sol_x = solver.get_solution().x, sol_y = solver.get_solution().y;
96
97 // Solve the problems again to average runtimes
98 using ns = std::chrono::nanoseconds;
99 auto avg_duration = duration_cast<ns>(t1 - t0);
100 ladel_set_print_config_printf(&print_wrap_noop);
101 os.setstate(std::ios_base::badbit);
102 for (unsigned i = 0; i < N_exp; ++i) {
103 auto t0 = std::chrono::steady_clock::now();
104 warm_start();
105 solver.solve();
106 auto t1 = std::chrono::steady_clock::now();
107 avg_duration += duration_cast<ns>(t1 - t0);
108 }
109 os.clear();
110 avg_duration /= (N_exp + 1);
111 auto evals = *problem.evaluations;
112
113 // Results
114 SolverResults results{
115 .status = info.status,
116 .success = info.status_val == QPALM_SOLVED,
117 .evals = evals,
118 .duration = avg_duration,
119 .solver = "QPALM",
120 .h = 0,
121 .δ = info.pri_res_norm,
122 .ε = info.dua_res_norm,
123 .γ = 0,
124 .Σ = 0,
125 .solution = sol_x,
126 .multipliers = sol_y.bottomRows(m),
127 .multipliers_bounds = vec::Zero(n), // see bleow
128 .penalties = vec::Zero(n),
129 .outer_iter = info.iter_out,
130 .inner_iter = info.iter,
131 .extra = {{"dua2_res_norm", info.dua2_res_norm}},
132 };
133 // Expand the multipliers for the bounds constraints again
134 if (problem.problem.provides_get_box_C())
135 expand_multipliers_bounds(problem.problem.get_box_C(), sol_y,
136 results.multipliers_bounds);
137 return results;
138}
139
140int print_wrap(const char *fmt, ...) {
141 static std::vector<char> buffer(1024);
142 std::va_list args, args2;
143 va_start(args, fmt);
144 va_copy(args2, args);
145 int needed = vsnprintf(buffer.data(), buffer.size(), fmt, args);
146 va_end(args);
147 // Error occurred
148 if (needed < 0) {
149 // ignore and return
150 }
151 // Buffer was too small
152 else if (auto buf_needed = static_cast<size_t>(needed) + 1;
153 buf_needed > buffer.size()) {
154 buffer.resize(buf_needed);
155 va_start(args2, fmt);
156 needed = vsnprintf(buffer.data(), buffer.size(), fmt, args2);
157 va_end(args2);
158 }
159 if (needed >= 0) {
160 assert(qpalm_os);
161 std::string_view out{buffer.data(), static_cast<size_t>(needed)};
162 *qpalm_os << out;
163 }
164 return needed;
165}
166
167int print_wrap_noop(const char *, ...) { return 0; }
168
169auto get_qpalm_settings(Options &opts) {
170 qpalm::Settings settings;
171 settings.eps_abs = 1e-8;
172 settings.eps_rel = 1e-8;
173 set_params(settings, "solver", opts);
174 return settings;
175}
176
177template <class LoadedProblem>
178SharedSolverWrapper make_qpalm_drive_impl(std::string_view direction,
179 Options &opts) {
180 if (!direction.empty())
181 throw std::invalid_argument(
182 "QPALM solver does not support any directions");
183 auto settings = get_qpalm_settings(opts);
184 unsigned N_exp = 0;
185 set_params(N_exp, "num_exp", opts);
186 return std::make_shared<SolverWrapper>(
187 [settings, N_exp](LoadedProblem &problem,
188 std::ostream &os) mutable -> SolverResults {
189 return run_qpalm_solver(problem, settings, os, N_exp);
190 });
191}
192
193} // namespace
194
195SharedSolverWrapper make_qpalm_driver(std::string_view direction,
196 Options &opts) {
197 static constexpr bool valid_config =
198 std::is_same_v<LoadedProblem::config_t, alpaqa::EigenConfigd>;
199 if constexpr (valid_config)
200 return make_qpalm_drive_impl<LoadedProblem>(direction, opts);
201 else
202 throw std::invalid_argument(
203 "QPALM solver only supports double precision");
204}
205
206#else
207
208#include "solver-driver.hpp"
209
211 throw std::invalid_argument(
212 "This version of alpaqa was compiled without QPALM support.");
213}
214
215#endif
#define USING_ALPAQA_CONFIG(Conf)
Definition config.hpp:77
OwningQPALMData build_qpalm_problem(const TypeErasedProblem< EigenConfigd > &problem)
typename Conf::index_t index_t
Definition config.hpp:104
typename Conf::length_t length_t
Definition config.hpp:103
typename Conf::vec vec
Definition config.hpp:88
void set_params(T &t, std::string_view prefix, Options &opts)
Definition options.hpp:126
SharedSolverWrapper make_qpalm_driver(std::string_view, Options &)
std::shared_ptr< SolverWrapper > SharedSolverWrapper
std::string status
Definition results.hpp:25
Double-precision double configuration.
Definition config.hpp:176