alpaqa 0.0.1
Nonconvex constrained optimization
alm.hpp
Go to the documentation of this file.
1#pragma once
2
5
6#include <iomanip>
7#include <iostream>
8
9namespace alpaqa {
10
11using std::chrono::duration_cast;
12using std::chrono::microseconds;
13
14template <class InnerSolverT>
15typename ALMSolver<InnerSolverT>::Stats
17 auto start_time = std::chrono::steady_clock::now();
18
19 constexpr auto sigNaN = std::numeric_limits<real_t>::signaling_NaN();
20 vec Σ = vec::Constant(problem.m, sigNaN);
21 vec Σ_old = vec::Constant(problem.m, sigNaN);
22 vec error₁ = vec::Constant(problem.m, sigNaN);
23 vec error₂ = vec::Constant(problem.m, sigNaN);
24 real_t norm_e₁ = sigNaN;
25 real_t norm_e₂ = sigNaN;
26
27 Stats s;
28
29 Problem prec_problem;
30 real_t prec_f;
31 vec prec_g;
32
33 if (params.preconditioning)
34 detail::apply_preconditioning(problem, prec_problem, x, prec_f, prec_g);
35 const auto &p = params.preconditioning ? prec_problem : problem;
36
37 // Initialize the penalty weights
38 if (params.Σ₀ > 0) {
39 Σ.fill(params.Σ₀);
40 }
41 // Initial penalty weights from problem
42 else {
44 }
45
46 real_t ε = params.ε₀;
47 real_t ε_old = sigNaN;
48 real_t Δ = params.Δ;
49 real_t ρ = params.ρ;
50 bool first_successful_iter = true;
51
52 for (unsigned int i = 0; i < params.max_iter; ++i) {
53 // TODO: this is unnecessary when the previous iteration lowered the
54 // penalty update factor.
55 detail::project_y(y, p.D.lowerbound, p.D.upperbound, params.M);
56 // Check if we're allowed to lower the penalty factor even further.
57 bool out_of_penalty_factor_updates =
58 (first_successful_iter
59 ? s.initial_penalty_reduced == params.max_num_initial_retries
60 : s.penalty_reduced == params.max_num_retries) ||
62 params.max_total_num_retries);
63 bool out_of_iter = i + 1 == params.max_iter;
64 // If this is the final iteration, or the final chance to reduce the
65 // penalty update factor, the inner solver can just return its results,
66 // even if it doesn't converge.
67 bool overwrite_results = out_of_iter || out_of_penalty_factor_updates;
68
69 // Inner solver
70 // ------------
71
72 // Call the inner solver to minimize the augmented lagrangian for fixed
73 // Lagrange multipliers y.
74 auto ps = inner_solver(p, Σ, ε, overwrite_results, x, y, error₂);
75 bool inner_converged = ps.status == SolverStatus::Converged;
76 // Accumulate the inner solver statistics
77 s.inner_convergence_failures += not inner_converged;
78 s.inner += ps;
79
80 auto time_elapsed = std::chrono::steady_clock::now() - start_time;
81 bool out_of_time = time_elapsed > params.max_time;
82 bool backtrack =
83 not inner_converged && not overwrite_results && not out_of_time;
84
85 // Print statistics of current iteration
86 if (params.print_interval != 0 && i % params.print_interval == 0) {
87 real_t δ = backtrack ? NaN : vec_util::norm_inf(error₂);
88 auto color = inner_converged ? "\x1b[0;32m" : "\x1b[0;31m";
89 auto color_end = "\x1b[0m";
90 std::cout << "[\x1b[0;34mALM\x1b[0m] " << std::setw(5) << i
91 << ": ‖Σ‖ = " << std::setw(13) << Σ.norm()
92 << ", ‖y‖ = " << std::setw(13) << y.norm()
93 << ", δ = " << std::setw(13) << δ
94 << ", ε = " << std::setw(13) << ps.ε
95 << ", Δ = " << std::setw(13) << Δ
96 << ", status = " << color << std::setw(13) << ps.status
97 << color_end << ", iter = " << std::setw(13)
98 << ps.iterations << "\r\n";
99 }
100
101 // TODO: check penalty size?
102 if (ps.status == SolverStatus::Interrupted) {
103 s.ε = ps.ε;
104 s.δ = vec_util::norm_inf(error₂);
105 s.norm_penalty = Σ.norm();
106 s.outer_iterations = i + 1;
107 s.elapsed_time = duration_cast<microseconds>(time_elapsed);
108 s.status = ps.status;
109 if (params.preconditioning)
110 y = prec_g.asDiagonal() * y / prec_f;
111 return s;
112 }
113
114 // Backtrack and lower penalty if inner solver did not converge
115 if (backtrack) {
116 // This means the inner solver didn't produce a solution that
117 // satisfies the required tolerance.
118 // The best thing we can do now is to restore the penalty to its
119 // previous value (when the inner solver did converge), then lower
120 // the penalty factor, and update the penalty with this smaller
121 // factor.
122 // error₂ was not overwritten by the inner solver, so it still
123 // contains the error from the iteration before the previous
124 // successful iteration. error₁ contains the error of the last
125 // successful iteration.
126 if (not first_successful_iter) {
127 // We have a previous Σ and error
128 // Recompute penalty with smaller Δ
129 Δ = std::fmax(1., Δ * params.Δ_lower);
130 detail::update_penalty_weights(params, Δ, first_successful_iter,
131 error₁, error₂, norm_e₁, norm_e₂,
132 Σ_old, Σ);
133 // Recompute the primal tolerance with larger ρ
134 ρ = std::fmin(0.5, ρ * params.ρ_increase); // keep ρ <= 0.5
135 ε = std::fmax(ρ * ε_old, params.ε);
136 ++s.penalty_reduced;
137 } else {
138 // We don't have a previous Σ, simply lower the current Σ and
139 // increase ε
140 Σ *= params.Σ₀_lower;
141 ε *= params.ε₀_increase;
143 }
144 }
145
146 // If the inner solver did converge, increase penalty
147 else {
148 // After this line, error₁ contains the error of the current
149 // (successful) iteration, and error₂ contains the error of the
150 // previous successful iteration.
151 error₂.swap(error₁);
152 norm_e₂ = std::exchange(norm_e₁, vec_util::norm_inf(error₁));
153
154 // Check the termination criteria
155 bool alm_converged =
156 ps.ε <= params.ε && inner_converged && norm_e₁ <= params.δ;
157 bool exit = alm_converged || out_of_iter || out_of_time;
158 if (exit) {
159 s.ε = ps.ε;
160 s.δ = norm_e₁;
161 s.norm_penalty = Σ.norm();
162 s.outer_iterations = i + 1;
163 s.elapsed_time = duration_cast<microseconds>(time_elapsed);
164 s.status = alm_converged ? SolverStatus::Converged
165 : out_of_time ? SolverStatus::MaxTime
166 : out_of_iter ? SolverStatus::MaxIter
168 if (params.preconditioning)
169 y = prec_g.asDiagonal() * y / prec_f;
170 return s;
171 }
172 // After this line, Σ_old contains the penalty used in the current
173 // (successful) iteration.
174 Σ_old.swap(Σ);
175 // Update Σ to contain the penalty to use on the next iteration.
176 detail::update_penalty_weights(params, Δ, first_successful_iter,
177 error₁, error₂, norm_e₁, norm_e₂,
178 Σ_old, Σ);
179 // Lower the primal tolerance for the inner solver.
180 ε_old = std::exchange(ε, std::fmax(ρ * ε, params.ε));
181 first_successful_iter = false;
182 }
183 }
184 throw std::logic_error("[ALM] loop error");
185}
186
187} // namespace alpaqa
unsigned penalty_reduced
The number of times that the penalty update factor ALMParams::Δ was reduced, that the tolerance updat...
Definition: decl/alm.hpp:110
real_t δ
Final dual tolerance or constraint violation that was reached:
Definition: decl/alm.hpp:121
Stats operator()(const Problem &problem, rvec y, rvec x)
Definition: alm.hpp:16
real_t norm_penalty
2-norm of the final penalty factors .
Definition: decl/alm.hpp:123
unsigned initial_penalty_reduced
The number of times that the initial penalty factor was reduced by ALMParams::Σ₀_lower and that the i...
Definition: decl/alm.hpp:100
InnerStatsAccumulator< typename InnerSolver::Stats > inner
The statistics of the inner solver invocations, accumulated over all ALM iterations.
Definition: decl/alm.hpp:131
unsigned inner_convergence_failures
The total number of times that the inner solver failed to converge.
Definition: decl/alm.hpp:112
real_t ε
Final primal tolerance that was reached, depends on the stopping criterion used by the inner solver,...
Definition: decl/alm.hpp:116
unsigned outer_iterations
Total number of outer ALM iterations (i.e.
Definition: decl/alm.hpp:90
std::chrono::microseconds elapsed_time
Total elapsed time.
Definition: decl/alm.hpp:92
SolverStatus status
Whether the solver converged or not.
Definition: decl/alm.hpp:127
void project_y(rvec y, crvec z_lb, crvec z_ub, real_t M)
Definition: alm-helpers.hpp:8
void update_penalty_weights(const ALMParams &params, real_t Δ, bool first_iter, rvec e, rvec old_e, real_t norm_e, real_t old_norm_e, crvec Σ_old, rvec Σ)
Definition: alm-helpers.hpp:25
void initialize_penalty(const Problem &p, const ALMParams &params, crvec x0, rvec Σ)
Definition: alm-helpers.hpp:53
void apply_preconditioning(const Problem &problem, Problem &prec_problem, crvec x, real_t &prec_f, vec &prec_g)
Definition: alm-helpers.hpp:66
int Σ
Definition: test.py:72
int ε
Definition: test.py:73
real_t norm_inf(const Vec &v)
Get the maximum or infinity-norm of the given vector.
Definition: vec.hpp:42
constexpr real_t NaN
Not a number.
Definition: vec.hpp:28
@ Interrupted
Solver was interrupted by the user.
@ Unknown
Initial value.
@ MaxTime
Maximum allowed execution time exceeded.
@ MaxIter
Maximum number of iterations exceeded.
@ Converged
Converged and reached given tolerance.
realvec vec
Default type for vectors.
Definition: vec.hpp:14
double real_t
Default floating point type.
Definition: vec.hpp:8
Eigen::Ref< vec > rvec
Default type for mutable references to vectors.
Definition: vec.hpp:16
problem
Definition: main.py:16
Problem description for minimization problems.