35 using std::chrono::nanoseconds;
36 using clock = std::chrono::steady_clock;
37 auto start_time = clock::now();
38 auto *
os = opts.os ? opts.os : this->
os;
39 auto max_time =
params.max_time;
41 max_time = std::min(max_time, *opts.max_time);
44 if (!problem.provides_get_variable_bounds())
45 throw std::invalid_argument(
"LBFGSBSolver requires box constraints");
47 throw std::invalid_argument(
"LBFGSBSolver only supports "
48 "PANOCStopCrit::ProjGradUnitNorm");
50 const auto n = problem.get_num_variables();
51 const auto m_constr = problem.get_num_constraints();
52 const auto &C = problem.get_variable_bounds();
55 auto do_progress_cb = [
this, &s, &problem, &Σ, &y,
74 .outer_iter = opts.outer_iter,
80 using intvec = Eigen::VectorX<int>;
82 vec work_n(n), work_m(m_constr);
85 vec wa(2 * mem * n + 5 * n + 11 * mem * mem + 8 * mem);
87 intvec iwa(3 * n), isave(44);
88 std::array<bool, 4> lsave{};
89 std::array<char, 60> task{}, csave{};
93 for (
index_t i = 0; i < n; ++i) {
94 static constexpr int nbd_legend[2][2] {{0, 3},
98 nbd(i) = nbd_legend[lowerbounded][upperbounded];
107 std::string_view task_sv{task.begin(), task.end()};
108 const int &num_iter = isave.coeffRef(29);
109 const int &lbfgs_skipped = isave.coeffRef(25);
110 const int &num_free_var = isave.coeffRef(37);
111 const real_t &q_norm = dsave.coeffRef(3);
112 const real_t &τ_max = dsave.coeffRef(11);
113 const real_t &proj_grad_norm = dsave.coeffRef(12);
114 const real_t &τ_rel = dsave.coeffRef(13);
117 auto set_task = [&](std::string_view s) {
118 std::fill(std::copy(s.begin(), s.end(), task.begin()), task.end(),
' ');
121 std::array<char, 64> print_buf;
122 auto print_real = [
this, &print_buf](
real_t x) {
123 return float_to_str_vw(print_buf, x,
params.print_precision);
125 auto print_real3 = [&print_buf](
real_t x) {
126 return float_to_str_vw(print_buf, x, 3);
128 auto print_progress_1 = [&](
unsigned k,
real_t ψₖ,
crvec grad_ψₖ,
131 *
os <<
"┌─[LBFGSB]\n";
133 *
os <<
"├─ " << std::setw(6) << k <<
" ──\n";
134 *
os <<
"│ ψ = " << print_real(ψₖ)
135 <<
", ‖∇ψ‖ = " << print_real(grad_ψₖ.norm())
136 <<
", ε = " << print_real(εₖ) <<
'\n';
140 const auto thres = std::sqrt(std::numeric_limits<real_t>::epsilon());
141 const char *color = τ_rel == 1 ?
"\033[0;32m"
142 : τ_rel > thres ?
"\033[0;33m"
144 *
os <<
"│ ‖q‖ = " << print_real(q_norm)
145 <<
", τ = " << color << print_real3(τ_rel) <<
"\033[0m"
146 <<
", τ_max = " << print_real3(τ_max)
147 <<
", #J = " << std::setw(6) << nJ
151 *
os <<
"└─ " << status <<
" ──"
154 bool did_print =
false;
162 x_solve.data(), C.lower.data(), C.upper.data(),
163 nbd.data(), ψ, grad_ψ.data(), factr, pgtol, wa.data(),
164 iwa.data(), task.data(), print, csave.data(),
165 lsave.data(), isave.data(), dsave.data());
168 if (task_sv.starts_with(
"FG")) {
169 ψ = problem.eval_augmented_lagrangian_and_gradient(
170 x_solve, y, Σ, grad_ψ, work_n, work_m);
173 else if (task_sv.starts_with(
174 "CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL")) {
179 else if (task_sv.starts_with(
"NEW_X")) {
181 if (proj_grad_norm <= opts.tolerance) {
183 set_task(
"STOP: projected gradient norm");
185 }
else if (clock::now() - start_time >= max_time) {
187 set_task(
"STOP: time");
189 }
else if (
static_cast<unsigned>(num_iter) >=
params.max_iter) {
191 set_task(
"STOP: number iterations");
195 set_task(
"STOP: user request");
198 auto k =
static_cast<unsigned>(num_iter) - 1;
199 bool do_print =
params.print_interval != 0 &&
200 k %
params.print_interval == 0;
202 if (std::exchange(did_print, do_print))
203 print_progress_2(q_norm, τ_max, τ_rel, num_free_var);
204 if (std::exchange(do_print,
false))
205 print_progress_1(k, ψ, grad_ψ, proj_grad_norm);
207 do_progress_cb(k, x, ψ, grad_ψ, τ_max, τ_rel, proj_grad_norm,
212 else if (task_sv.starts_with(
"CONVERGENCE: REL_REDUCTION_OF_F")) {
224 auto k =
static_cast<unsigned>(num_iter) - 1;
225 if (std::exchange(did_print,
false))
226 print_progress_2(q_norm, τ_max, τ_rel, num_free_var);
227 else if (
params.print_interval != 0)
228 print_progress_1(k, ψ, grad_ψ, proj_grad_norm);
231 std::string_view task_trimmed = task_sv;
232 auto trim_pos = task_sv.find(
'\0');
233 trim_pos = task_sv.find_last_not_of(
' ', trim_pos);
234 if (trim_pos != task_trimmed.npos)
235 task_trimmed.remove_suffix(task_trimmed.size() - trim_pos);
236 *
os <<
"│ \033[0;31mLBFGSB failure\033[0m: '\033[0;33m" << task_trimmed
237 <<
"\033[0m'" << std::endl;
239 if (
params.print_interval != 0)
240 print_progress_n(s.
status);
243 do_progress_cb(k, x, ψ, grad_ψ, τ_max, τ_rel, proj_grad_norm, s.
status);
245 auto time_elapsed = clock::now() - start_time;
246 s.
elapsed_time = duration_cast<nanoseconds>(time_elapsed);
248 s.
iterations =
static_cast<unsigned>(num_iter);
251 s.
ε = proj_grad_norm;
255 opts.always_overwrite_results) {
257 s.
final_ψ = problem.eval_augmented_lagrangian(x_solve, y, Σ, ŷ);
258 if (err_z.size() > 0)
259 err_z = (ŷ - y).cwiseQuotient(Σ);
void alpaqa_setulb_c(int n, int m, double *x, const double *l, const double *u, const int *nbd, double &f, double *g, double factr, double pgtol, double *wa, int *iwa, char *task, int iprint, char *csave, bool *lsave, int *isave, double *dsave)