PANOC-ALM  quadratic-penalty
Nonconvex constrained optimization
pga.hpp
Go to the documentation of this file.
1 #pragma once
2 
8 
9 #include <cassert>
10 #include <chrono>
11 #include <cmath>
12 #include <iomanip>
13 #include <iostream>
14 #include <stdexcept>
15 
16 namespace pa {
17 
18 struct PGAParams {
22  unsigned max_iter = 100;
24  std::chrono::microseconds max_time = std::chrono::minutes(5);
26  real_t L_min = 1e-5;
28  real_t L_max = 1e9;
30  PANOCStopCrit stop_crit = PANOCStopCrit::ApproxKKT;
31 
34  unsigned print_interval = 0;
35 
37  10 * std::numeric_limits<real_t>::epsilon();
38 };
39 
41  unsigned k;
55  const Problem &problem;
56  const PGAParams &params;
57 };
58 
62 class PGASolver {
63  public:
64  using Params = PGAParams;
65 
67 
68  struct Stats {
69  SolverStatus status = SolverStatus::Unknown;
71  std::chrono::microseconds elapsed_time;
72  unsigned iterations = 0;
73  };
74 
76 
77  Stats operator()(const Problem &problem, // in
78  crvec Σ, // in
79  real_t ε, // in
80  bool always_overwrite_results, // in
81  rvec x, // inout
82  rvec λ, // inout
83  rvec err_z); // out
84 
85  PGASolver &
86  set_progress_callback(std::function<void(const ProgressInfo &)> cb) {
87  this->progress_cb = cb;
88  return *this;
89  }
90 
91  std::string get_name() const { return "PGA"; }
92 
93  void stop() { stop_signal.stop(); }
94 
95  const Params &get_params() const { return params; }
96 
97  private:
100  std::function<void(const ProgressInfo &)> progress_cb;
101 };
102 
103 using std::chrono::duration_cast;
104 using std::chrono::microseconds;
105 
106 inline PGASolver::Stats
108  crvec Σ, // in
109  real_t ε, // in
110  bool always_overwrite_results, // in
111  rvec x, // inout
112  rvec y, // inout
113  rvec err_z // out
114 ) {
115  auto start_time = std::chrono::steady_clock::now();
116  Stats s;
117 
118  const auto n = problem.n;
119  const auto m = problem.m;
120 
121  vec xₖ = x, // Value of x at the beginning of the iteration
122  x̂ₖ(n), // Value of x after a projected gradient step
123  pₖ(n), // Projected gradient step
124  ŷₖ(m), // <?>
125  grad_ψₖ(n), // ∇ψ(xₖ)
126  grad_ψx̂ₖ(n); // ∇ψ(x̂ₖ)
127 
128  vec work_n(n), work_m(m);
129 
130  // Helper functions --------------------------------------------------------
131 
132  // Wrappers for helper functions that automatically pass along any arguments
133  // that are constant within PGA (for readability in the main algorithm)
134  auto calc_ψ_ŷ = [&problem, &y, &Σ](crvec x, rvec ŷ) {
135  return detail::calc_ψ_ŷ(problem, x, y, Σ, ŷ);
136  };
137  auto calc_ψ_grad_ψ = [&problem, &y, &Σ, &work_n, &work_m](crvec x,
138  rvec grad_ψ) {
139  return detail::calc_ψ_grad_ψ(problem, x, y, Σ, grad_ψ, work_n, work_m);
140  };
141  auto calc_grad_ψ_from_ŷ = [&problem, &work_n](crvec x, crvec ŷ,
142  rvec grad_ψ) {
143  detail::calc_grad_ψ_from_ŷ(problem, x, ŷ, grad_ψ, work_n);
144  };
145  auto calc_x̂ = [&problem](real_t γ, crvec x, crvec grad_ψ, rvec x̂, rvec p) {
146  detail::calc_x̂(problem, γ, x, grad_ψ, x̂, p);
147  };
148  auto calc_err_z = [&problem, &y, &Σ](crvec x̂, rvec err_z) {
150  };
151  auto descent_lemma = [this, &problem, &y,
152  &Σ](crvec xₖ, real_t ψₖ, crvec grad_ψₖ, rvec x̂ₖ,
153  rvec pₖ, rvec ŷx̂ₖ, real_t &ψx̂ₖ, real_t &pₖᵀpₖ,
154  real_t &grad_ψₖᵀpₖ, real_t &Lₖ, real_t &γₖ) {
155  return detail::descent_lemma(
157  xₖ, ψₖ, grad_ψₖ, y, Σ, x̂ₖ, pₖ, ŷx̂ₖ, ψx̂ₖ, pₖᵀpₖ, grad_ψₖᵀpₖ, Lₖ, γₖ);
158  };
159  auto print_progress = [&](unsigned k, real_t ψₖ, crvec grad_ψₖ, crvec pₖ,
160  real_t γₖ, real_t εₖ) {
161  std::cout << "[PGA] " << std::setw(6) << k
162  << ": ψ = " << std::setw(13) << ψₖ
163  << ", ‖∇ψ‖ = " << std::setw(13) << grad_ψₖ.norm()
164  << ", ‖p‖ = " << std::setw(13) << pₖ.norm()
165  << ", γ = " << std::setw(13) << γₖ
166  << ", εₖ = " << std::setw(13) << εₖ << "\r\n";
167  };
168 
169  // Estimate Lipschitz constant ---------------------------------------------
170 
171  real_t ψₖ, Lₖ;
172  // Finite difference approximation of ∇²ψ in starting point
173  if (params.Lipschitz.L₀ <= 0) {
177  /* in ⟹ out */ ψₖ, grad_ψₖ, x̂ₖ, grad_ψx̂ₖ, work_n, work_m);
178  }
179  // Initial Lipschitz constant provided by the user
180  else {
181  Lₖ = params.Lipschitz.L₀;
182  // Calculate ψ(xₖ), ∇ψ(x₀)
183  ψₖ = calc_ψ_grad_ψ(xₖ, /* in ⟹ out */ grad_ψₖ);
184  }
185  if (not std::isfinite(Lₖ)) {
186  s.status = SolverStatus::NotFinite;
187  return s;
188  }
189  real_t γₖ = params.Lipschitz.Lγ_factor / Lₖ;
190 
191  unsigned no_progress = 0;
192 
193  // Main loop
194  // =========================================================================
195  for (unsigned k = 0; k <= params.max_iter; ++k) {
196  // From previous iteration:
197  // - xₖ
198  // - grad_ψₖ
199  // - ψₖ
200 
201  // Quadratic upper bound -----------------------------------------------
202 
203  // Projected gradient step: x̂ₖ and pₖ
204  calc_x̂(γₖ, xₖ, grad_ψₖ, /* in ⟹ out */ x̂ₖ, pₖ);
205  // Calculate ψ(x̂ₖ) and ŷ(x̂ₖ)
206  real_t ψx̂ₖ = calc_ψ_ŷ(x̂ₖ, /* in ⟹ out */ ŷₖ);
207  // Calculate ∇ψ(xₖ)ᵀpₖ and ‖pₖ‖²
208  real_t grad_ψₖᵀpₖ = grad_ψₖ.dot(pₖ);
209  real_t pₖᵀpₖ = pₖ.squaredNorm();
210 
211  // Decrease step size until quadratic upper bound is satisfied
212  descent_lemma(xₖ, ψₖ, grad_ψₖ, x̂ₖ, pₖ, ŷₖ, ψx̂ₖ, pₖᵀpₖ, grad_ψₖᵀpₖ, Lₖ,
213  γₖ);
214 
215  // Calculate ∇ψ(x̂ₖ)
216  calc_grad_ψ_from_ŷ(x̂ₖ, ŷₖ, /* in ⟹ out */ grad_ψx̂ₖ);
217 
218  // Check stop condition ------------------------------------------------
219 
220  real_t εₖ = detail::calc_error_stop_crit(params.stop_crit, pₖ, γₖ, xₖ,
221  grad_ψx̂ₖ, grad_ψₖ, problem.C);
222 
223  // Print progress
224  if (params.print_interval != 0 && k % params.print_interval == 0)
225  print_progress(k, ψₖ, grad_ψₖ, pₖ, γₖ, εₖ);
226  if (progress_cb)
227  progress_cb({k, xₖ, pₖ, pₖᵀpₖ, x̂ₖ, ψₖ, grad_ψₖ, ψx̂ₖ, grad_ψx̂ₖ, Lₖ,
228  γₖ, εₖ, Σ, y, problem, params});
229 
230  auto time_elapsed = std::chrono::steady_clock::now() - start_time;
231  bool out_of_time = time_elapsed > params.max_time;
232  bool out_of_iter = k == params.max_iter;
233  bool interrupted = stop_signal.stop_requested();
234  bool not_finite = not std::isfinite(εₖ);
235  bool conv = εₖ <= ε;
236  bool max_no_progress = no_progress > 1;
237  bool exit = conv || out_of_iter || out_of_time || not_finite ||
238  interrupted || max_no_progress;
239  if (exit) {
240  // TODO: We could cache g(x) and ẑ, but would that faster?
241  // It saves 1 evaluation of g per ALM iteration, but requires
242  // many extra stores in the inner loops.
243  // TODO: move the computation of ẑ and g(x) to ALM?
244  if (conv || interrupted || always_overwrite_results) {
245  calc_err_z(x̂ₖ, /* in ⟹ out */ err_z);
246  x = std::move(x̂ₖ);
247  y = std::move(ŷₖ);
248  }
249  s.iterations = k; // TODO: what do we count as an iteration?
250  s.ε = εₖ;
251  s.elapsed_time = duration_cast<microseconds>(time_elapsed);
252  s.status = conv ? SolverStatus::Converged
253  : out_of_time ? SolverStatus::MaxTime
254  : out_of_iter ? SolverStatus::MaxIter
255  : not_finite ? SolverStatus::NotFinite
256  : max_no_progress ? SolverStatus::NoProgress
258  return s;
259  }
260 
261  if (xₖ == x̂ₖ) // TODO: this is quite expensive to do on each iteration
262  ++no_progress;
263  else
264  no_progress = 0;
265 
266  xₖ.swap(x̂ₖ);
267  grad_ψₖ.swap(grad_ψx̂ₖ);
268  ψₖ = ψx̂ₖ;
269  }
270  throw std::logic_error("[PGA] loop error");
271 }
272 
273 template <class InnerSolverStats>
274 struct InnerStatsAccumulator;
275 
276 template <>
278  std::chrono::microseconds elapsed_time;
279  unsigned iterations = 0;
280 };
281 
284  const PGASolver::Stats &s) {
285  acc.elapsed_time += s.elapsed_time;
286  acc.iterations += s.iterations;
287  return acc;
288 }
289 
290 } // namespace pa
pa::detail::print_progress
void print_progress(unsigned k, real_t ψₖ, crvec grad_ψₖ, real_t pₖᵀpₖ, real_t γₖ, real_t εₖ)
Definition: standalone/panoc.hpp:139
pa::PGAParams
Definition: pga.hpp:18
pa::PGASolver::get_name
std::string get_name() const
Definition: pga.hpp:91
pa::AtomicStopSignal
Definition: atomic_stop_signal.hpp:7
pa::detail::calc_x̂
void calc_x̂(const ProblemT &prob, real_t γ, crvec x, crvec grad_ψ, rvec x̂, rvec p)
Definition: panoc-helpers.hpp:271
pa::PGAProgressInfo::p
crvec p
Definition: pga.hpp:43
pa::AtomicStopSignal::stop_requested
bool stop_requested() const
Definition: atomic_stop_signal.hpp:16
pa::PGAProgressInfo::k
unsigned k
Definition: pga.hpp:41
pa::PGAParams::L_min
real_t L_min
Minimum Lipschitz constant estimate.
Definition: pga.hpp:26
pa::SolverStatus::Unknown
@ Unknown
Initial value.
pa::rvec
Eigen::Ref< vec > rvec
Default type for mutable references to vectors.
Definition: vec.hpp:16
pa::PGAProgressInfo::norm_sq_p
real_t norm_sq_p
Definition: pga.hpp:44
pa::InnerStatsAccumulator< PGASolver::Stats >::iterations
unsigned iterations
Definition: pga.hpp:279
pa::PGAProgressInfo::grad_ψ_hat
crvec grad_ψ_hat
Definition: pga.hpp:49
pa::InnerStatsAccumulator< PGASolver::Stats >
Definition: pga.hpp:277
pa::PGASolver::stop
void stop()
Definition: pga.hpp:93
pa::PGASolver::Stats::status
SolverStatus status
Definition: pga.hpp:69
panocpy.test.y
y
Definition: test.py:76
atomic_stop_signal.hpp
pa::PGASolver::Stats::iterations
unsigned iterations
Definition: pga.hpp:72
panocpy.test.err_z
err_z
Definition: test.py:76
pa::SolverStatus
SolverStatus
Exit status of a numerical solver such as ALM or PANOC.
Definition: solverstatus.hpp:7
panocpy.test.ε
int ε
Definition: test.py:71
pa::PGASolver::operator()
Stats operator()(const Problem &problem, crvec Σ, real_t ε, bool always_overwrite_results, rvec x, rvec λ, rvec err_z)
Definition: pga.hpp:107
pa::PGAProgressInfo::x
crvec x
Definition: pga.hpp:42
pa::vec
realvec vec
Default type for vectors.
Definition: vec.hpp:14
pa::PGAProgressInfo::ψ
real_t ψ
Definition: pga.hpp:46
pa::LipschitzEstimateParams::L₀
real_t L₀
Initial estimate of the Lipschitz constant of ∇ψ(x)
Definition: lipschitz.hpp:9
pa::PGAProgressInfo::problem
const Problem & problem
Definition: pga.hpp:55
pa::PGASolver::set_progress_callback
PGASolver & set_progress_callback(std::function< void(const ProgressInfo &)> cb)
Definition: pga.hpp:86
pa::PGAParams::print_interval
unsigned print_interval
When to print progress.
Definition: pga.hpp:34
pa::PGAParams::quadratic_upperbound_tolerance_factor
real_t quadratic_upperbound_tolerance_factor
Definition: pga.hpp:36
pa::operator+=
InnerStatsAccumulator< PANOCStats > & operator+=(InnerStatsAccumulator< PANOCStats > &acc, const PANOCStats &s)
Definition: inner/decl/panoc.hpp:217
pa::PGAProgressInfo::params
const PGAParams & params
Definition: pga.hpp:56
pa::PGAProgressInfo::x_hat
crvec x_hat
Definition: pga.hpp:45
main.problem
problem
Definition: main.py:16
pa::PGAProgressInfo::Σ
crvec Σ
Definition: pga.hpp:53
pa::PGASolver::params
Params params
Definition: pga.hpp:98
pa::PGAParams::L_max
real_t L_max
Maximum Lipschitz constant estimate.
Definition: pga.hpp:28
pa
Definition: alm.hpp:10
pa::detail::calc_ψ_grad_ψ
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).
Definition: panoc-helpers.hpp:113
panocpy.test.x
x
Definition: test.py:40
pa::PGAProgressInfo::L
real_t L
Definition: pga.hpp:50
pa::PANOCStopCrit
PANOCStopCrit
Definition: panoc-stop-crit.hpp:8
panoc-stop-crit.hpp
pa::detail::calc_ψ_ŷ
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 ∇ψ.
Definition: panoc-helpers.hpp:16
pa::PGASolver::Stats::ε
real_t ε
Definition: pga.hpp:70
pa::inf
constexpr real_t inf
Definition: vec.hpp:26
pa::PGASolver::get_params
const Params & get_params() const
Definition: pga.hpp:95
pa::PGAProgressInfo::γ
real_t γ
Definition: pga.hpp:51
panoc-helpers.hpp
pa::detail::calc_err_z
void calc_err_z(const Problem &p, crvec x̂, crvec y, crvec Σ, rvec err_z)
Calculate the error between ẑ and g(x).
Definition: panoc-helpers.hpp:212
pa::PGAParams::max_time
std::chrono::microseconds max_time
Maximum duration.
Definition: pga.hpp:24
lipschitz.hpp
pa::detail::calc_error_stop_crit
real_t calc_error_stop_crit(PANOCStopCrit crit, crvec pₖ, real_t γ, crvec xₖ, crvec grad_̂ψₖ, crvec grad_ψₖ, const Box &C)
Definition: panoc-helpers.hpp:284
pa::PGAProgressInfo::ε
real_t ε
Definition: pga.hpp:52
pa::detail::descent_lemma
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 ...
Definition: panoc-helpers.hpp:327
pa::crvec
Eigen::Ref< const vec > crvec
Default type for immutable references to vectors.
Definition: vec.hpp:18
pa::PGAProgressInfo::ψ_hat
real_t ψ_hat
Definition: pga.hpp:48
pa::PGASolver::Stats
Definition: pga.hpp:68
pa::LipschitzEstimateParams
Definition: lipschitz.hpp:7
pa::PGAParams::Lipschitz
LipschitzEstimateParams Lipschitz
Parameters related to the Lipschitz constant estimate and step size.
Definition: pga.hpp:20
pa::LipschitzEstimateParams::δ
real_t δ
Minimum step size for initial finite difference Lipschitz estimate.
Definition: lipschitz.hpp:13
pa::PGAProgressInfo::grad_ψ
crvec grad_ψ
Definition: pga.hpp:47
pa::PGASolver::progress_cb
std::function< void(const ProgressInfo &)> progress_cb
Definition: pga.hpp:100
panocpy.test.Σ
int Σ
Definition: test.py:70
pa::PGASolver::Stats::elapsed_time
std::chrono::microseconds elapsed_time
Definition: pga.hpp:71
pa::PGASolver
Standard Proximal Gradient Algorithm without any bells and whistles.
Definition: pga.hpp:62
pa::PGASolver::stop_signal
AtomicStopSignal stop_signal
Definition: pga.hpp:99
panocpy.test.m
int m
Definition: test.py:39
pa::PGAParams::stop_crit
PANOCStopCrit stop_crit
What stop criterion to use.
Definition: pga.hpp:30
pa::detail::initial_lipschitz_estimate
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.
Definition: panoc-helpers.hpp:574
pa::PGAProgressInfo
Definition: pga.hpp:40
pa::PGAProgressInfo::y
crvec y
Definition: pga.hpp:54
pa::detail::calc_grad_ψ_from_ŷ
void calc_grad_ψ_from_ŷ(const Problem &p, crvec x, crvec ŷ, rvec grad_ψ, rvec work_n)
Calculate ∇ψ(x) using ŷ.
Definition: panoc-helpers.hpp:81
panocpy.test.λ
λ
Definition: test.py:41
panocpy.test.p
p
Definition: test.py:57
pa::InnerStatsAccumulator
Definition: panoc-fwd.hpp:10
pa::LipschitzEstimateParams::Lγ_factor
real_t Lγ_factor
Factor that relates step size γ and Lipschitz constant.
Definition: lipschitz.hpp:15
panocpy.test.n
int n
Definition: test.py:38
pa::InnerStatsAccumulator< PGASolver::Stats >::elapsed_time
std::chrono::microseconds elapsed_time
Definition: pga.hpp:278
pa::real_t
double real_t
Default floating point type.
Definition: vec.hpp:8
solverstatus.hpp
pa::AtomicStopSignal::stop
void stop()
Definition: atomic_stop_signal.hpp:15
pa::PGAParams::max_iter
unsigned max_iter
Maximum number of inner iterations.
Definition: pga.hpp:22
pa::PGASolver::PGASolver
PGASolver(const Params &params)
Definition: pga.hpp:66
pa::Problem
Problem description for minimization problems.
Definition: include/panoc-alm/util/problem.hpp:24