alpaqa 1.0.0a11
Nonconvex constrained optimization
Loading...
Searching...
No Matches
lbfgsb-adapter.cpp
Go to the documentation of this file.
3
4#include <iomanip>
5#include <stdexcept>
6
7extern "C" {
8void alpaqa_setulb_c(int n, int m, double *x, const double *l, const double *u,
9 const int *nbd, double &f, double *g, double factr,
10 double pgtol, double *wa, int *iwa, char *task, int iprint,
11 char *csave, bool *lsave, int *isave, double *dsave);
12}
13
14namespace alpaqa::lbfgsb {
15
16std::string LBFGSBSolver::get_name() const { return "LBFGSBSolver"; }
18 /// [in] Problem description
19 const Problem &problem,
20 /// [in] Solve options
21 const SolveOptions &opts,
22 /// [inout] Decision variable @f$ x @f$
23 rvec x,
24 /// [inout] Lagrange multipliers @f$ y @f$
25 rvec y,
26 /// [in] Constraint weights @f$ \Sigma @f$
27 crvec Σ,
28 /// [out] Slack variable error @f$ g(x) - \Pi_D(g(x) + \Sigma^{-1} y) @f$
29 rvec err_z) -> Stats {
30
31 if (opts.check)
32 problem.check();
33
34 using std::chrono::nanoseconds;
35 using clock = std::chrono::steady_clock;
36 auto start_time = clock::now();
37 auto *os = opts.os ? opts.os : this->os;
38 auto max_time = params.max_time;
39 if (opts.max_time)
40 max_time = std::min(max_time, *opts.max_time);
41 Stats s;
42
43 if (!problem.provides_get_box_C())
44 throw std::invalid_argument("LBFGSBSolver requires box constraints");
45 if (params.stop_crit != PANOCStopCrit::ProjGradUnitNorm)
46 throw std::invalid_argument("LBFGSBSolver only supports "
47 "PANOCStopCrit::ProjGradUnitNorm");
48
49 const auto n = problem.get_n();
50 const auto m_constr = problem.get_m();
51 const auto &C = problem.get_box_C();
52 const auto mem = static_cast<length_t>(params.memory);
53
54 using intvec = Eigen::VectorX<int>;
55
56 vec work_n(n), work_m(m_constr);
57 vec grad_ψ(n);
58 real_t ψ = NaN<config_t>;
59 vec wa(2 * mem * n + 5 * n + 11 * mem * mem + 8 * mem);
60 vec dsave(29);
61 intvec iwa(3 * n), isave(44);
62 std::array<bool, 4> lsave{};
63 std::array<char, 60> task{}, csave{};
64
65 // Determine constraint type for each variable
66 intvec nbd(n);
67 for (index_t i = 0; i < n; ++i) {
68 static constexpr int nbd_legend[2][2] /* NOLINT(*c-arrays) */ {{0, 3},
69 {1, 2}};
70 int lowerbounded = C.lowerbound(i) == -inf<config_t> ? 0 : 1;
71 int upperbounded = C.upperbound(i) == +inf<config_t> ? 0 : 1;
72 nbd(i) = nbd_legend[lowerbounded][upperbounded];
73 }
74
75 vec x_solve = x;
76
77 real_t factr = 0;
78 real_t pgtol = 0;
79 int print = params.print;
80
81 std::string_view task_sv{task.begin(), task.end()};
82 const int &num_iter = isave.coeffRef(29);
83 const int &lbfgs_skipped = isave.coeffRef(25);
84 const int &num_free_var = isave.coeffRef(37);
85 const real_t &τ_max = dsave.coeffRef(11);
86 const real_t &proj_grad_norm = dsave.coeffRef(12);
87 const real_t &τ_rel = dsave.coeffRef(13);
88 // const int &lbfgs_tot = isave.coeffRef(30);
89
90 auto set_task = [&](std::string_view s) {
91 std::fill(std::copy(s.begin(), s.end(), task.begin()), task.end(), ' ');
92 };
93
94 std::array<char, 64> print_buf;
95 auto print_real = [this, &print_buf](real_t x) {
96 return float_to_str_vw(print_buf, x, params.print_precision);
97 };
98 auto print_real3 = [&print_buf](real_t x) {
99 return float_to_str_vw(print_buf, x, 3);
100 };
101 auto print_progress_1 = [&](int k, real_t ψₖ, crvec grad_ψₖ, real_t εₖ) {
102 if (k == 0)
103 *os << "┌─[LBFGSB]\n";
104 else
105 *os << "├─ " << std::setw(6) << k << " ──\n";
106 *os << "│ ψ = " << print_real(ψₖ) //
107 << ", ‖∇ψ‖ = " << print_real(grad_ψₖ.norm()) //
108 << ", ε = " << print_real(εₖ) << '\n';
109 };
110 auto print_progress_2 = [&](real_t τ_max, real_t τ_rel, int nJ) {
111 *os << "│ τ_max = " << print_real3(τ_max) //
112 << ", τ_rel = " << print_real3(τ_rel) //
113 << ", #J = " << std::setw(6) << nJ
114 << std::endl; // Flush for Python buffering
115 };
116 auto print_progress_n = [&](SolverStatus status) {
117 *os << "└─ " << status << " ──"
118 << std::endl; // Flush for Python buffering
119 };
120 bool do_print = false;
121 bool did_print = false;
122
123 // Start solving
124 set_task("START");
125
126 while (true) {
127 // Invoke solver
128 alpaqa_setulb_c(static_cast<int>(n), static_cast<int>(mem),
129 x_solve.data(), C.lowerbound.data(),
130 C.upperbound.data(), nbd.data(), ψ, grad_ψ.data(),
131 factr, pgtol, wa.data(), iwa.data(), task.data(), print,
132 csave.data(), lsave.data(), isave.data(), dsave.data());
133
134 // New evaluation
135 if (task_sv.starts_with("FG")) {
136 ψ = problem.eval_ψ_grad_ψ(x_solve, y, Σ, grad_ψ, work_n, work_m);
137 }
138 // Converged
139 else if (task_sv.starts_with(
140 "CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL")) {
142 break;
143 }
144 // Next iteration
145 else if (task_sv.starts_with("NEW_X")) {
146 do_print =
147 params.print_interval != 0 &&
148 static_cast<unsigned>(num_iter) % params.print_interval == 0;
149 // Print info
150 if (std::exchange(did_print, do_print))
151 print_progress_2(τ_max, τ_rel, num_free_var);
152 // Check termination
153 if (proj_grad_norm <= opts.tolerance) {
155 set_task("STOP: projected gradient norm");
156 break;
157 } else if (clock::now() - start_time >= max_time) {
159 set_task("STOP: time");
160 break;
161 } else if (static_cast<unsigned>(num_iter) >= params.max_iter) {
163 set_task("STOP: number iterations");
164 break;
165 } else if (stop_signal.stop_requested()) {
167 set_task("STOP: user request");
168 break;
169 } else {
170 // Print info
171 if (std::exchange(do_print, false))
172 print_progress_1(num_iter - 1, ψ, grad_ψ, proj_grad_norm);
173 }
174 }
175 // Stop
176 else if (task_sv.starts_with("CONVERGENCE: REL_REDUCTION_OF_F")) {
178 break;
179 }
180 // Unexpected status
181 else {
182 std::string_view task_trimmed = task_sv;
183 auto trim_pos = task_sv.find('\0');
184 trim_pos = task_sv.find_last_not_of(' ', trim_pos);
185 if (trim_pos != task_trimmed.npos)
186 task_trimmed.remove_suffix(task_trimmed.size() - trim_pos);
187 *os << "[LBFGSB] Unexpected task: '" << task_trimmed << "'"
188 << std::endl;
190 break;
191 }
192 }
193
194 if (std::exchange(did_print, false))
195 print_progress_2(τ_max, τ_rel, num_free_var);
196 else if (params.print_interval != 0)
197 print_progress_1(num_iter - 1, ψ, grad_ψ, proj_grad_norm);
198 if (params.print_interval != 0)
199 print_progress_n(s.status);
200
201 auto time_elapsed = clock::now() - start_time;
202 s.elapsed_time = duration_cast<nanoseconds>(time_elapsed);
203 s.lbfgs_rejected = static_cast<unsigned>(lbfgs_skipped);
204 s.iterations = static_cast<unsigned>(num_iter);
205
206 // Check final error
207 s.ε = proj_grad_norm;
208 // Update result vectors
211 opts.always_overwrite_results) {
212 auto &ŷ = work_m;
213 s.final_ψ = problem.eval_ψ(x_solve, y, Σ, ŷ);
214 if (err_z.size() > 0)
215 err_z = Σ.asDiagonal().inverse() * (ŷ - y);
216 x = x_solve;
217 y = ŷ;
218 }
219 return s;
220}
221
222} // namespace alpaqa::lbfgsb
std::string get_name() const
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 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.
@ Converged
Converged and reached given tolerance.
typename Conf::real_t real_t
Definition: config.hpp:63
typename Conf::index_t index_t
Definition: config.hpp:75
typename Conf::length_t length_t
Definition: config.hpp:74
typename Conf::rvec rvec
Definition: config.hpp:67
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:68
typename Conf::vec vec
Definition: config.hpp:64