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_num_variables();
48 const auto m = problem.get_num_constraints();
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)
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_augmented_lagrangian_and_gradient(
114 i.x, y, Σ, i.grad_ψ, work_n, work_m);
116 auto eval_prox_grad_step = [&problem](Iterate &i) {
118 problem.eval_proximal_gradient_step(i.γ, i.x, i.grad_ψ, i.x̂, i.p);
119 i.pᵀp = i.p.squaredNorm();
120 i.grad_ψᵀp = i.p.dot(i.grad_ψ);
122 auto eval_cost_in_prox = [&problem, &y, &Σ](Iterate &i) {
123 i.ψx̂ = problem.eval_augmented_lagrangian(i.x̂, y, Σ, i.ŷx̂);
125 auto eval_grad_in_prox = [&problem, &
prox, &work_n](
const Iterate &i) {
126 problem.eval_lagrangian_gradient(i.x̂, i.ŷx̂,
prox->grad_ψ, work_n);
128 auto eval_prox_grad_step_in_prox = [&problem, &
prox](
const Iterate &i) {
129 prox->hx̂ = problem.eval_proximal_gradient_step(i.γ, i.x̂,
prox->grad_ψ,
137 std::array<char, 64> print_buf;
138 auto print_real = [
this, &print_buf](
real_t x) {
139 return float_to_str_vw(print_buf, x,
params.print_precision);
141 auto print_real3 = [&print_buf](
real_t x) {
142 return float_to_str_vw(print_buf, x, 3);
144 auto print_progress_1 = [&print_real,
os](
unsigned k,
real_t φₖ,
real_t ψₖ,
148 *
os <<
"┌─[ZeroFPR]\n";
150 *
os <<
"├─ " << std::setw(6) << k <<
'\n';
151 *
os <<
"│ φγ = " << print_real(φₖ)
152 <<
", ψ = " << print_real(ψₖ)
153 <<
", ‖∇ψ‖ = " << print_real(grad_ψₖ.norm())
154 <<
", ‖p‖ = " << print_real(std::sqrt(pₖᵀpₖ))
155 <<
", γ = " << print_real(γₖ)
156 <<
", ε = " << print_real(εₖ) <<
'\n';
158 auto print_progress_2 = [&print_real, &print_real3,
os](
crvec qₖ,
real_t τₖ,
160 const char *color = τₖ == 1 ?
"\033[0;32m"
161 : τₖ > 0 ?
"\033[0;33m"
163 *
os <<
"│ ‖q‖ = " << print_real(qₖ.norm())
164 <<
", τ = " << color << print_real3(τₖ) <<
"\033[0m"
166 << (reject ?
"\033[0;31mrejected\033[0m"
167 :
"\033[0;32maccepted\033[0m")
171 *
os <<
"└─ " << status <<
" ──"
175 auto do_progress_cb = [
this, &s, &problem, &Σ, &y, &opts](
176 unsigned k, Iterate &it,
crvec q,
crvec grad_ψx̂,
194 .grad_ψ_hat = grad_ψx̂,
202 .outer_iter = opts.outer_iter,
215 if (
params.Lipschitz.L_0 <= 0) {
217 problem, curr->x, y, Σ,
params.Lipschitz.ε,
params.Lipschitz.δ,
219 curr->ψx, curr->grad_ψ, curr->x̂, next->grad_ψ,
224 curr->L =
params.Lipschitz.L_0;
226 eval_ψ_grad_ψ(*curr);
228 if (not std::isfinite(curr->L)) {
232 curr->γ =
params.Lipschitz.Lγ_factor / curr->L;
237 eval_prox_grad_step(*curr);
238 eval_cost_in_prox(*curr);
241 while (curr->L <
params.L_max && qub_violated(*curr)) {
244 eval_prox_grad_step(*curr);
245 eval_cost_in_prox(*curr);
254 unsigned no_progress = 0;
265 eval_grad_in_prox(*curr);
266 eval_prox_grad_step_in_prox(*curr);
269 problem,
params.stop_crit, curr->p, curr->γ, curr->x, curr->x̂,
270 curr->ŷx̂, curr->grad_ψ,
prox->grad_ψ, work_n, next->p);
274 params.print_interval != 0 && k %
params.print_interval == 0;
276 print_progress_1(k, curr->fbe(), curr->ψx, curr->grad_ψ, curr->pᵀp,
281 auto time_elapsed = std::chrono::steady_clock::now() - start_time;
287 bool do_final_print =
params.print_interval != 0;
288 if (!do_print && do_final_print)
289 print_progress_1(k, curr->fbe(), curr->ψx, curr->grad_ψ,
290 curr->pᵀp, curr->γ, εₖ);
291 if (do_print || do_final_print)
292 print_progress_n(stop_status);
295 opts.always_overwrite_results) {
297 if (err_z.size() > 0)
298 err_z = (ŷ - y).cwiseQuotient(Σ);
304 s.
elapsed_time = duration_cast<nanoseconds>(time_elapsed);
318 direction.initialize(problem, y, Σ, curr->γ, curr->x̂,
prox->x̂,
322 if (k > 0 ||
direction.has_initial_direction()) {
328 if (τ_init == 1 && not q.allFinite())
342 bool update_lbfgs_in_linesearch =
params.update_direction_in_candidate;
343 bool update_lbfgs_in_accel =
params.update_direction_in_accel;
344 bool updated_lbfgs =
false;
345 bool dir_rejected =
true;
348 auto take_safe_step = [&] {
350 next->ψx = curr->ψx̂;
351 next->grad_ψ =
prox->grad_ψ;
356 auto take_accelerated_step = [&](
real_t τ) {
358 next->x = curr->x̂ + q;
360 next->x = curr->x̂ + τ * q;
362 eval_ψ_grad_ψ(*next);
369 τ != 0 ? take_accelerated_step(τ) : take_safe_step();
376 bool fail = !std::isfinite(next->ψx);
377 fail |= next->L >=
params.L_max && !(curr->L >=
params.L_max);
387 update_lbfgs_in_linesearch =
false;
392 eval_prox_grad_step(*next);
393 eval_cost_in_prox(*next);
396 if (update_lbfgs_in_accel && !updated_lbfgs) {
397 if (τ > 0 &&
params.update_direction_from_prox_step) {
399 not
direction.update(curr->γ, next->γ, curr->x̂, next->x,
404 not
direction.update(curr->γ, next->γ, curr->x, next->x,
405 curr->p, next->p, curr->grad_ψ,
408 update_lbfgs_in_accel =
false;
409 updated_lbfgs =
true;
413 if (next->L <
params.L_max && qub_violated(*next)) {
421 update_lbfgs_in_linesearch =
false;
426 if (update_lbfgs_in_linesearch && !updated_lbfgs) {
427 if (τ > 0 &&
params.update_direction_from_prox_step) {
429 not
direction.update(curr->γ, next->γ, curr->x̂, next->x,
434 not
direction.update(curr->γ, next->γ, curr->x, next->x,
435 curr->p, next->p, curr->grad_ψ,
438 update_lbfgs_in_linesearch =
false;
439 updated_lbfgs =
true;
443 if (τ > 0 && linesearch_violated(*curr, *next)) {
445 if (τ <
params.min_linesearch_coefficient)
461 if (no_progress > 0 || k %
params.max_no_progress == 0)
462 no_progress = curr->x == next->x ? no_progress + 1 : 0;
466 if (!updated_lbfgs) {
467 if (curr->γ != next->γ) {
469 if (
params.recompute_last_prox_step_after_stepsize_change) {
472 eval_prox_grad_step_in_prox(*curr);
475 if (τ > 0 &&
params.update_direction_from_prox_step) {
477 not
direction.update(curr->γ, next->γ, curr->x̂, next->x,
482 not
direction.update(curr->γ, next->γ, curr->x, next->x,
483 curr->p, next->p, curr->grad_ψ,
490 if (do_print && (k != 0 ||
direction.has_initial_direction()))
491 print_progress_2(q, τ, dir_rejected);
494 std::swap(curr, next);
505 throw std::logic_error(
"[ZeroFPR] loop error");