19template <
class DirectionProv
iderT>
21 return "ZeroFPRSolver<" + std::string(direction.get_name()) +
">";
24template <
class DirectionProv
iderT>
42 using std::chrono::nanoseconds;
43 auto os = opts.os ? opts.os : this->os;
44 auto start_time = std::chrono::steady_clock::now();
47 const auto n = problem.get_n();
48 const auto m = problem.get_m();
56 real_t pᵀp = NaN<config_t>;
57 real_t grad_ψᵀp = NaN<config_t>;
58 real_t hx̂ = NaN<config_t>;
71 real_t ψx̂ = NaN<config_t>;
74 real_t pᵀp = NaN<config_t>;
75 real_t grad_ψᵀp = NaN<config_t>;
76 real_t hx̂ = NaN<config_t>;
80 real_t fbe()
const {
return ψx + hx̂ + pᵀp / (2 * γ) + grad_ψᵀp; }
83 } iterates[2]{{n, m}, {n, m}};
84 Iterate *curr = &iterates[0];
85 ProxIterate *prox = &prox_iterate;
86 Iterate *next = &iterates[1];
88 vec work_n(n), work_m(m);
93 auto qub_violated = [
this](
const Iterate &i) {
95 (1 + std::abs(i.ψx)) * params.quadratic_upperbound_tolerance_factor;
96 return i.ψx̂ > i.ψx + i.grad_ψᵀp +
real_t(0.5) * i.L * i.pᵀp + margin;
99 auto linesearch_violated = [
this](
const Iterate &curr,
100 const Iterate &next) {
101 if (params.force_linesearch)
103 real_t β = params.linesearch_strictness_factor;
104 real_t σ = β * (1 - curr.γ * curr.L) / (2 * curr.γ);
106 real_t margin = (1 + std::abs(φγ)) * params.linesearch_tolerance_factor;
107 return next.fbe() > φγ - σ * curr.pᵀp + margin;
112 auto eval_ψ_grad_ψ = [&problem, &y, &Σ, &work_n, &work_m](Iterate &i) {
113 i.ψx = problem.eval_ψ_grad_ψ(i.x, y, Σ, i.grad_ψ, work_n, work_m);
115 auto eval_prox_grad_step = [&problem](Iterate &i) {
116 i.hx̂ = problem.eval_prox_grad_step(i.γ, i.x, i.grad_ψ, i.x̂, i.p);
117 i.pᵀp = i.p.squaredNorm();
118 i.grad_ψᵀp = i.p.dot(i.grad_ψ);
120 auto eval_cost_in_prox = [&problem, &y, &Σ](Iterate &i) {
121 i.ψx̂ = problem.eval_ψ(i.x̂, y, Σ, i.ŷx̂);
123 auto eval_grad_in_prox = [&problem, &prox, &work_n](
const Iterate &i) {
124 problem.eval_grad_L(i.x̂, i.ŷx̂, prox->grad_ψ, work_n);
126 auto eval_prox_grad_step_in_prox = [&problem, &prox](
const Iterate &i) {
127 prox->hx̂ = problem.eval_prox_grad_step(i.γ, i.x̂, prox->grad_ψ, prox->x̂,
129 prox->pᵀp = prox->p.squaredNorm();
130 prox->grad_ψᵀp = prox->p.dot(prox->grad_ψ);
135 std::array<char, 64> print_buf;
136 auto print_real = [
this, &print_buf](
real_t x) {
139 auto print_real3 = [&print_buf](
real_t x) {
142 auto print_progress_1 = [&print_real, os](
unsigned k,
real_t φₖ,
real_t ψₖ,
146 *os <<
"┌─[ZeroFPR]\n";
148 *os <<
"├─ " << std::setw(6) << k <<
'\n';
149 *os <<
"│ φγ = " << print_real(φₖ)
150 <<
", ψ = " << print_real(ψₖ)
151 <<
", ‖∇ψ‖ = " << print_real(grad_ψₖ.norm())
152 <<
", ‖p‖ = " << print_real(std::sqrt(pₖᵀpₖ))
153 <<
", γ = " << print_real(γₖ)
154 <<
", ε = " << print_real(εₖ) <<
'\n';
156 auto print_progress_2 = [&print_real, &print_real3, os](
crvec qₖ,
real_t τₖ,
158 const char *color = τₖ == 1 ?
"\033[0;32m"
159 : τₖ > 0 ?
"\033[0;33m"
161 *os <<
"│ ‖q‖ = " << print_real(qₖ.norm())
162 <<
", τ = " << color << print_real3(τₖ) <<
"\033[0m"
164 << (reject ?
"\033[0;31mrejected\033[0m"
165 :
"\033[0;32maccepted\033[0m")
169 *os <<
"└─ " << status <<
" ──"
173 auto do_progress_cb = [
this, &s, &problem, &Σ, &y, &opts](
174 unsigned k, Iterate &it,
crvec q,
crvec grad_ψx̂,
191 .grad_ψ_hat = grad_ψx̂,
199 .outer_iter = opts.outer_iter,
212 if (params.Lipschitz.L_0 <= 0) {
213 curr->L = Helpers::initial_lipschitz_estimate(
214 problem, curr->x, y, Σ, params.Lipschitz.ε, params.Lipschitz.δ,
215 params.L_min, params.L_max,
216 curr->ψx, curr->grad_ψ, curr->x̂, next->grad_ψ,
221 curr->L = params.Lipschitz.L_0;
223 eval_ψ_grad_ψ(*curr);
225 if (not std::isfinite(curr->L)) {
229 curr->γ = params.Lipschitz.Lγ_factor / curr->L;
234 eval_prox_grad_step(*curr);
235 eval_cost_in_prox(*curr);
238 while (curr->L < params.L_max && qub_violated(*curr)) {
241 eval_prox_grad_step(*curr);
242 eval_cost_in_prox(*curr);
250 unsigned no_progress = 0;
261 eval_grad_in_prox(*curr);
262 eval_prox_grad_step_in_prox(*curr);
264 real_t εₖ = Helpers::calc_error_stop_crit(
265 problem, params.stop_crit, curr->p, curr->γ, curr->x, curr->x̂,
266 curr->ŷx̂, curr->grad_ψ, prox->grad_ψ, work_n, next->p);
270 params.print_interval != 0 && k % params.print_interval == 0;
272 print_progress_1(k, curr->fbe(), curr->ψx, curr->grad_ψ, curr->pᵀp,
277 auto time_elapsed = std::chrono::steady_clock::now() - start_time;
278 auto stop_status = Helpers::check_all_stop_conditions(
279 params, opts, time_elapsed, k, stop_signal, εₖ, no_progress);
281 do_progress_cb(k, *curr, null_vec<config_t>, prox->grad_ψ, -1, εₖ,
283 bool do_final_print = params.print_interval != 0;
284 if (!do_print && do_final_print)
285 print_progress_1(k, curr->fbe(), curr->ψx, curr->grad_ψ,
286 curr->pᵀp, curr->γ, εₖ);
287 if (do_print || do_final_print)
288 print_progress_n(stop_status);
291 opts.always_overwrite_results) {
293 if (err_z.size() > 0)
294 err_z = Σ.asDiagonal().inverse() * (ŷ - y);
295 x = std::move(curr->x̂);
296 y = std::move(curr->ŷx̂);
300 s.
elapsed_time = duration_cast<nanoseconds>(time_elapsed);
311 real_t τ_init = NaN<config_t>;
314 direction.initialize(problem, y, Σ, curr->γ, curr->x̂, prox->x̂,
315 prox->p, prox->grad_ψ);
318 if (k > 0 || direction.has_initial_direction()) {
319 τ_init = direction.apply(curr->γ, curr->x̂, prox->x̂, prox->p,
324 if (τ_init == 1 && not q.allFinite())
338 bool update_lbfgs_in_linesearch = params.update_direction_in_candidate;
339 bool updated_lbfgs =
false;
340 bool dir_rejected =
true;
343 auto take_safe_step = [&] {
345 next->ψx = curr->ψx̂;
346 next->grad_ψ = prox->grad_ψ;
351 auto take_accelerated_step = [&](
real_t τ) {
353 next->x = curr->x̂ + q;
355 next->x = curr->x̂ + τ * q;
357 eval_ψ_grad_ψ(*next);
360 while (!stop_signal.stop_requested()) {
364 τ != 0 ? take_accelerated_step(τ) : take_safe_step();
371 bool fail = next->L >= params.L_max || !std::isfinite(next->ψx);
381 update_lbfgs_in_linesearch =
false;
386 eval_prox_grad_step(*next);
387 eval_cost_in_prox(*next);
390 if (next->L < params.L_max && qub_violated(*next)) {
398 update_lbfgs_in_linesearch =
false;
403 if (update_lbfgs_in_linesearch && !updated_lbfgs) {
404 if (params.update_direction_from_prox_step) {
406 curr->γ, next->γ, curr->x̂, next->x, prox->p, next->p,
407 prox->grad_ψ, next->grad_ψ);
410 curr->γ, next->γ, curr->x, next->x, curr->p, next->p,
411 curr->grad_ψ, next->grad_ψ);
413 update_lbfgs_in_linesearch =
false;
414 updated_lbfgs =
true;
418 if (τ > 0 && linesearch_violated(*curr, *next)) {
420 if (τ < params.min_linesearch_coefficient)
436 if (no_progress > 0 || k % params.max_no_progress == 0)
437 no_progress = curr->x == next->x ? no_progress + 1 : 0;
441 if (!updated_lbfgs) {
442 if (curr->γ != next->γ) {
443 direction.changed_γ(next->γ, curr->γ);
444 if (params.recompute_last_prox_step_after_lbfgs_flush) {
447 eval_prox_grad_step_in_prox(*curr);
450 if (τ > 0 && params.update_direction_from_prox_step) {
452 curr->γ, next->γ, curr->x̂, next->x, prox->p, next->p,
453 prox->grad_ψ, next->grad_ψ);
456 curr->γ, next->γ, curr->x, next->x, curr->p, next->p,
457 curr->grad_ψ, next->grad_ψ);
463 if (do_print && (k != 0 || direction.has_initial_direction()))
464 print_progress_2(q, τ, dir_rejected);
467 std::swap(curr, next);
478 throw std::logic_error(
"[ZeroFPR] loop error");
std::string get_name() const
Stats operator()(const Problem &problem, const SolveOptions &opts, rvec x, rvec y, crvec Σ, rvec err_z)
unsigned stepsize_backtracks
SolverStatus
Exit status of a numerical solver such as ALM or PANOC.
@ Interrupted
Solver was interrupted by the user.
@ Converged
Converged and reached given tolerance.
@ NotFinite
Intermediate results were infinite or not-a-number.
std::chrono::nanoseconds time_progress_callback
std::chrono::nanoseconds elapsed_time
typename Conf::real_t real_t
unsigned linesearch_backtracks
typename Conf::length_t length_t
std::string_view float_to_str_vw(auto &buf, double value, int precision=std::numeric_limits< double >::max_digits10)
typename Conf::crvec crvec
unsigned linesearch_failures