cyqlone develop
Fast, parallel and vectorized solver for linear systems with optimal control structure.
Loading...
Searching...
No Matches
solver.tpp
Go to the documentation of this file.
1#pragma once
2
3#include <cyqlone/config.hpp>
7#include <guanaqo/atomic-stop-signal.hpp>
8#include <guanaqo/print.hpp>
9#include <guanaqo/timed.hpp>
10#include <cmath>
11#include <functional>
12#include <iostream>
13#include <limits>
14#include <optional>
15#include <utility>
16
17namespace CYQLONE_NS(cyqlone::qpalm) {
18
19template <class Backend>
21 using backend_type = Backend;
22 static_assert(!std::is_const_v<backend_type>);
23
24 using active_set_t = typename backend_type::active_set_t;
25 using ineq_vec_t = typename backend_type::ineq_constr_vec_t;
26 using eq_vec_t = typename backend_type::eq_constr_vec_t;
27 using var_vec_t = typename backend_type::var_vec_t;
29
30 static void initialize_penalty_y(Backend::Context &ctx, backend_type &backend, real_t f0,
31 const ineq_vec_t &e0, ineq_vec_t &Σ,
32 const Settings &settings) {
33 using std::abs;
34 using std::fmax;
35 using std::fmin;
36 auto numer = fmax(1, abs(f0));
37 auto denom = fmax(1, 0.5 * backend.norm_squared(ctx, e0));
38 auto Σ0 = settings.initial_penalty_y;
39 if (settings.scale_initial_penalty_y)
40 Σ0 = fmax(1e-4, fmin(Σ0 * numer / denom, 1e4));
41 backend.set_constant(ctx, Σ, Σ0);
42 }
43
44 static index_t update_penalty_y(Backend::Context &ctx, backend_type &backend, ineq_vec_t &Σ,
45 const ineq_vec_t &e, const ineq_vec_t &e_old,
46 const Settings &settings) {
47 if constexpr (requires { &backend_type::update_penalty_y; }) {
48 return backend.update_penalty_y(ctx, Σ, e, e_old,
49 {.θ = settings.θ,
50 .Δy = settings.Δy,
51 .Δy_always = settings.Δy_always,
52 .max_penalty_y = settings.max_penalty_y});
53 } else {
54 GUANAQO_TRACE("update_penalty_y", 0);
55 using std::abs;
56 using std::fmax;
57 using std::fmin;
58 using std::views::zip;
59 const real_t min_denom = 1e-6;
60 const real_t norm_inf_e = fmax(min_denom, backend.norm_inf(ctx, e));
61 index_t num_changed = 0;
62 for (auto &&[ei, ei_old, Σi] : zip(e, e_old, Σ)) {
63 bool insufficient_progress = abs(ei) > settings.θ * abs(ei_old);
64 real_t update_factor =
65 insufficient_progress ? settings.Δy * abs(ei) / norm_inf_e : 1;
66 update_factor *= settings.Δy_always;
67 real_t Σ_new = Σi * update_factor;
68 Σ_new = fmax(Σi * settings.Δy_always, fmin(Σ_new, settings.max_penalty_y));
69 num_changed += Σ_new != Σi;
70 Σi = Σ_new;
71 }
72 return num_changed;
73 }
74 }
75
76 static real_t update_penalty_x(real_t S, const Settings &settings) {
77 using std::fmin;
78 return fmin(settings.Δx * S, settings.max_penalty_x);
79 }
80
82 backend.reset();
83 backend.initialize_active_set(active_set, active_set_old);
84 backend.initialize_ineq_constr_vec(Σ, y, ŷ, e, e_old, Ax, Ad);
85 backend.initialize_eq_constr_vec(Mxb, Δλ, λ);
86 backend.initialize_var_vec(x, grad, Mᵀλ, Aᵀŷ, x_outer, MᵀΔλ, d, ξ, grad_add);
87 }
88
89 SolverStatus do_main_loop(Backend::Context &ctx, backend_type &backend,
90 const Settings &settings, guanaqo::AtomicStopSignal &stop_signal,
91 SolverStats &stats);
92
97 // TODO: we don't really need grad_add unless we need to compute the directional derivative
98};
99
100template <class Backend>
102 assert(backend);
103 if (!impl)
104 impl = std::make_unique<SolverImplementation<backend_type>>();
105 impl->ensure_storage(*backend);
106 SolverStatus status;
107#if GUANAQO_WITH_TRACING
108 backend->parallel_ctx->run([](auto &ctx) { GUANAQO_TRACE("thread_id", ctx.index); });
109#endif
110 backend->parallel_ctx->run([&](backend_type::Context &ctx) {
112 auto status_local = impl->do_main_loop(ctx, *backend, settings, stop_signal, stats);
113 if (ctx.is_master()) {
114 status = status_local;
115 this->stats = std::move(stats);
116 this->stats_backend = backend->clear_stats();
117 }
118 GUANAQO_TRACE("end", ctx.index);
119 });
120 return status;
121}
122
123template <class Backend>
125 backend_type &backend,
126 const Settings &settings,
127 guanaqo::AtomicStopSignal &stop_signal,
128 SolverStats &stats) {
129 using clock_t = std::chrono::steady_clock;
131 using std::abs;
132 using std::cbrt;
133 using std::isfinite;
134 using std::sqrt;
135 using std::swap;
136 clock_t::time_point start_time = clock_t::now();
137 real_t inner_tol = settings.initial_inner_tolerance;
138 real_t ineq_constr_resid = std::numeric_limits<real_t>::quiet_NaN();
139 unsigned inner_iter = 0, outer_iter = 0;
140 SolverTimings timings{};
141 std::optional<DetailedStats> detailed_stats{};
142 if (settings.detailed_stats)
143 detailed_stats.emplace();
144
145 // Initial guess
146 backend.initial_variables(ctx, x);
147 backend.initial_multipliers_eq(ctx, λ);
148 backend.initial_multipliers_ineq(ctx, y);
149
150 real_t S = settings.initial_penalty_x;
151 // Thread-local views for swapping
152 auto y = std::ref(this->y), ŷ = std::ref(this->ŷ);
153 auto active_set = std::ref(this->active_set), active_set_old = std::ref(this->active_set_old);
154 auto e = std::ref(this->e), e_old = std::ref(this->e_old);
155
156 // Initialize matrix-vector products and initial penalty
157 timed(timings.mat_vec_M, [&] {
158 backend.eq_constr_resid(ctx, x, Mxb); // Mxb = M * x - b
159 });
160 timed(timings.mat_vec_A, [&] {
161 backend.mat_vec_A(ctx, x, Ax); // Ax = A * x
162 });
163 backend.ineq_constr_resid(ctx, Ax, e); // e = Ax - clamp(Ax, b_min, b_max)
164 real_t f0 = timed(timings.mat_vec_Q, [&] {
165 return backend.f_grad_f(ctx, x, grad); // ∇f = Q * x + q
166 });
167 timed(timings.mat_vec_MT, [&] {
168 backend.mat_vec_MT(ctx, λ, Mᵀλ); // Mᵀλ = Mᵀ * λ
169 });
170
171 // Initial penalties
172 initialize_penalty_y(ctx, backend, f0, e, Σ, settings);
173 std::ignore = f0; // TODO
174
175 // Outer ALM loop
176 while (true) {
177 backend.xcopy(ctx, x, x_outer);
178
179 // Inner semismooth Newton loop
180 unsigned no_change_active_set = 0;
181 bool force_τ_1_active_set = false;
182 auto remaining_iter = settings.max_total_inner_iter - inner_iter;
183 remaining_iter = std::min(remaining_iter, settings.max_inner_iter);
184 real_t stationarity = std::numeric_limits<real_t>::infinity();
185 real_t eq_resid = std::numeric_limits<real_t>::infinity();
186 bool increase_penalty_y = true;
187 for (unsigned inner = 0; true; ++inner) {
188 const unsigned inner_total = inner_iter + inner;
189 // Compute gradient of augmented Lagrangian
190 index_t nJ = timed(timings.mat_vec_AT, [&] {
191 return backend.calc_ŷ_Aᵀŷ(ctx, Ax, Σ, y, ŷ, Aᵀŷ, active_set);
192 });
193
194 // What to do upon inner loop termination
195 auto leave_inner = [&] {
196 // Remove the primal regularization from the gradient because
197 // we'll replace x_outer by x
198 backend.grad_f_remove_regularization(ctx, S, x, x_outer, grad);
199 GUANAQO_TRACE("leave_inner", inner);
200 // x contains x_next, ŷ contains y_next, Aᵀŷ contains Aᵀy_next
201 inner_iter += inner;
202 };
203
204 // Check inner loop termination
205 bool first_iter = outer_iter == 0 && inner == 0;
206 bool check_eq = first_iter || settings.recompute_eq_res;
207 {
208 GUANAQO_TRACE("compute residuals", inner_total);
209 eq_resid = check_eq ? backend.unscaled_eq_constr_viol(ctx, Mxb) : 0;
210 stationarity = backend.unscaled_aug_lagr_norm(ctx, grad, Mᵀλ, Aᵀŷ);
211 }
212 real_t eq_tol = settings.eq_constr_tolerance;
213 bool out_of_iter = inner >= remaining_iter;
214 bool out_of_time = clock_t::now() >= start_time + settings.max_time;
215 bool inf_err = !isfinite(stationarity + eq_resid);
216 bool stop = stop_signal.stop_requested();
217 bool fail = out_of_iter || out_of_time || inf_err || stop;
218 bool inner_conv = stationarity <= inner_tol && eq_resid <= eq_tol;
219
220 if (detailed_stats) {
221 detailed_stats->entries.push_back({
222 .outer_iter = outer_iter,
223 .inner_iter = inner,
224 .stationarity = stationarity,
225 .ineq_constr_viol = ineq_constr_resid,
226 .eq_constr_viol = eq_resid,
227 .linesearch_step_size = std::numeric_limits<real_t>::quiet_NaN(),
228 .linesearch_breakpoint_index = 0,
229 .num_active_constr = nJ,
230 .num_changing_constr = 0,
231 .exit_reason = DetailedStats::ExitReason::Busy,
232 });
233 }
234
235 if (inner_conv || fail) {
236 increase_penalty_y &= !fail;
237 if (detailed_stats)
238 detailed_stats->entries.back().exit_reason =
241 if (settings.verbose && ctx.is_master()) {
242 int prec = settings.print_precision;
243 const char *status = inner_conv ? "\x1b[0;32mConverged\x1b[0m" /* green */
244 : "\x1b[0;31mFail\x1b[0m" /* red */;
245 std::cout << " Exit inner: " << status << ": #J = " << std::setw(6) << nJ
246 << ", stationarity=" << float_to_str(stationarity, prec)
247 << ", eq constr resid=" << float_to_str(eq_resid, prec) << "\n\n";
248 }
249 leave_inner();
250 break;
251 }
252
253 // Count the number of constraints that changed activity
254 auto active_set_change = timed(timings.active_set_change, [&] {
255 GUANAQO_TRACE("active_set_change", inner_total);
256 return backend.active_set_change(ctx, S, Σ, active_set, active_set_old);
257 });
259 if (detailed_stats)
260 detailed_stats->entries.back().num_changing_constr = active_set_change;
261 // If the active set didn't change for a while, there are numerical issues. We may
262 // be close to convergence, in which case we may try to accept step size τ=1 and hope
263 // for the best. Alternatively, we leave the inner solver and update the multipliers
264 // without increasing the penalty in the outer solver.
265 if (!active_set_change &&
266 ++no_change_active_set >= settings.max_no_changes_active_set) {
267 if (force_τ_1_active_set || !settings.force_linesearch_if_no_set_change) {
268 increase_penalty_y = false;
269 if (detailed_stats)
270 detailed_stats->entries.back().exit_reason =
272 if (settings.verbose && ctx.is_master())
273 std::cout << " Exit inner: \x1b[0;32mNo active set "
274 "change\x1b[0m\n\n";
275 leave_inner();
276 break;
277 }
278 force_τ_1_active_set = true;
279 } else {
280 force_τ_1_active_set = false;
281 }
282
283 // Update regularization: if the constraints are all satisfied and no longer changing,
284 // we can try to remove the regularization to get faster convergence.
285 bool upd_reg_iter = inner == 0 && outer_iter > 0;
286 if (upd_reg_iter && ineq_constr_resid <= settings.dual_tolerance)
287 if (!active_set_change)
288 timed(timings.boost_regularization, [&] {
289 S = backend.boost_regularization(ctx, S, settings.boost_penalty_x);
290 });
291
292 // Solve the Newton system
293 timed(timings.solve, [&] {
294 GUANAQO_TRACE("solve", inner_total);
295 backend.solve(ctx, x, grad, Mᵀλ, Aᵀŷ, Mxb, S, Σ, active_set_old, //
296 d, ξ, Ad, Δλ, MᵀΔλ);
297 });
298 real_t scal_d = 1;
299 if (settings.scale_newton_step) {
300 scal_d = 1 / sqrt(backend.norm_squared(ctx, d));
301 backend.scale(ctx, scal_d, d);
302 backend.scale(ctx, scal_d, ξ);
303 backend.scale(ctx, scal_d, Ad);
304 backend.scale(ctx, scal_d, Δλ);
305 backend.scale(ctx, scal_d, MᵀΔλ);
306 }
307 // Optionally check whether the Newton step is a descent direction. If not, we simply
308 // accept τ=1 and hope for the best.
309 bool force_τ_1_dir_deriv = false;
311 settings.detailed_stats) {
312 backend.xcopy(ctx, grad, grad_add);
313 backend.xaxpy(ctx, 1, Aᵀŷ, grad_add);
314 real_t dir_deriv = backend.dot(ctx, d, grad_add);
315 if (settings.print_directional_deriv && ctx.is_master()) {
316 const char *color = dir_deriv < 0 ? "\x1b[0;33m" /* green */
317 : "\x1b[0;31m" /* red */;
318 std::cout << "dir deriv: " << color << dir_deriv << "\x1b[0m" << std::endl;
319 }
320 if (settings.force_linesearch_if_dir_deriv_pos && dir_deriv > 0)
321 force_τ_1_dir_deriv = true;
322 }
323
324 // Line search
325 bool force_τ_1_first_iter = inner_total == 0;
326 typename decltype(linesearch)::Result ls{.τ = 1 / scal_d, .index = 999999999};
327 // During the first iteration, the equality constraints may not be satisfied, so we
328 // cannot use a line search on the augmented Lagrangian. We just take a single step
329 // with τ=1, which ensures that the next iteration will be feasible w.r.t. the equality
330 // constraints. If the active set doesn't change for a while, the logic above may decide
331 // to force τ=1.
332 if (force_τ_1_active_set || force_τ_1_dir_deriv || force_τ_1_first_iter) {
333 if (settings.verbose && !force_τ_1_first_iter && ctx.is_master())
334 std::cout << " \x1b[0;33mWarning\x1b[0m: Forcing line "
335 "search τ=1\n";
336 } else {
337 // Perform exact line search
338 ls = timed(timings.line_search, [&] {
339 GUANAQO_TRACE("line_search", inner_total);
340 auto [η, β] = [&] {
341 if (settings.linesearch_include_multipliers) {
342 // In theory, d lies in the null space of the equality constraints, so
343 // the multipliers shouldn't have an effect on the line search.
344 // In practice, they do have a numerical effect, and including them in
345 // the line search seems to improve convergence in some cases.
346 auto [η, β, dMᵀΔλ, dMᵀλ] =
347 backend.dots(ctx, d, ξ, d, grad, d, MᵀΔλ, d, Mᵀλ);
348 if (settings.print_linesearch_inputs && ctx.is_master())
349 std::cout << " η = " << η << "\n"
350 << " <d, MᵀΔλ> = " << dMᵀΔλ << "\n"
351 << " β = " << β << "\n"
352 << " <d, Mᵀλ> = " << dMᵀλ << "\n";
353 return std::make_pair(η + dMᵀΔλ, β + dMᵀλ);
354 } else {
355 auto [η, β] = backend.dots(ctx, d, ξ, d, grad);
356 if (settings.print_linesearch_inputs && ctx.is_master())
357 std::cout << " η = " << η << "\n"
358 << " β = " << β << "\n";
359 return std::make_pair(η, β);
360 }
361 }();
362 return linesearch(ctx, backend, η, β, Σ, y, Ad, Ax, backend.Ax_min(),
363 backend.Ax_max());
364 });
365 }
366 if (detailed_stats) {
367 detailed_stats->entries.back().linesearch_step_size = ls.τ;
368 detailed_stats->entries.back().linesearch_breakpoint_index = ls.index;
369 }
370
371 // Print some information about the Newton step and line search.
372 const real_t τ_min = 1e-8 / scal_d, τ_max = 1e2 / scal_d;
373 if (settings.verbose && ctx.is_master()) {
374 int prec = settings.print_precision;
375 const auto eps = cbrt(std::numeric_limits<real_t>::epsilon());
376 const char *color = abs(1 - ls.τ) < eps ? "\x1b[0;32m" /* green */
377 : ls.τ > τ_max ? "\x1b[0;35m" /* pink */
378 : ls.τ > τ_min ? "\x1b[0;33m" /* yellow */
379 : "\x1b[0;31m" /* red */;
380 std::cout << " inner " << std::setw(4) << inner << " (" << std::setw(4)
381 << inner_total << "): #J = " << std::setw(6) << nJ
382 << ", #ΔJ = " << std::setw(6) << active_set_change
383 << ", stationarity=" << float_to_str(stationarity, prec)
384 << ", eq constr resid=" << float_to_str(eq_resid, prec) << ", τ=" << color
385 << float_to_str(ls.τ) << "\x1b[0m (" << ls.index << ")\n";
386 }
387 ls.τ = std::clamp(ls.τ, τ_min, τ_max);
388
389 { // Apply step
390 GUANAQO_TRACE("apply step", inner_total);
391 backend.xaxpy(ctx, ls.τ, d, x);
392 backend.xaxpy(ctx, ls.τ, Δλ, λ);
393 }
394
395 // Optionally recompute Ax and ∇f
396 if (settings.recompute_inner) {
397 timed(timings.recompute_inner, [&] {
398 GUANAQO_TRACE("recompute_inner", inner_total);
399 backend.recompute_inner(ctx, S, x_outer, x, λ, grad, Ax, Mᵀλ);
400 });
401 } else {
402 GUANAQO_TRACE("apply step derived", inner_iter + inner);
403 backend.xaxpy(ctx, ls.τ, Ad, Ax);
404 backend.xaxpy(ctx, ls.τ, MᵀΔλ, Mᵀλ);
405 backend.xaxpy(ctx, ls.τ, ξ, grad);
406 }
407
408 // Compute new equality constraint residual
409 if (settings.recompute_eq_res)
410 timed(timings.mat_vec_M, [&] {
411 backend.eq_constr_resid(ctx, x, Mxb); //
412 });
413 else
414 backend.set_constant(ctx, Mxb, real_t{});
415 }
416 ++outer_iter;
417
418 // Compute constraint violation
419 swap(e, e_old);
420 ineq_constr_resid = backend.ineq_constr_resid_al(ctx, y, ŷ, Σ, e);
421
422 if (settings.recompute) {
423 stationarity = timed(timings.recompute_outer, [&] {
424 GUANAQO_TRACE("recompute_outer", outer_iter);
425 return backend.recompute_outer(ctx, x, Aᵀŷ, λ, grad, Ax, Mᵀλ);
426 });
427 }
428
429 // Print progress
430 if (settings.verbose) {
431 auto nrm_Σ = backend.norm_inf(ctx, Σ);
432 if (ctx.is_master()) {
433 int prec = settings.print_precision;
434 std::cout << "outer " << std::setw(4) << outer_iter
435 << ": stationarity=" << float_to_str(stationarity, prec)
436 << ", constraints=" << float_to_str(ineq_constr_resid, prec)
437 << ", penalty=" << float_to_str(nrm_Σ, prec)
438 << ", regularization=" << float_to_str(1 / S, prec) << '\n'
439 << std::endl;
440 }
441 }
442
443 // Check stopping criteria
444 bool converged = stationarity <= settings.tolerance &&
445 ineq_constr_resid <= settings.dual_tolerance &&
446 (!settings.recompute_eq_res || eq_resid <= settings.eq_constr_tolerance);
447 bool out_of_iter =
448 inner_iter >= settings.max_total_inner_iter || outer_iter >= settings.max_outer_iter;
449 bool out_of_time = clock_t::now() - start_time >= settings.max_time;
450 bool inf_err = !std::isfinite(stationarity + ineq_constr_resid);
451 bool stop = stop_signal.stop_requested();
452 // Return solution
453 if (converged || out_of_iter || out_of_time || inf_err || stop) {
454 stats.inner_iter = inner_iter;
455 stats.outer_iter = outer_iter;
456 stats.detail = std::move(detailed_stats);
457 stats.stationarity = stationarity;
458 stats.primal_residual_norm = ineq_constr_resid;
459 stats.max_penalty = backend.norm_inf(ctx, Σ);
460 stats.timings = std::move(timings);
461 if (ctx.is_master())
462 stats.timings.backend = backend.clear_timings();
463 swap(y, ŷ);
464 backend.project_multipliers_ineq(ctx, y);
465 if (&this->y != &y.get())
466 backend.xcopy(ctx, y.get(), this->y);
467 return converged ? SolverStatus::Converged
468 : out_of_iter ? SolverStatus::MaxIter
469 : out_of_time ? SolverStatus::MaxTime
470 : inf_err ? SolverStatus::NotFinite
473 }
474
475 // Update penalty factors (only if inner solve was successful and if the constraints are not
476 // yet satisfied)
477 if (increase_penalty_y && ineq_constr_resid > settings.dual_tolerance) {
478 index_t num_Σ_changed = update_penalty_y(ctx, backend, Σ, e, e_old, settings);
479 if (num_Σ_changed > 0)
480 timed(stats.timings.update_penalty,
481 [&] { backend.update_penalty_changed(ctx, Σ, num_Σ_changed); });
482 }
483 // Update primal regularization
484 real_t S_old = std::exchange(S, update_penalty_x(S, settings));
485 if (S != S_old) {
486 timed(stats.timings.update_regularization, [&] {
487 backend.update_regularization_changed(ctx, S, S_old); //
488 });
489 }
490 // Update Lagrange multipliers
491 {
492 GUANAQO_TRACE("update multipliers", outer_iter);
493 swap(y, ŷ);
494 backend.project_multipliers_ineq(ctx, y);
495 }
496 // Update tolerances
497 inner_tol = std::max(inner_tol * settings.ρ, settings.tolerance);
498 }
499}
500
501template <class Backend>
503 return backend->num_var();
504}
505template <class Backend>
507 return backend->num_eq_constr();
508}
509template <class Backend>
511 return backend->num_ineq_constr();
512}
513template <class Backend>
515 return static_cast<bool>(impl);
516}
517template <class Backend>
518void Solver<Backend>::get_solution(std::span<real_t> x) const {
519 assert(impl);
520 backend->unscale_variables(impl->x, x);
521}
522template <class Backend>
523std::vector<real_t> Solver<Backend>::get_solution() const {
524 std::vector<real_t> x(backend->num_var());
525 get_solution(x);
526 return x;
527}
528template <class Backend>
529void Solver<Backend>::get_equality_multipliers(std::span<real_t> λ) const {
530 assert(impl);
531 backend->unscale_eq_constr(impl->λ, λ);
532}
533template <class Backend>
534std::vector<real_t> Solver<Backend>::get_equality_multipliers() const {
535 std::vector<real_t> λ(backend->num_eq_constr());
537 return λ;
538}
539template <class Backend>
540void Solver<Backend>::get_equality_constraints(std::span<real_t> Mxb) const {
541 assert(impl);
542 backend->unscale_eq_constr(impl->Mxb, Mxb);
543}
544template <class Backend>
545std::vector<real_t> Solver<Backend>::get_equality_constraints() const {
546 std::vector<real_t> Mxb(backend->num_eq_constr());
548 return Mxb;
549}
550template <class Backend>
551void Solver<Backend>::get_inequality_multipliers(std::span<real_t> y) const {
552 assert(impl);
553 backend->unscale_ineq_constr(impl->y, y);
554}
555template <class Backend>
557 std::vector<real_t> y(backend->num_ineq_constr());
559 return y;
560}
561template <class Backend>
562void Solver<Backend>::get_inequality_constraints(std::span<real_t> Ax) const {
563 assert(impl);
564 backend->unscale_ineq_constr(impl->Ax, Ax);
565}
566template <class Backend>
568 std::vector<real_t> x(backend->num_ineq_constr());
570 return x;
571}
572template <class Backend>
574 assert(impl);
575 backend->warm_start(impl->x, impl->y, impl->λ);
576}
577template <class Backend>
578void Solver<Backend>::set_initial_guess(std::span<const real_t> x, std::span<const real_t> y,
579 std::span<const real_t> λ) {
580 if (!backend->x0)
581 backend->x0.emplace(backend->var_vec());
582 backend->scale_variables(x, *(backend->x0));
583 if (!backend->y0)
584 backend->y0.emplace(backend->ineq_constr_vec());
585 backend->scale_ineq_constr(y, *(backend->y0));
586 if (!backend->λ0)
587 backend->λ0.emplace(backend->eq_constr_vec());
588 backend->scale_eq_constr(λ, *(backend->λ0));
589}
590template <class Backend>
591bool Solver<Backend>::get_initial_guess(std::span<real_t> x, std::span<real_t> y,
592 std::span<real_t> λ) {
593 if (!backend->x0 || !backend->y0 || !backend->λ0)
594 return false;
595 backend->unscale_variables(*(backend->x0), x);
596 backend->unscale_ineq_constr(*(backend->y0), y);
597 backend->unscale_eq_constr(*(backend->λ0), λ);
598 return true;
599}
600template <class Backend>
601void Solver<Backend>::set_b_eq(std::span<const real_t> b_eq) {
602 backend->set_b_eq(b_eq);
603}
604template <class Backend>
605void Solver<Backend>::set_b_lb(std::span<const real_t> b_lb) {
606 backend->set_b_lb(b_lb);
607}
608template <class Backend>
609void Solver<Backend>::set_b_ub(std::span<const real_t> b_ub) {
610 backend->set_b_ub(b_ub);
611}
612// template <class Backend>
613// void Solver<Backend>::get_penalty_factors(std::span<real_t>) const {
614// backend->unscale(...);
615// }
616// template <class Backend>
617// std::vector<real_t> Solver<Backend>::get_penalty_factors() const {
618// std::vector<real_t> x(backend->num_ineq_constr());
619// get_penalty_factors(x);
620// return x;
621// }
622
623template <class Backend>
626template <class Backend>
627Solver<Backend>::Solver(Solver &&) noexcept = default;
628template <class Backend>
629Solver<Backend> &Solver<Backend>::operator=(Solver &&) noexcept = default;
630template <class Backend>
631Solver<Backend>::~Solver() = default;
632
633} // namespace CYQLONE_NS(cyqlone::qpalm)
std::optional< BackendStats > stats_backend
Definition solver.hpp:102
guanaqo::AtomicStopSignal stop_signal
Definition solver.hpp:103
std::optional< SolverStats > stats
Definition solver.hpp:101
void get_inequality_constraints(std::span< real_t >) const
Definition solver.tpp:562
void get_equality_multipliers(std::span< real_t >) const
Definition solver.tpp:529
void get_equality_constraints(std::span< real_t >) const
Definition solver.tpp:540
std::unique_ptr< SolverImplementation< backend_type > > impl
Definition solver.hpp:100
Solver(Backend backend, Settings settings={})
Definition solver.tpp:624
void get_inequality_multipliers(std::span< real_t >) const
Definition solver.tpp:551
void get_solution(std::span< real_t >) const
Definition solver.tpp:518
#define CYQLONE_NS(ns)
Definition config.hpp:10
std::string float_to_str(F value, int precision)
std::optional< DetailedStats > detail
Definition solver.hpp:49
SolverStatus
Exit status of a numerical solver.
Definition status.hpp:12
@ Interrupted
Solver was interrupted by the user.
Definition status.hpp:19
@ MaxTime
Maximum allowed execution time exceeded.
Definition status.hpp:15
@ MaxIter
Maximum number of iterations exceeded.
Definition status.hpp:16
@ Converged
Converged and reached given tolerance.
Definition status.hpp:14
@ NotFinite
Intermediate results were infinite or not-a-number.
Definition status.hpp:17
#define GUANAQO_TRACE(name, instance,...)
decltype(auto) get_solution(Context &ctx, view<> λ, auto &&func) const
Definition cyqlone.hpp:202
unsigned max_no_changes_active_set
Definition settings.hpp:40
real_t tolerance
Primal tolerance.
Definition settings.hpp:19
unsigned max_outer_iter
Maximum number of (total) iterations.
Definition settings.hpp:14
std::chrono::microseconds max_time
Definition settings.hpp:17
typename backend_type::eq_constr_vec_t eq_vec_t
Definition solver.tpp:26
static void initialize_penalty_y(Backend::Context &ctx, backend_type &backend, real_t f0, const ineq_vec_t &e0, ineq_vec_t &Σ, const Settings &settings)
Definition solver.tpp:30
typename backend_type::var_vec_t var_vec_t
Definition solver.tpp:27
void ensure_storage(backend_type &backend)
Definition solver.tpp:81
typename backend_type::active_set_t active_set_t
Definition solver.tpp:24
LineSearch< ineq_vec_t > linesearch
Definition solver.tpp:28
SolverStatus do_main_loop(Backend::Context &ctx, backend_type &backend, const Settings &settings, guanaqo::AtomicStopSignal &stop_signal, SolverStats &stats)
Definition solver.tpp:124
static real_t update_penalty_x(real_t S, const Settings &settings)
Definition solver.tpp:76
typename backend_type::ineq_constr_vec_t ineq_vec_t
Definition solver.tpp:25
static index_t update_penalty_y(Backend::Context &ctx, backend_type &backend, ineq_vec_t &Σ, const ineq_vec_t &e, const ineq_vec_t &e_old, const Settings &settings)
Definition solver.tpp:44
std::map< std::string, DefaultTimings > backend
Definition solver.hpp:35
DefaultTimings recompute_inner
Definition solver.hpp:23
DefaultTimings update_penalty
Definition solver.hpp:31
DefaultTimings boost_regularization
Definition solver.hpp:33
DefaultTimings line_search
Definition solver.hpp:22
DefaultTimings mat_vec_AT
Definition solver.hpp:28
DefaultTimings update_regularization
Definition solver.hpp:32
DefaultTimings active_set_change
Definition solver.hpp:30
DefaultTimings mat_vec_MT
Definition solver.hpp:26
DefaultTimings recompute_outer
Definition solver.hpp:24