20template <
class DirectionProv
iderT>
22 return "PANTRSolver<" + std::string(direction.get_name()) +
">";
25template <
class DirectionProv
iderT>
43 using std::chrono::nanoseconds;
44 auto os = opts.os ? opts.os : this->os;
45 auto start_time = std::chrono::steady_clock::now();
48 const auto n = problem.get_n();
49 const auto m = problem.get_m();
60 real_t ψx̂ = NaN<config_t>;
63 real_t pᵀp = NaN<config_t>;
64 real_t grad_ψᵀp = NaN<config_t>;
65 real_t hx̂ = NaN<config_t>;
69 real_t fbe()
const {
return ψx + hx̂ + pᵀp / (2 * γ) + grad_ψᵀp; }
72 } iterates[3]{{n, m}, {n, m}, {n, m}};
73 Iterate *curr = &iterates[0];
74 Iterate *prox = &iterates[1];
75 Iterate *cand = &iterates[2];
77 bool need_grad_ψx̂ = Helpers::stop_crit_requires_grad_ψx̂(params.stop_crit);
79 vec work_n(n), work_m(m);
81 std::chrono::nanoseconds direction_duration{};
85 auto eval_ψ_grad_ψ = [&problem, &y, &Σ, &work_n, &work_m](Iterate &i) {
86 i.ψx = problem.eval_ψ_grad_ψ(i.x, y, Σ, i.grad_ψ, work_n, work_m);
88 auto eval_prox_grad_step = [&problem](Iterate &i) {
89 i.hx̂ = problem.eval_prox_grad_step(i.γ, i.x, i.grad_ψ, i.x̂, i.p);
90 i.pᵀp = i.p.squaredNorm();
91 i.grad_ψᵀp = i.p.dot(i.grad_ψ);
93 auto eval_ψx̂ = [&problem, &y, &Σ](Iterate &i) {
94 i.ψx̂ = problem.eval_ψ(i.x̂, y, Σ, i.ŷx̂);
96 auto eval_grad_ψx̂ = [&problem, &work_n](Iterate &i,
rvec grad_ψx̂) {
97 problem.eval_grad_L(i.x̂, i.ŷx̂, grad_ψx̂, work_n);
102 auto qub_violated = [
this](
const Iterate &i) {
104 (1 + std::abs(i.ψx)) * params.quadratic_upperbound_tolerance_factor;
105 return i.ψx̂ > i.ψx + i.grad_ψᵀp +
real_t(0.5) * i.L * i.pᵀp + margin;
107 auto backtrack_qub = [&](Iterate &i) {
108 while (i.L < params.L_max && qub_violated(i)) {
112 eval_prox_grad_step(i);
119 std::array<char, 64> print_buf;
120 auto print_real = [
this, &print_buf](
real_t x) {
123 auto print_real3 = [&print_buf](
real_t x) {
130 *os <<
"┌─[PANTR]\n";
132 *os <<
"├─ " << std::setw(6) << k <<
" ──\n";
133 *os <<
"│ φγ = " << print_real(φₖ)
134 <<
", ψ = " << print_real(ψₖ)
135 <<
", ‖∇ψ‖ = " << print_real(grad_ψₖ.norm())
136 <<
", ‖p‖ = " << print_real(std::sqrt(pₖᵀpₖ))
137 <<
", γ = " << print_real(γₖ)
138 <<
", Δ = " << print_real(Δₖ)
139 <<
", ε = " << print_real(εₖ) <<
'\n';
141 auto print_progress_2 = [&](
crvec qₖ,
real_t ρₖ,
bool accept,
142 std::chrono::nanoseconds direction_duration) {
143 *os <<
"│ ‖q‖ = " << print_real(qₖ.norm())
144 <<
", ρ = " << print_real3(ρₖ)
147 static_cast<real_t>(1e6) *
148 std::chrono::duration<real_t>(direction_duration).count())
150 << (accept ?
"\033[0;32maccepted\033[0m"
151 :
"\033[0;35mrejected\033[0m")
155 *os <<
"└─ " << status <<
" ──"
158 auto do_progress_cb = [
this, &s, &problem, &Σ, &y,
159 &opts](
unsigned k, Iterate &it,
crvec q,
177 .grad_ψ_hat = grad_ψx̂,
186 .outer_iter = opts.outer_iter,
199 if (params.Lipschitz.L_0 <= 0) {
200 curr->L = Helpers::initial_lipschitz_estimate(
201 problem, curr->x, y, Σ, params.Lipschitz.ε, params.Lipschitz.δ,
202 params.L_min, params.L_max,
203 curr->ψx, curr->grad_ψ, curr->x̂, cand->grad_ψ,
208 curr->L = params.Lipschitz.L_0;
210 eval_ψ_grad_ψ(*curr);
212 if (not std::isfinite(curr->L)) {
216 curr->γ = params.Lipschitz.Lγ_factor / curr->L;
220 eval_prox_grad_step(*curr);
222 backtrack_qub(*curr);
227 bool accept_candidate =
false;
229 unsigned no_progress = 0;
231 real_t Δ = params.initial_radius;
232 if (!std::isfinite(Δ) || Δ == 0)
233 Δ =
real_t(0.1) * curr->grad_ψ.norm();
234 Δ = std::fmax(Δ, params.min_radius);
248 eval_grad_ψx̂(*curr, grad_ψx̂);
249 bool have_grad_ψx̂ = need_grad_ψx̂;
251 real_t εₖ = Helpers::calc_error_stop_crit(
252 problem, params.stop_crit, curr->p, curr->γ, curr->x, curr->x̂,
253 curr->ŷx̂, curr->grad_ψ, grad_ψx̂, work_n, cand->p);
258 params.print_interval != 0 && k % params.print_interval == 0;
260 print_progress_1(k, curr->fbe(), curr->ψx, curr->grad_ψ, curr->pᵀp,
265 auto time_elapsed = std::chrono::steady_clock::now() - start_time;
266 auto stop_status = Helpers::check_all_stop_conditions(
267 params, opts, time_elapsed, k, stop_signal, εₖ, no_progress);
269 do_progress_cb(k, *curr, null_vec<config_t>, grad_ψx̂, NaN<config_t>,
270 NaN<config_t>, εₖ, stop_status);
271 bool do_final_print = params.print_interval != 0;
272 if (!do_print && do_final_print)
273 print_progress_1(k, curr->fbe(), curr->ψx, curr->grad_ψ,
274 curr->pᵀp, curr->γ, εₖ, Δ);
275 if (do_print || do_final_print)
276 print_progress_n(stop_status);
280 opts.always_overwrite_results) {
282 if (err_z.size() > 0)
283 err_z = Σ.asDiagonal().inverse() * (ŷ - y);
284 x = std::move(curr->x̂);
285 y = std::move(curr->ŷx̂);
290 s.
elapsed_time = duration_cast<nanoseconds>(time_elapsed);
302 auto compute_FBS_step = [&] {
303 assert(curr->L >= params.L_max || !qub_violated(*curr));
305 if (not have_grad_ψx̂)
306 eval_grad_ψx̂(*curr, grad_ψx̂);
307 have_grad_ψx̂ =
true;
309 prox->ψx = curr->ψx̂;
310 prox->grad_ψ.swap(grad_ψx̂);
313 eval_ψ_grad_ψ(*prox);
314 eval_prox_grad_step(*prox);
323 direction.initialize(problem, y, Σ, prox->γ, prox->x, prox->x̂,
324 prox->p, prox->grad_ψ);
328 auto compute_candidate_fbe = [&](
crvec q) {
330 cand->x = prox->x + q;
332 eval_ψ_grad_ψ(*cand);
336 eval_prox_grad_step(*cand);
339 if (params.compute_ratio_using_new_stepsize) {
341 backtrack_qub(*cand);
346 auto compute_candidate_ratio = [
this, prox, cand](
real_t q_model) {
348 real_t ϕγ_next = cand->fbe();
349 real_t margin = (1 + std::abs(ϕγ)) * params.TR_tolerance_factor;
350 real_t ρ = (ϕγ - ϕγ_next + margin) / (-q_model);
351 return params.ratio_approx_fbe_quadratic_model
352 ? ρ / (1 - params.Lipschitz.Lγ_factor)
359 if (ρ >= params.ratio_threshold_good)
360 return std::max(params.radius_factor_good * q.norm(), old_Δ);
362 else if (ρ >= params.ratio_threshold_acceptable)
363 return old_Δ * params.radius_factor_acceptable;
366 return params.radius_factor_rejected * q.norm();
370 auto compute_trust_region_step = [&](
rvec q,
real_t Δ) {
371 auto t0 = std::chrono::steady_clock::now();
372 real_t q_model = direction.apply(prox->γ, prox->x, prox->x̂, prox->p,
374 auto t1 = std::chrono::steady_clock::now();
375 direction_duration = t1 - t0;
378 if (not q.allFinite()) {
379 *os <<
"Direction fail: not finite" << std::endl;
382 return +inf<config_t>;
385 *os <<
"Direction fail: no decrease on model (" << q_model
394 accept_candidate =
false;
395 bool accelerated_iteration = k > 0 || direction.has_initial_direction();
396 if (accelerated_iteration && !params.disable_acceleration) {
397 if (
auto q_model = compute_trust_region_step(q, Δ); q_model < 0) {
398 compute_candidate_fbe(q);
399 ρ = compute_candidate_ratio(q_model);
400 accept_candidate = ρ >= params.ratio_threshold_acceptable;
401 Δ = std::fmax(compute_updated_radius(q, ρ, Δ),
410 if (accept_candidate) {
412 if (!params.compute_ratio_using_new_stepsize) {
414 backtrack_qub(*cand);
417 if (prox->γ != cand->γ) {
418 direction.changed_γ(cand->γ, prox->γ);
419 if (params.recompute_last_prox_step_after_direction_reset) {
420 std::tie(prox->γ, prox->L) = std::tie(cand->γ, cand->L);
421 eval_prox_grad_step(*prox);
426 prox->γ, cand->γ, prox->x, cand->x, prox->p, cand->p,
427 prox->grad_ψ, cand->grad_ψ);
430 print_progress_2(q, ρ,
true, direction_duration);
432 std::swap(curr, cand);
436 if (accelerated_iteration)
440 backtrack_qub(*prox);
441 if (prox->γ != curr->γ) {
442 direction.changed_γ(prox->γ, curr->γ);
443 if (params.recompute_last_prox_step_after_direction_reset) {
444 std::tie(curr->γ, curr->L) = std::tie(prox->γ, prox->L);
445 eval_prox_grad_step(*curr);
449 if (params.update_direction_on_prox_step)
451 curr->γ, prox->γ, curr->x, prox->x, curr->p, prox->p,
452 curr->grad_ψ, prox->grad_ψ);
453 if (do_print && accelerated_iteration)
454 print_progress_2(q, ρ,
false, direction_duration);
456 std::swap(curr, prox);
471 throw std::logic_error(
"[PANTR] loop error");
std::string get_name() const
Stats operator()(const Problem &problem, const SolveOptions &opts, rvec x, rvec y, crvec Σ, rvec err_z)
unsigned direction_update_rejected
unsigned accelerated_step_rejected
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 direction_failures
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