alpaqa 1.0.0a17
Nonconvex constrained optimization
Loading...
Searching...
No Matches
lbfgsb-adapter.cpp
Go to the documentation of this file.
4
5#include <iomanip>
6#include <stdexcept>
7
8extern "C" {
9void alpaqa_setulb_c(int n, int m, double *x, const double *l, const double *u,
10 const int *nbd, double &f, double *g, double factr,
11 double pgtol, double *wa, int *iwa, char *task, int iprint,
12 char *csave, bool *lsave, int *isave, double *dsave);
13}
14
15namespace alpaqa::lbfgsb {
16
17std::string LBFGSBSolver::get_name() const { return "LBFGSBSolver"; }
19 /// [in] Problem description
20 const Problem &problem,
21 /// [in] Solve options
22 const SolveOptions &opts,
23 /// [inout] Decision variable @f$ x @f$
24 rvec x,
25 /// [inout] Lagrange multipliers @f$ y @f$
26 rvec y,
27 /// [in] Constraint weights @f$ \Sigma @f$
28 crvec Σ,
29 /// [out] Slack variable error @f$ g(x) - \Pi_D(g(x) + \Sigma^{-1} y) @f$
30 rvec err_z) -> Stats {
31
32 if (opts.check)
33 problem.check();
34
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;
40 if (opts.max_time)
41 max_time = std::min(max_time, *opts.max_time);
42 Stats s;
43
44 if (!problem.provides_get_box_C())
45 throw std::invalid_argument("LBFGSBSolver requires box constraints");
46 if (params.stop_crit != PANOCStopCrit::ProjGradUnitNorm)
47 throw std::invalid_argument("LBFGSBSolver only supports "
48 "PANOCStopCrit::ProjGradUnitNorm");
49
50 const auto n = problem.get_n();
51 const auto m_constr = problem.get_m();
52 const auto &C = problem.get_box_C();
53 const auto mem = static_cast<length_t>(params.memory);
54
55 auto do_progress_cb = [this, &s, &problem, &Σ, &y,
56 &opts](unsigned k, crvec x, real_t ψx, crvec grad_ψx,
57 real_t τ_max, real_t τ, real_t ε,
58 SolverStatus status) {
59 if (!progress_cb)
60 return;
63 progress_cb(ProgressInfo{
64 .k = k,
65 .status = status,
66 .x = x,
67 .ψ = ψx,
68 .grad_ψ = grad_ψx,
69 .τ_max = τ_max,
70 .τ = τ,
71 .ε = ε,
72 .Σ = Σ,
73 .y = y,
74 .outer_iter = opts.outer_iter,
75 .problem = &problem,
76 .params = &params,
77 });
78 };
79
80 using intvec = Eigen::VectorX<int>;
81
82 vec work_n(n), work_m(m_constr);
83 vec grad_ψ(n);
85 vec wa(2 * mem * n + 5 * n + 11 * mem * mem + 8 * mem);
86 vec dsave(29);
87 intvec iwa(3 * n), isave(44);
88 std::array<bool, 4> lsave{};
89 std::array<char, 60> task{}, csave{};
90
91 // Determine constraint type for each variable
92 intvec nbd(n);
93 for (index_t i = 0; i < n; ++i) {
94 static constexpr int nbd_legend[2][2] /* NOLINT(*c-arrays) */ {{0, 3},
95 {1, 2}};
96 int lowerbounded = C.lowerbound(i) == -inf<config_t> ? 0 : 1;
97 int upperbounded = C.upperbound(i) == +inf<config_t> ? 0 : 1;
99 }
100
101 vec x_solve = x;
102
103 real_t factr = 0;
104 real_t pgtol = 0;
105 int print = params.print;
106
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);
115 // const int &lbfgs_tot = isave.coeffRef(30);
116
117 auto set_task = [&](std::string_view s) {
118 std::fill(std::copy(s.begin(), s.end(), task.begin()), task.end(), ' ');
119 };
120
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);
124 };
125 auto print_real3 = [&print_buf](real_t x) {
126 return float_to_str_vw(print_buf, x, 3);
127 };
128 auto print_progress_1 = [&](unsigned k, real_t ψₖ, crvec grad_ψₖ,
129 real_t εₖ) {
130 if (k == 0)
131 *os << "┌─[LBFGSB]\n";
132 else
133 *os << "├─ " << std::setw(6) << k << " ──\n";
134 *os << "│ ψ = " << print_real(ψₖ) //
135 << ", ‖∇ψ‖ = " << print_real(grad_ψₖ.norm()) //
136 << ", ε = " << print_real(εₖ) << '\n';
137 };
138 auto print_progress_2 = [&](real_t q_norm, real_t τ_max, real_t τ_rel,
139 int nJ) {
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"
143 : "\033[0;35m";
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
148 << std::endl; // Flush for Python buffering
149 };
150 auto print_progress_n = [&](SolverStatus status) {
151 *os << "└─ " << status << " ──"
152 << std::endl; // Flush for Python buffering
153 };
154 bool did_print = false;
155
156 // Start solving
157 set_task("START");
158
159 while (true) {
160 // Invoke solver
161 alpaqa_setulb_c(static_cast<int>(n), static_cast<int>(mem),
162 x_solve.data(), C.lowerbound.data(),
163 C.upperbound.data(), nbd.data(), ψ, grad_ψ.data(),
164 factr, pgtol, wa.data(), iwa.data(), task.data(), print,
165 csave.data(), lsave.data(), isave.data(), dsave.data());
166
167 // New evaluation
168 if (task_sv.starts_with("FG")) {
169 ψ = problem.eval_ψ_grad_ψ(x_solve, y, Σ, grad_ψ, work_n, work_m);
170 }
171 // Converged
172 else if (task_sv.starts_with(
173 "CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL")) {
175 break;
176 }
177 // Next iteration
178 else if (task_sv.starts_with("NEW_X")) {
179 // Check termination
180 if (proj_grad_norm <= opts.tolerance) {
182 set_task("STOP: projected gradient norm");
183 break;
184 } else if (clock::now() - start_time >= max_time) {
186 set_task("STOP: time");
187 break;
188 } else if (static_cast<unsigned>(num_iter) >= params.max_iter) {
190 set_task("STOP: number iterations");
191 break;
192 } else if (stop_signal.stop_requested()) {
194 set_task("STOP: user request");
195 break;
196 } else {
197 auto k = static_cast<unsigned>(num_iter) - 1;
198 bool do_print = params.print_interval != 0 &&
199 k % params.print_interval == 0;
200 // Print info
201 if (std::exchange(did_print, do_print))
203 if (std::exchange(do_print, false))
204 print_progress_1(k, ψ, grad_ψ, proj_grad_norm);
205 // Progress callback
206 do_progress_cb(k, x, ψ, grad_ψ, τ_max, τ_rel, proj_grad_norm,
208 }
209 }
210 // Stop
211 else if (task_sv.starts_with("CONVERGENCE: REL_REDUCTION_OF_F")) {
213 break;
214 }
215 // Unexpected status
216 else {
218 break;
219 }
220 }
221
222 // Print info
223 auto k = static_cast<unsigned>(num_iter) - 1;
224 if (std::exchange(did_print, false))
226 else if (params.print_interval != 0)
227 print_progress_1(k, ψ, grad_ψ, proj_grad_norm);
228 // Error reporting
230 std::string_view task_trimmed = task_sv;
231 auto trim_pos = task_sv.find('\0');
232 trim_pos = task_sv.find_last_not_of(' ', trim_pos);
233 if (trim_pos != task_trimmed.npos)
234 task_trimmed.remove_suffix(task_trimmed.size() - trim_pos);
235 *os << "│ \033[0;31mLBFGSB failure\033[0m: '\033[0;33m" << task_trimmed
236 << "\033[0m'" << std::endl;
237 }
238 if (params.print_interval != 0)
240
241 // Progress callback
242 do_progress_cb(k, x, ψ, grad_ψ, τ_max, τ_rel, proj_grad_norm, s.status);
243
244 auto time_elapsed = clock::now() - start_time;
246 s.lbfgs_rejected = static_cast<unsigned>(lbfgs_skipped);
247 s.iterations = static_cast<unsigned>(num_iter);
248
249 // Check final error
250 s.ε = proj_grad_norm;
251 // Update result vectors
254 opts.always_overwrite_results) {
255 auto &ŷ = work_m;
256 s.final_ψ = problem.eval_ψ(x_solve, y, Σ, ŷ);
257 if (err_z.size() > 0)
258 err_z = (ŷ - y).cwiseQuotient(Σ);
259 x = x_solve;
260 y = ŷ;
261 }
262 return s;
263}
264
265} // namespace alpaqa::lbfgsb
Stats operator()(const Problem &problem, const SolveOptions &opts, rvec x, rvec y, crvec Σ, rvec err_z)
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)
std::chrono::nanoseconds time_progress_callback
std::chrono::nanoseconds elapsed_time
@ ProjGradUnitNorm
∞-norm of the projected gradient with unit step size:
SolverStatus
Exit status of a numerical solver such as ALM or PANOC.
@ Interrupted
Solver was interrupted by the user.
@ MaxTime
Maximum allowed execution time exceeded.
@ Exception
An unexpected exception was thrown.
@ NoProgress
No progress was made in the last iteration.
@ MaxIter
Maximum number of iterations exceeded.
@ Busy
In progress.
@ Converged
Converged and reached given tolerance.
typename Conf::real_t real_t
Definition config.hpp:86
typename Conf::index_t index_t
Definition config.hpp:104
typename Conf::length_t length_t
Definition config.hpp:103
constexpr const auto inf
Definition config.hpp:112
typename Conf::rvec rvec
Definition config.hpp:91
std::string_view float_to_str_vw(auto &buf, double value, int precision=std::numeric_limits< double >::max_digits10)
Definition print.tpp:39
typename Conf::crvec crvec
Definition config.hpp:92
typename Conf::vec vec
Definition config.hpp:88