alpaqa 1.1.0a1
Nonconvex constrained optimization
Loading...
Searching...
No Matches
alm.tpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <algorithm>
6#include <iomanip>
7#include <iostream>
8#include <utility>
9
14#include <alpaqa/util/print.hpp>
15
16namespace alpaqa {
17
18template <class InnerSolverT>
21 std::optional<rvec> Σ) {
22 using std::chrono::duration_cast;
23 using std::chrono::nanoseconds;
24 auto start_time = std::chrono::steady_clock::now();
25
26 // Check the problem dimensions etc.
27 p.check();
28
29 if (params.max_iter == 0)
30 return {.status = SolverStatus::MaxIter};
31
32 auto m = p.get_num_constraints();
33 if (m == 0) { // No general constraints, only box constraints
34 Stats s;
35 vec Σ_curr(0), error(0);
37 .always_overwrite_results = true,
38 .max_time = params.max_time,
39 .tolerance = params.tolerance,
40 .os = os,
41 .check = false,
42 };
43 auto ps = inner_solver(p, opts, x, y, Σ_curr, error);
44 bool inner_converged = ps.status == SolverStatus::Converged;
45 auto time_elapsed = std::chrono::steady_clock::now() - start_time;
46 s.inner_convergence_failures = not inner_converged;
47 s.inner += ps;
48 s.ε = ps.ε;
49 s.δ = 0;
50 s.norm_penalty = 0;
51 s.outer_iterations = 1;
52 s.elapsed_time = duration_cast<nanoseconds>(time_elapsed);
53 s.status = ps.status;
54 return s;
55 }
56
57 constexpr auto NaN = alpaqa::NaN<config_t>;
58 vec Σ_curr = vec::Constant(m, NaN);
59 vec error = vec::Constant(m, NaN);
60 vec error_old = vec::Constant(m, NaN);
61 real_t norm_e = NaN, norm_e_old = NaN;
62
63 std::array<char, 64> printbuf;
64 auto print_real = [&](real_t x) {
65 return float_to_str_vw(printbuf, x, params.print_precision);
66 };
67
68 Stats s;
69
70 if (Σ && Σ->allFinite() && Σ->norm() > 0) {
71 Σ_curr = *Σ;
72 }
73 // Initialize the penalty weights
74 else if (params.initial_penalty > 0) {
75 Σ_curr.setConstant(params.initial_penalty);
76 }
77 // Initial penalty weights from problem
78 else {
79 Helpers::initialize_penalty(p, params, x, Σ_curr);
80 }
81
82 // Inner solver tolerance
83 real_t ε = params.initial_tolerance;
84
85 for (unsigned i = 0; i < params.max_iter; ++i) {
86 p.eval_projection_multipliers(y, params.max_multiplier);
87 bool out_of_iter = i + 1 == params.max_iter;
88
89 auto time_elapsed = std::chrono::steady_clock::now() - start_time;
90 auto time_remaining = time_elapsed < params.max_time
91 ? params.max_time - time_elapsed
92 : decltype(time_elapsed){0};
94 .always_overwrite_results = true,
95 .max_time = time_remaining,
96 .tolerance = ε,
97 .os = os,
98 .outer_iter = i,
99 .check = false,
100 };
101 // Call the inner solver to minimize the augmented lagrangian for fixed
102 // Lagrange multipliers y.
103 auto ps = inner_solver(p, opts, x, y, Σ_curr, error);
104 // Check if the inner solver converged
105 bool inner_converged = ps.status == SolverStatus::Converged;
106 // Accumulate the inner solver statistics
107 s.inner_convergence_failures += not inner_converged;
108 s.inner += ps;
109 // Compute the constraint violation
110 using vec_util::norm_inf;
111 norm_e = norm_inf(error);
112
113 time_elapsed = std::chrono::steady_clock::now() - start_time;
114 bool out_of_time = time_elapsed > params.max_time;
115
116 // Print statistics of current iteration
117 if (params.print_interval != 0 && i % params.print_interval == 0) {
118 const char *color = inner_converged ? "\x1b[0;32m" : "\x1b[0;31m";
119 const char *color_end = "\x1b[0m";
120 *os << "[\x1b[0;34mALM\x1b[0m] " << std::setw(5) << i
121 << ": ‖Σ‖ = " << print_real(Σ_curr.norm())
122 << ", ‖y‖ = " << print_real(y.norm())
123 << ", δ = " << print_real(norm_e)
124 << ", ε = " << print_real(ps.ε) << ", status = " << color
125 << std::setw(13) << ps.status << color_end
126 << ", iter = " << std::setw(13) << ps.iterations
127 << std::endl; // Flush for Python buffering
128 }
129
130 // TODO: check penalty size?
131 if (ps.status == SolverStatus::Interrupted) {
132 s.ε = ps.ε;
133 s.δ = norm_e;
134 s.norm_penalty = Σ_curr.norm() / std::sqrt(real_t(m));
135 s.outer_iterations = i + 1;
136 s.elapsed_time = duration_cast<nanoseconds>(time_elapsed);
137 s.status = ps.status;
138 if (Σ)
139 *Σ = Σ_curr;
140 return s;
141 }
142
143 // Check the termination criteria
144 bool alm_converged = ps.ε <= params.tolerance && inner_converged &&
145 norm_e <= params.dual_tolerance;
146 bool exit = alm_converged || out_of_iter || out_of_time;
147 if (exit) {
148 s.ε = ps.ε;
149 s.δ = norm_e;
150 s.norm_penalty = Σ_curr.norm() / std::sqrt(real_t(m));
151 s.outer_iterations = i + 1;
152 s.elapsed_time = duration_cast<nanoseconds>(time_elapsed);
153 s.status = alm_converged ? SolverStatus::Converged
154 : out_of_time ? SolverStatus::MaxTime
155 : out_of_iter ? SolverStatus::MaxIter
157 if (Σ)
158 *Σ = Σ_curr;
159 return s;
160 }
161 // Update Σ to contain the penalty to use on the next iteration.
162 Helpers::update_penalty_weights(params, params.penalty_update_factor,
163 i == 0, error, error_old, norm_e,
164 norm_e_old, Σ_curr);
165 // Lower the primal tolerance for the inner solver.
166 ε = std::fmax(params.tolerance_update_factor * ε, params.tolerance);
167 // Save previous error
168 norm_e_old = norm_e;
169 error.swap(error_old);
170 }
171 throw std::logic_error("[ALM] loop error");
172}
173
174} // namespace alpaqa
Params params
Definition alm.hpp:131
real_t δ
Final dual tolerance or constraint violation that was reached:
Definition alm.hpp:93
real_t norm_penalty
2-norm of the final penalty factors .
Definition alm.hpp:95
typename InnerSolver::Problem Problem
Definition alm.hpp:75
std::chrono::nanoseconds elapsed_time
Total elapsed time.
Definition alm.hpp:82
InnerStatsAccumulator< typename InnerSolver::Stats > inner
The statistics of the inner solver invocations, accumulated over all ALM iterations.
Definition alm.hpp:103
unsigned inner_convergence_failures
The total number of times that the inner solver failed to converge.
Definition alm.hpp:84
real_t ε
Final primal tolerance that was reached, depends on the stopping criterion used by the inner solver,...
Definition alm.hpp:88
unsigned outer_iterations
Total number of outer ALM iterations (i.e.
Definition alm.hpp:80
InnerSolver inner_solver
Definition alm.hpp:135
Stats operator()(const Problem &problem, rvec x, rvec y, std::optional< rvec > Σ=std::nullopt)
Definition alm.tpp:20
SolverStatus status
Whether the solver converged or not.
Definition alm.hpp:99
std::ostream * os
Definition alm.hpp:136
auto norm_inf(const Eigen::MatrixBase< Derived > &v)
Get the maximum or infinity-norm of the given vector.
Definition config.hpp:204
@ Interrupted
Solver was interrupted by the user.
@ MaxTime
Maximum allowed execution time exceeded.
@ MaxIter
Maximum number of iterations exceeded.
@ Converged
Converged and reached given tolerance.
typename Conf::real_t real_t
Definition config.hpp:86
constexpr const auto NaN
Definition config.hpp:114
typename Conf::rvec rvec
Definition config.hpp:91
typename Conf::vec vec
Definition config.hpp:88
static void update_penalty_weights(const ALMParams< config_t > &params, real_t Δ, bool first_iter, rvec e, rvec old_e, real_t norm_e, real_t old_norm_e, rvec Σ)
static void initialize_penalty(const TypeErasedProblem< config_t > &p, const ALMParams< config_t > &params, crvec x0, rvec Σ)