alpaqa 0.0.1
Nonconvex constrained optimization
structured-panoc-lbfgs.hpp
Go to the documentation of this file.
1#pragma once
2
6
7#include <cassert>
8#include <cmath>
9#include <iomanip>
10#include <iostream>
11#include <stdexcept>
12
13namespace alpaqa {
14
15using std::chrono::duration_cast;
16using std::chrono::microseconds;
17
19 /// [in] Problem description
20 const Problem &problem,
21 /// [in] Constraint weights @f$ \Sigma @f$
22 crvec Σ,
23 /// [in] Tolerance @f$ \varepsilon @f$
24 real_t ε,
25 /// [in] Overwrite @p x, @p y and @p err_z even if not converged
26 bool always_overwrite_results,
27 /// [inout] Decision variable @f$ x @f$
28 rvec x,
29 /// [inout] Lagrange multipliers @f$ y @f$
30 rvec y,
31 /// [out] Slack variable error @f$ g(x) - z @f$
32 rvec err_z) {
33
34 auto start_time = std::chrono::steady_clock::now();
35 Stats s;
36
37 const auto n = problem.n;
38 const auto m = problem.m;
39
40 // Allocate vectors, init L-BFGS -------------------------------------------
41
42 // TODO: the L-BFGS objects and vectors allocate on each iteration of ALM,
43 // and there are more vectors than strictly necessary.
44
46
47 vec xₖ = x, // Value of x at the beginning of the iteration
48 x̂ₖ(n), // Value of x after a projected gradient step
49 xₖ₊₁(n), // xₖ for next iteration
50 x̂ₖ₊₁(n), // x̂ₖ for next iteration
51 ŷx̂ₖ(m), // ŷ(x̂ₖ) = Σ (g(x̂ₖ) - ẑₖ)
52 ŷx̂ₖ₊₁(m), // ŷ(x̂ₖ) for next iteration
53 pₖ(n), // Projected gradient step pₖ = x̂ₖ - xₖ
54 pₖ₊₁(n), // Projected gradient step pₖ₊₁ = x̂ₖ₊₁ - xₖ₊₁
55 qₖ(n), // Newton step Hₖ pₖ
56 grad_ψₖ(n), // ∇ψ(xₖ)
57 grad_̂ψₖ(need_grad_̂ψₖ ? n : 0), // ∇ψ(x̂ₖ)
58 grad_ψₖ₊₁(n); // ∇ψ(xₖ₊₁)
59
60 vec work_n(n), work_m(m);
61
62 vec work_n2(n);
63 vec HqK(n);
64
65 using indexvec = std::vector<vec::Index>;
66 indexvec J;
67 J.reserve(n);
68 lbfgs.resize(n);
69
70 // Keep track of how many successive iterations didn't update the iterate
71 unsigned no_progress = 0;
72 unsigned last_γ_change = 0;
73
74 // Helper functions --------------------------------------------------------
75
76 // Wrappers for helper functions that automatically pass along any arguments
77 // that are constant within PANOC (for readability in the main algorithm)
78 auto calc_ψ_ŷ = [&problem, &y, &Σ](crvec x, rvec ŷ) {
79 return detail::calc_ψ_ŷ(problem, x, y, Σ, ŷ);
80 };
81 auto calc_ψ_grad_ψ = [&problem, &y, &Σ, &work_n, &work_m](crvec x,
82 rvec grad_ψ) {
83 return detail::calc_ψ_grad_ψ(problem, x, y, Σ, grad_ψ, work_n, work_m);
84 };
85 auto calc_grad_ψ_from_ŷ = [&problem, &work_n](crvec x, crvec ŷ,
86 rvec grad_ψ) {
87 detail::calc_grad_ψ_from_ŷ(problem, x, ŷ, grad_ψ, work_n);
88 };
89 auto calc_x̂ = [&problem](real_t γ, crvec x, crvec grad_ψ, rvec x̂, rvec p) {
90 detail::calc_x̂(problem, γ, x, grad_ψ, x̂, p);
91 };
92 auto calc_err_z = [&problem, &y, &Σ](crvec x̂, rvec err_z) {
94 };
95 auto descent_lemma = [this, &problem, &y,
96 &Σ](crvec xₖ, real_t ψₖ, crvec grad_ψₖ, rvec x̂ₖ,
97 rvec pₖ, rvec ŷx̂ₖ, real_t &ψx̂ₖ, real_t &pₖᵀpₖ,
98 real_t &grad_ψₖᵀpₖ, real_t &Lₖ, real_t &γₖ) {
101 xₖ, ψₖ, grad_ψₖ, y, Σ, x̂ₖ, pₖ, ŷx̂ₖ, ψx̂ₖ, pₖᵀpₖ, grad_ψₖᵀpₖ, Lₖ, γₖ);
102 };
103 auto print_progress = [&](unsigned k, real_t ψₖ, crvec grad_ψₖ,
104 real_t pₖᵀpₖ, real_t γₖ, real_t εₖ) {
105 std::cout << "[PANOC] " << std::setw(6) << k
106 << ": ψ = " << std::setw(13) << ψₖ
107 << ", ‖∇ψ‖ = " << std::setw(13) << grad_ψₖ.norm()
108 << ", ‖p‖ = " << std::setw(13) << std::sqrt(pₖᵀpₖ)
109 << ", γ = " << std::setw(13) << γₖ
110 << ", εₖ = " << std::setw(13) << εₖ << "\r\n";
111 };
112
113 // Estimate Lipschitz constant ---------------------------------------------
114
115 real_t ψₖ, Lₖ;
116 // Finite difference approximation of ∇²ψ in starting point
117 if (params.Lipschitz.L₀ <= 0) {
121 /* in ⟹ out */ ψₖ, grad_ψₖ, x̂ₖ, grad_ψₖ₊₁, work_n, work_m);
122 }
123 // Initial Lipschitz constant provided by the user
124 else {
125 Lₖ = params.Lipschitz.L₀;
126 // Calculate ψ(xₖ), ∇ψ(x₀)
127 ψₖ = calc_ψ_grad_ψ(xₖ, /* in ⟹ out */ grad_ψₖ);
128 }
129 if (not std::isfinite(Lₖ)) {
131 return s;
132 }
133 real_t γₖ = params.Lipschitz.Lγ_factor / Lₖ;
134 real_t τ = NaN;
135
136 // First projected gradient step -------------------------------------------
137
138 // Calculate x̂₀, p₀ (projected gradient step)
139 calc_x̂(γₖ, xₖ, grad_ψₖ, /* in ⟹ out */ x̂ₖ, pₖ);
140 // Calculate ψ(x̂ₖ) and ŷ(x̂ₖ)
141 real_t ψx̂ₖ = calc_ψ_ŷ(x̂ₖ, /* in ⟹ out */ ŷx̂ₖ);
142 real_t grad_ψₖᵀpₖ = grad_ψₖ.dot(pₖ);
143 real_t pₖᵀpₖ = pₖ.squaredNorm();
144 // Compute forward-backward envelope
145 real_t φₖ = ψₖ + 1 / (2 * γₖ) * pₖᵀpₖ + grad_ψₖᵀpₖ;
146 real_t nmΦₖ = φₖ;
147
148 // Main PANOC loop
149 // =========================================================================
150 for (unsigned k = 0; k <= params.max_iter; ++k) {
151
152 // Heuristically try to increase step size
153 bool force_qub_check = false;
154 bool skip_qub_check = false;
155 if (k > 0 && params.hessian_step_size_heuristic > 0 &&
156 k - last_γ_change > params.hessian_step_size_heuristic) {
158 problem, xₖ, y, Σ, grad_ψₖ, grad_ψₖ, HqK, work_n, work_n2,
159 work_m);
160 real_t η = grad_ψₖ.squaredNorm() / grad_ψₖ.dot(HqK);
161 if (η > γₖ * 10)
162 η = γₖ * 10;
163 if (η > γₖ) {
164 real_t Lₖ₊₁ = params.Lipschitz.Lγ_factor / η;
165 Lₖ₊₁ = std::clamp(Lₖ₊₁, params.L_min, params.L_max);
166 real_t γₖ₊₁ = params.Lipschitz.Lγ_factor / Lₖ₊₁;
167 // Calculate x̂ₖ, pₖ (projected gradient step)
168 calc_x̂(γₖ₊₁, xₖ, grad_ψₖ, /* in ⟹ out */ x̂ₖ₊₁, pₖ₊₁);
169 // Calculate ∇ψ(xₖ)ᵀpₖ and ‖pₖ‖²
170 real_t grad_ψₖ₊₁ᵀpₖ₊₁ = grad_ψₖ.dot(pₖ₊₁);
171 real_t pₖ₊₁ᵀpₖ₊₁ = pₖ₊₁.squaredNorm();
172 // Calculate ψ(x̂ₖ) and ŷ(x̂ₖ)
173 real_t ψx̂ₖ₊₁ = calc_ψ_ŷ(x̂ₖ₊₁, /* in ⟹ out */ ŷx̂ₖ₊₁);
174 // Check quadratic upper bound
175 real_t margin = (1 + std::abs(ψₖ)) *
177 constexpr bool always_accept = false;
178 if (always_accept || ψx̂ₖ₊₁ - ψₖ <= grad_ψₖ₊₁ᵀpₖ₊₁ +
179 0.5 * Lₖ₊₁ * pₖ₊₁ᵀpₖ₊₁ +
180 margin) {
181 // If QUB is satisfied, accept new, larger step
182 Lₖ = Lₖ₊₁;
183 γₖ = γₖ₊₁;
184 x̂ₖ.swap(x̂ₖ₊₁);
185 pₖ.swap(pₖ₊₁);
186 grad_ψₖᵀpₖ = grad_ψₖ₊₁ᵀpₖ₊₁;
187 pₖᵀpₖ = pₖ₊₁ᵀpₖ₊₁;
188 ψx̂ₖ = ψx̂ₖ₊₁;
189 // Recompute forward-backward envelope
190 φₖ = ψₖ + 1 / (2 * γₖ) * pₖᵀpₖ + grad_ψₖᵀpₖ;
191 // No need to re-check later
192 skip_qub_check = not always_accept;
193 force_qub_check = always_accept;
194 last_γ_change = k;
195 } else {
196 // nothing is updated (except x̂ₖ₊₁, pₖ₊₁, ŷx̂ₖ₊₁, which will
197 // be overwritten before the next use)
198 }
199 }
200 }
201
202 // Quadratic upper bound -----------------------------------------------
203 bool qub_check =
204 k == 0 || params.update_lipschitz_in_linesearch == false;
205 if ((qub_check && !skip_qub_check) || force_qub_check) {
206 // Decrease step size until quadratic upper bound is satisfied
207 real_t old_γₖ =
208 descent_lemma(xₖ, ψₖ, grad_ψₖ,
209 /* in ⟹ out */ x̂ₖ, pₖ, ŷx̂ₖ,
210 /* inout */ ψx̂ₖ, pₖᵀpₖ, grad_ψₖᵀpₖ, Lₖ, γₖ);
211 if (γₖ != old_γₖ) {
212 last_γ_change = k;
213 φₖ = ψₖ + 1 / (2 * γₖ) * pₖᵀpₖ + grad_ψₖᵀpₖ;
214 }
215 }
216 // Calculate ∇ψ(x̂ₖ)
217 if (need_grad_̂ψₖ)
218 calc_grad_ψ_from_ŷ(x̂ₖ, ŷx̂ₖ, /* in ⟹ out */ grad_̂ψₖ);
219
220 // Check stop condition ------------------------------------------------
222 problem.C, params.stop_crit, pₖ, γₖ, xₖ, x̂ₖ, ŷx̂ₖ, grad_ψₖ, grad_̂ψₖ);
223
224 // Print progress
225 if (params.print_interval != 0 && k % params.print_interval == 0)
226 print_progress(k, ψₖ, grad_ψₖ, pₖᵀpₖ, γₖ, εₖ);
227 if (progress_cb)
228 progress_cb({k, xₖ, pₖ, pₖᵀpₖ, x̂ₖ, φₖ, ψₖ, grad_ψₖ, ψx̂ₖ, grad_̂ψₖ,
229 Lₖ, γₖ, τ, εₖ, Σ, y, problem, params});
230
231 auto time_elapsed = std::chrono::steady_clock::now() - start_time;
232 auto stop_status = detail::check_all_stop_conditions(
233 params, time_elapsed, k, stop_signal, ε, εₖ, no_progress);
234 if (stop_status != SolverStatus::Unknown) {
235 // TODO: We could cache g(x) and ẑ, but would that faster?
236 // It saves 1 evaluation of g per ALM iteration, but requires
237 // many extra stores in the inner loops of PANOC.
238 // TODO: move the computation of ẑ and g(x) to ALM?
239 if (stop_status == SolverStatus::Converged ||
240 stop_status == SolverStatus::Interrupted ||
241 always_overwrite_results) {
242 calc_err_z(x̂ₖ, /* in ⟹ out */ err_z);
243 x = std::move(x̂ₖ);
244 y = std::move(ŷx̂ₖ);
245 }
246 s.iterations = k;
247 s.ε = εₖ;
248 s.elapsed_time = duration_cast<microseconds>(time_elapsed);
249 s.status = stop_status;
250 return s;
251 }
252
253 // Calculate Newton step -----------------------------------------------
254
255 if (k > 0) { // No L-BFGS estimate on first iteration → no Newton step
256 J.clear();
257 // Find inactive indices J
258 for (vec::Index i = 0; i < n; ++i) {
259 real_t gd = xₖ(i) - γₖ * grad_ψₖ(i);
260 if (gd < problem.C.lowerbound(i)) { // i ∊ J̲ ⊆ K
261 qₖ(i) = pₖ(i); //
262 } else if (problem.C.upperbound(i) < gd) { // i ∊ J̅ ⊆ K
263 qₖ(i) = pₖ(i); //
264 } else { // i ∊ J
265 J.push_back(i);
266 qₖ(i) = params.hessian_vec ? 0 : -grad_ψₖ(i);
267 }
268 }
269
270 if (not J.empty()) { // There are inactive indices J
271 if (J.size() == n) { // There are no active indices K
272 qₖ = -grad_ψₖ;
273 } else if (params.hessian_vec) { // There are active indices K
276 problem, xₖ, y, Σ, grad_ψₖ, qₖ, HqK, work_n,
277 work_n2, work_m);
278 } else {
279 problem.hess_L_prod(xₖ, y, qₖ, HqK);
281 auto &g = work_m;
282 problem.g(xₖ, g);
283 for (vec::Index i = 0; i < m; ++i) {
284 real_t ζ = g(i) + y(i) / Σ(i);
285 bool inactive = problem.D.lowerbound(i) < ζ &&
286 ζ < problem.D.upperbound(i);
287 if (not inactive) {
288 problem.grad_gi(xₖ, i, work_n);
289 auto t = Σ(i) * work_n.dot(qₖ);
290 // TODO: the dot product is more work than
291 // strictly necessary (only over K)
292 for (auto j : J)
293 HqK(j) += work_n(j) * t;
294 }
295 }
296 }
297 }
298
299 for (auto j : J) // Compute right-hand side of 6.1c
300 qₖ(j) = -grad_ψₖ(j) - HqK(j);
301 }
302
303 real_t stepsize = params.lbfgs_stepsize ==
305 ? γₖ
306 : -1;
307 // If all indices are inactive, we can use standard L-BFGS,
308 // if there are active indices, we need the specialized version
309 // that only applies L-BFGS to the inactive indices
310 bool success = lbfgs.apply(qₖ, stepsize, J);
311 // If L-BFGS application failed, qₖ(J) still contains
312 // -∇ψ(x)(J) - HqK(J) or -∇ψ(x)(J), which is not a valid step.
313 // A good alternative is to use H₀ = γI as an L-BFGS estimate.
314 // This seems to be better than just falling back to a projected
315 // gradient step.
316 if (not success) {
317 if (J.size() == n)
318 qₖ *= γₖ;
319 else
320 for (auto j : J)
321 qₖ(j) *= γₖ;
322 }
323 }
324 }
325
326 // Line search initialization ------------------------------------------
327 τ = 1;
328 real_t σₖγₖ⁻¹pₖᵀpₖ = (1 - γₖ * Lₖ) * pₖᵀpₖ / (2 * γₖ);
329 real_t φₖ₊₁, ψₖ₊₁, ψx̂ₖ₊₁, grad_ψₖ₊₁ᵀpₖ₊₁, pₖ₊₁ᵀpₖ₊₁;
330 real_t Lₖ₊₁, γₖ₊₁;
331 real_t ls_cond;
333 nmΦₖ = k == 0 ? φₖ : w * nmΦₖ + (1 - w) * φₖ;
334 // TODO: make separate parameter
335 real_t margin =
336 (1 + std::abs(nmΦₖ)) * params.quadratic_upperbound_tolerance_factor;
337
338 // Make sure quasi-Newton step is valid
339 if (k == 0) {
340 τ = 0; // Always use prox step on first iteration
341 } else if (not qₖ.allFinite()) {
342 τ = 0;
343 ++s.lbfgs_failures;
344 } else if (J.empty()) {
345 τ = 0; // All constraints are active, no Newton step possible
346 }
347
348 // Line search loop ----------------------------------------------------
349 unsigned last_γ_changeₖ₊₁;
350 do {
351 last_γ_changeₖ₊₁ = last_γ_change;
352 Lₖ₊₁ = Lₖ;
353 γₖ₊₁ = γₖ;
354
355 // Calculate xₖ₊₁
356 if (τ / 2 < params.τ_min) { // line search failed
357 xₖ₊₁.swap(x̂ₖ); // → safe prox step
358 ψₖ₊₁ = ψx̂ₖ;
359 if (need_grad_̂ψₖ)
360 grad_ψₖ₊₁.swap(grad_̂ψₖ);
361 else
362 calc_grad_ψ_from_ŷ(xₖ₊₁, ŷx̂ₖ, /* in ⟹ out */ grad_ψₖ₊₁);
363 } else { // line search didn't fail (yet)
364 if (τ == 1) // → faster quasi-Newton step
365 xₖ₊₁ = xₖ + qₖ;
366 else
367 xₖ₊₁ = xₖ + (1 - τ) * pₖ + τ * qₖ;
368 // Calculate ψ(xₖ₊₁), ∇ψ(xₖ₊₁)
369 ψₖ₊₁ = calc_ψ_grad_ψ(xₖ₊₁, /* in ⟹ out */ grad_ψₖ₊₁);
370 }
371
372 // Calculate x̂ₖ₊₁, pₖ₊₁ (projected gradient step in xₖ₊₁)
373 calc_x̂(γₖ₊₁, xₖ₊₁, grad_ψₖ₊₁, /* in ⟹ out */ x̂ₖ₊₁, pₖ₊₁);
374 // Calculate ψ(x̂ₖ₊₁) and ŷ(x̂ₖ₊₁)
375 ψx̂ₖ₊₁ = calc_ψ_ŷ(x̂ₖ₊₁, /* in ⟹ out */ ŷx̂ₖ₊₁);
376
377 // Quadratic upper bound -------------------------------------------
378 grad_ψₖ₊₁ᵀpₖ₊₁ = grad_ψₖ₊₁.dot(pₖ₊₁);
379 pₖ₊₁ᵀpₖ₊₁ = pₖ₊₁.squaredNorm();
380 real_t pₖ₊₁ᵀpₖ₊₁_ₖ = pₖ₊₁ᵀpₖ₊₁; // prox step with step size γₖ
381
383 // Decrease step size until quadratic upper bound is satisfied
384 real_t old_γₖ₊₁ = descent_lemma(
385 xₖ₊₁, ψₖ₊₁, grad_ψₖ₊₁,
386 /* in ⟹ out */ x̂ₖ₊₁, pₖ₊₁, ŷx̂ₖ₊₁,
387 /* inout */ ψx̂ₖ₊₁, pₖ₊₁ᵀpₖ₊₁, grad_ψₖ₊₁ᵀpₖ₊₁, Lₖ₊₁, γₖ₊₁);
388 if (old_γₖ₊₁ != γₖ₊₁)
389 last_γ_changeₖ₊₁ = k;
390 }
391
392 // Compute forward-backward envelope
393 φₖ₊₁ = ψₖ₊₁ + 1 / (2 * γₖ₊₁) * pₖ₊₁ᵀpₖ₊₁ + grad_ψₖ₊₁ᵀpₖ₊₁;
394 // Compute line search condition
395 ls_cond = φₖ₊₁ - (nmΦₖ - σₖγₖ⁻¹pₖᵀpₖ);
397 ls_cond -= (0.5 / γₖ₊₁ - 0.5 / γₖ) * pₖ₊₁ᵀpₖ₊₁_ₖ;
398
399 τ /= 2;
400 } while (ls_cond > margin && τ >= params.τ_min);
401
402 // If τ < τ_min the line search failed and we accepted the prox step
403 if (τ < params.τ_min && k != 0) {
405 τ = 0;
406 }
407 if (k != 0) {
408 s.count_τ += 1;
409 s.sum_τ += τ * 2;
410 s.τ_1_accepted += τ * 2 == 1;
411 // std::cout << J.size() << "," << τ*2 << "\r\n";
412 }
413
414 // Check if we made any progress
415 if (no_progress > 0 || k % params.max_no_progress == 0)
416 no_progress = xₖ == xₖ₊₁ ? no_progress + 1 : 0;
417
418 // Update L-BFGS
419 const bool force = true;
420 s.lbfgs_rejected += not lbfgs.update(xₖ, xₖ₊₁, grad_ψₖ, grad_ψₖ₊₁,
421 LBFGS::Sign::Positive, force);
422
423 // Advance step --------------------------------------------------------
424 last_γ_change = last_γ_changeₖ₊₁;
425 Lₖ = Lₖ₊₁;
426 γₖ = γₖ₊₁;
427
428 ψₖ = ψₖ₊₁;
429 ψx̂ₖ = ψx̂ₖ₊₁;
430 φₖ = φₖ₊₁;
431
432 xₖ.swap(xₖ₊₁);
433 x̂ₖ.swap(x̂ₖ₊₁);
434 ŷx̂ₖ.swap(ŷx̂ₖ₊₁);
435 pₖ.swap(pₖ₊₁);
436 grad_ψₖ.swap(grad_ψₖ₊₁);
437 grad_ψₖᵀpₖ = grad_ψₖ₊₁ᵀpₖ₊₁;
438 pₖᵀpₖ = pₖ₊₁ᵀpₖ₊₁;
439 }
440 throw std::logic_error("[PANOC] loop error");
441}
442
443} // namespace alpaqa
bool apply(Vec &&q, real_t γ)
Apply the inverse Hessian approximation to the given vector q.
Definition: lbfgs.hpp:59
bool update(crvec xₖ, crvec xₖ₊₁, crvec pₖ, crvec pₖ₊₁, Sign sign, bool forced=false)
Update the inverse Hessian approximation using the new vectors xₖ₊₁ and pₖ₊₁.
Definition: lbfgs.hpp:33
void resize(size_t n)
Re-allocate storage for a problem with a different size.
Definition: lbfgs.hpp:188
std::function< void(const ProgressInfo &)> progress_cb
Stats operator()(const Problem &problem, crvec Σ, real_t ε, bool always_overwrite_results, rvec x, rvec y, rvec err_z)
real_t calc_error_stop_crit(const Box &C, PANOCStopCrit crit, crvec pₖ, real_t γ, crvec xₖ, crvec x̂ₖ, crvec ŷₖ, crvec grad_ψₖ, crvec grad_̂ψₖ)
Compute the ε from the stopping criterion, see PANOCStopCrit.
SolverStatus check_all_stop_conditions(const ParamsT &params, DurationT time_elapsed, unsigned iteration, const AtomicStopSignal &stop_signal, real_t ε, real_t εₖ, unsigned no_progress)
Check all stop conditions (required tolerance reached, out of time, maximum number of iterations exce...
real_t calc_ψ_ŷ(const Problem &p, crvec x, crvec y, crvec Σ, rvec ŷ)
Calculate both ψ(x) and the vector ŷ that can later be used to compute ∇ψ.
real_t descent_lemma(const Problem &problem, real_t rounding_tolerance, real_t L_max, crvec xₖ, real_t ψₖ, crvec grad_ψₖ, crvec y, crvec Σ, rvec x̂ₖ, rvec pₖ, rvec ŷx̂ₖ, real_t &ψx̂ₖ, real_t &norm_sq_pₖ, real_t &grad_ψₖᵀpₖ, real_t &Lₖ, real_t &γₖ)
Increase the estimate of the Lipschitz constant of the objective gradient and decrease the step size ...
bool stop_crit_requires_grad_̂ψₖ(PANOCStopCrit crit)
real_t calc_ψ_grad_ψ(const Problem &p, crvec x, crvec y, crvec Σ, rvec grad_ψ, rvec work_n, rvec work_m)
Calculate both ψ(x) and its gradient ∇ψ(x).
void calc_err_z(const Problem &p, crvec x̂, crvec y, crvec Σ, rvec err_z)
Calculate the error between ẑ and g(x).
real_t initial_lipschitz_estimate(const Problem &problem, crvec xₖ, crvec y, crvec Σ, real_t ε, real_t δ, real_t L_min, real_t L_max, real_t &ψ, rvec grad_ψ, rvec work_n1, rvec work_n2, rvec work_n3, rvec work_m)
Estimate the Lipschitz constant of the gradient using finite differences.
void calc_augmented_lagrangian_hessian_prod_fd(const Problem &problem, crvec xₖ, crvec y, crvec Σ, crvec grad_ψ, crvec v, rvec Hv, rvec work_n1, rvec work_n2, rvec work_m)
Compute the Hessian matrix of the augmented Lagrangian function multiplied by the given vector,...
void calc_grad_ψ_from_ŷ(const Problem &p, crvec x, crvec ŷ, rvec grad_ψ, rvec work_n)
Calculate ∇ψ(x) using ŷ.
void calc_x̂(const Problem &prob, real_t γ, crvec x, crvec grad_ψ, rvec x̂, rvec p)
void print_progress(unsigned k, real_t ψₖ, crvec grad_ψₖ, real_t pₖᵀpₖ, real_t γₖ, real_t εₖ)
int Σ
Definition: test.py:72
int m
Definition: test.py:41
int n
Definition: test.py:40
int ε
Definition: test.py:73
LipschitzEstimateParams Lipschitz
Parameters related to the Lipschitz constant estimate and step size.
real_t Lγ_factor
Factor that relates step size γ and Lipschitz constant.
Definition: lipschitz.hpp:15
Eigen::Ref< const vec > crvec
Default type for immutable references to vectors.
Definition: vec.hpp:18
real_t δ
Minimum step size for initial finite difference Lipschitz estimate.
Definition: lipschitz.hpp:13
constexpr real_t NaN
Not a number.
Definition: vec.hpp:28
unsigned max_no_progress
Maximum number of iterations without any progress before giving up.
real_t L_max
Maximum Lipschitz constant estimate.
@ Interrupted
Solver was interrupted by the user.
@ Unknown
Initial value.
@ Converged
Converged and reached given tolerance.
@ NotFinite
Intermediate results were infinite or not-a-number.
realvec vec
Default type for vectors.
Definition: vec.hpp:14
real_t ε
Relative step size for initial finite difference Lipschitz estimate.
Definition: lipschitz.hpp:11
real_t L₀
Initial estimate of the Lipschitz constant of ∇ψ(x)
Definition: lipschitz.hpp:9
double real_t
Default floating point type.
Definition: vec.hpp:8
unsigned print_interval
When to print progress.
real_t τ_min
Minimum weight factor between Newton step and projected gradient step.
real_t L_min
Minimum Lipschitz constant estimate.
unsigned max_iter
Maximum number of inner PANOC iterations.
PANOCStopCrit stop_crit
What stopping criterion to use.
Eigen::Ref< vec > rvec
Default type for mutable references to vectors.
Definition: vec.hpp:16
real_t nonmonotone_linesearch
Factor used in update for exponentially weighted nonmonotone line search.
problem
Definition: main.py:16
Problem description for minimization problems.