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);
116 int num_iter_tot = 0;
117 int lbfgs_skipped_tot = 0;
119 auto set_task = [&](std::string_view s) {
120 std::fill(std::copy(s.begin(), s.end(), task.begin()), task.end(),
' ');
123 auto check_termination = [&] {
124 if (proj_grad_norm <= opts.tolerance) {
126 set_task(
"STOP: projected gradient norm");
128 }
else if (clock::now() - start_time >= max_time) {
130 set_task(
"STOP: time");
132 }
else if (
static_cast<unsigned>(num_iter + num_iter_tot) >=
135 set_task(
"STOP: number iterations");
139 set_task(
"STOP: user request");
145 std::array<char, 64> print_buf;
146 auto print_real = [
this, &print_buf](
real_t x) {
147 return float_to_str_vw(print_buf, x,
params.print_precision);
149 auto print_real3 = [&print_buf](
real_t x) {
150 return float_to_str_vw(print_buf, x, 3);
152 auto print_progress_1 = [&](
unsigned k,
real_t ψₖ,
crvec grad_ψₖ,
155 *
os <<
"┌─[LBFGSB]\n";
157 *
os <<
"├─ " << std::setw(6) << k <<
" ──\n";
158 *
os <<
"│ ψ = " << print_real(ψₖ)
159 <<
", ‖∇ψ‖ = " << print_real(grad_ψₖ.norm())
160 <<
", ε = " << print_real(εₖ) <<
'\n';
164 const auto thres = std::sqrt(std::numeric_limits<real_t>::epsilon());
165 const char *color = τ_rel == 1 ?
"\033[0;32m"
166 : τ_rel > thres ?
"\033[0;33m"
168 *
os <<
"│ ‖q‖ = " << print_real(q_norm)
169 <<
", τ = " << color << print_real3(τ_rel) <<
"\033[0m"
170 <<
", τ_max = " << print_real3(τ_max)
171 <<
", #J = " << std::setw(6) << nJ
175 *
os <<
"└─ " << status <<
" ──"
178 auto print_error = [&](std::string_view task_sv) {
179 std::string_view task_trimmed = task_sv;
180 auto trim_pos = task_sv.find(
'\0');
181 trim_pos = task_sv.find_last_not_of(
' ', trim_pos);
182 if (trim_pos != task_trimmed.npos)
183 task_trimmed.remove_suffix(task_trimmed.size() - trim_pos);
184 *
os <<
"│ \033[0;31mLBFGSB failure\033[0m: '\033[0;33m" << task_trimmed
185 <<
"\033[0m'" << std::endl;
187 bool did_print =
false;
195 x_solve.data(), C.lower.data(), C.upper.data(),
196 nbd.data(), ψ, grad_ψ.data(), factr, pgtol, wa.data(),
197 iwa.data(), task.data(), print, csave.data(),
198 lsave.data(), isave.data(), dsave.data());
201 if (task_sv.starts_with(
"FG")) {
202 ψ = problem.eval_augmented_lagrangian_and_gradient(
203 x_solve, y, Σ, grad_ψ, work_n, work_m);
206 else if (task_sv.starts_with(
207 "CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL")) {
212 else if (task_sv.starts_with(
"NEW_X")) {
214 if (check_termination()) {
217 auto k =
static_cast<unsigned>(num_iter) - 1;
218 bool do_print =
params.print_interval != 0 &&
219 k %
params.print_interval == 0;
221 if (std::exchange(did_print, do_print))
222 print_progress_2(q_norm, τ_max, τ_rel, num_free_var);
224 print_progress_1(k, ψ, grad_ψ, proj_grad_norm);
226 do_progress_cb(k, x, ψ, grad_ψ, τ_max, τ_rel, proj_grad_norm,
231 else if (task_sv.starts_with(
"CONVERGENCE: REL_REDUCTION_OF_F")) {
234 }
else if (task_sv.starts_with(
"ERROR: ")) {
241 if (check_termination() || !
params.restart_on_error) {
244 if (
params.print_interval != 0) {
245 auto k =
static_cast<unsigned>(num_iter) - 2;
246 if (!std::exchange(did_print,
false))
247 print_progress_1(k, ψ, grad_ψ, proj_grad_norm);
248 print_progress_2(q_norm, τ_max, τ_rel, num_free_var);
249 print_error(task_sv);
250 print_progress_n(s.
status);
253 num_iter_tot += std::max(0, num_iter - 1);
254 lbfgs_skipped_tot += lbfgs_skipped;
261 auto k =
static_cast<unsigned>(num_iter) - 1;
262 if (std::exchange(did_print,
false))
263 print_progress_2(q_norm, τ_max, τ_rel, num_free_var);
264 else if (
params.print_interval != 0)
265 print_progress_1(k, ψ, grad_ψ, proj_grad_norm);
268 print_error(task_sv);
269 if (
params.print_interval != 0)
270 print_progress_n(s.
status);
273 do_progress_cb(k, x, ψ, grad_ψ, τ_max, τ_rel, proj_grad_norm, s.
status);
275 auto time_elapsed = clock::now() - start_time;
276 s.
elapsed_time = duration_cast<nanoseconds>(time_elapsed);
278 static_cast<unsigned>(lbfgs_skipped + lbfgs_skipped_tot);
279 s.
iterations =
static_cast<unsigned>(num_iter + num_iter_tot);
282 s.
ε = proj_grad_norm;
286 opts.always_overwrite_results) {
288 s.
final_ψ = problem.eval_augmented_lagrangian(x_solve, y, Σ, ŷ);
289 if (err_z.size() > 0)
290 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)