alpaqa 1.0.0a9
Nonconvex constrained optimization
Loading...
Searching...
No Matches
cutest-loader.cpp
Go to the documentation of this file.
3
4#include <cutest.h>
5#include <dlfcn.h>
6
7#include <cassert>
8#include <functional>
9#include <iostream>
10#include <memory>
11#include <stdexcept>
12#include <string>
13#include <string_view>
14#include <vector>
15
16/*
17CUTEST_cfn : function and constraints values
18CUTEST_cofg : function value and possibly gradient
19CUTEST_cofsg : function value and possibly gradient in sparse format
20CUTEST_ccfg : constraint functions values and possibly gradients
21CUTEST_clfg : Lagrangian function value and possibly gradient
22CUTEST_cgr : constraints gradients and gradient of objective/Lagrangian function
23CUTEST_csgr : constraints gradients and gradient of objective/Lagrangian function
24CUTEST_ccfsg : constraint functions values and possibly their gradients in sparse format
25CUTEST_ccifg : single constraint function value and possibly its gradient
26CUTEST_ccifsg : single constraint function value and possibly gradient in sparse format
27CUTEST_cgrdh : constraints gradients, Hessian of Lagrangian function and gradient of objective/Lagrangian function
28CUTEST_cdh : Hessian of the Lagrangian
29CUTEST_cdhc : Hessian of the constraint part of the Lagrangian
30CUTEST_cshp : sparsity pattern of the Hessian of the Lagrangian function
31CUTEST_csh : Hessian of the Lagrangian, in sparse format
32CUTEST_cshc : Hessian of the constraint part of the Lagrangian, in sparse format
33CUTEST_ceh : sparse Lagrangian Hessian matrix in finite element format
34CUTEST_cifn : problem function value
35CUTEST_cigr : gradient of a problem function
36CUTEST_cisgr : gradient of a problem function in sparse format
37CUTEST_cidh : Hessian of a problem function
38CUTEST_cish : Hessian of an individual problem function, in sparse format
39CUTEST_csgrsh : constraints gradients, sparse Lagrangian Hessian and the gradient of either the objective/Lagrangian in sparse format
40CUTEST_csgreh : constraint gradients, the Lagrangian Hessian in finite element format and the gradient of either the objective/Lagrangian in sparse format
41CUTEST_chprod : matrix-vector product of a vector with the Hessian matrix of the Lagrangian
42CUTEST_cshprod : matrix-vector product of a sparse vector with the Hessian matrix of the Lagrangian
43CUTEST_chcprod : matrix-vector product of a vector with the Hessian matrix of the constraint part of the Lagrangian
44CUTEST_cshcprod : matrix-vector product of a spaarse vector with the Hessian matrix of the constraint part of the Lagrangian
45CUTEST_cjprod : matrix-vector product of a vector with the Jacobian of the constraints, or its transpose
46CUTEST_csjprod : matrix-vector product of a sparse vector with the Jacobian of the constraints, or its transpose
47CUTEST_cchprods : matrix-vector products of a vector with each of the Hessian matrices of the constraint functions
48*/
49
50#ifndef CUTEST_csjp
51#define CUTEST_csjp FUNDERSCORE(cutest_csjp)
52void CUTEST_csjp(integer *status, integer *nnzj, const integer *lj,
53 integer *J_var, integer *J_con);
54#endif
55
56#define STR(x) #x
57#define LOAD_DL_FUNC(f) dlfun<decltype(f)>(STR(f))
58
59using namespace std::string_literals;
60
61namespace {
62void throw_error(std::string s, int code) {
63 throw std::runtime_error(s + " (" + std::to_string(code) + ")");
64}
65void throw_if_error(std::string s, int code) {
66 if (code)
67 throw_error(s, code);
68}
69void log_if_error(std::string s, int code) {
70 if (code)
71 std::cerr << s << " (" << code << ")\n";
72}
73
74std::shared_ptr<void> load_lib(const char *so_filename) {
75 assert(so_filename);
76 ::dlerror();
77 void *h = ::dlopen(so_filename, RTLD_LOCAL | RTLD_NOW);
78 if (auto *err = ::dlerror())
79 throw std::runtime_error(err);
80 return std::shared_ptr<void>{h, &::dlclose};
81}
82} // namespace
83
84namespace alpaqa {
85
87 public:
88 USING_ALPAQA_CONFIG(CUTEstProblem::config_t);
90
91 private:
92 using cleanup_t = std::shared_ptr<void>;
93 template <class F>
94 cleanup_t cleanup(F &&func) {
95 return cleanup_t{nullptr,
96 [func{std::forward<F>(func)}](void *) { func(); }};
97 }
98
99 cleanup_t load_outsdif(const char *outsdif_fname) {
100 integer status;
101 auto fptr_open = LOAD_DL_FUNC(FORTRAN_open);
102 auto fptr_close = LOAD_DL_FUNC(FORTRAN_close);
103 fptr_open(&funit, outsdif_fname, &status);
104 throw_if_error("Failed to open "s + outsdif_fname, status);
105 return cleanup([funit{this->funit}, fptr_close] {
106 integer status;
107 fptr_close(&funit, &status);
108 log_if_error("Failed to close OUTSDIF.d file", status);
109 });
110 }
111
113 auto fptr_cterminate = LOAD_DL_FUNC(CUTEST_cterminate);
114 return cleanup([fptr_cterminate] {
115 integer status;
116 fptr_cterminate(&status);
117 log_if_error("Failed to call cutest_cterminate", status);
118 });
119 }
120
121 public:
122 CUTEstLoader(const char *so_fname, const char *outsdif_fname) {
123 // Open the shared library
124 so_handle = load_lib(so_fname);
125
126 // Open the OUTSDIF.d file
127 cleanup_outsdif = load_outsdif(outsdif_fname);
128
129 // Get the dimensions of the problem
130 integer status;
131 auto fptr_cdimen = LOAD_DL_FUNC(CUTEST_cdimen);
132 fptr_cdimen(&status, &funit, &nvar, &ncon);
133 throw_if_error("Failed to call cutest_cdimen", status);
134 }
135
136 struct ConstrFuncs {
137 decltype(CUTEST_cfn) *cfn;
138 decltype(CUTEST_cofg) *cofg;
139 decltype(CUTEST_ccfg) *ccfg;
140 decltype(CUTEST_clfg) *clfg;
141 decltype(CUTEST_cjprod) *cjprod;
142 decltype(CUTEST_ccifg) *ccifg;
143 decltype(CUTEST_cigr) *cigr;
144 decltype(CUTEST_cdimsj) *cdimsj;
145 decltype(CUTEST_csjp) *csjp;
146 decltype(CUTEST_ccfsg) *ccfsg;
147 decltype(CUTEST_cdh) *cdh;
148 decltype(CUTEST_cdimsh) *cdimsh;
149 decltype(CUTEST_cshp) *cshp;
150 decltype(CUTEST_csh) *csh;
151 decltype(CUTEST_chprod) *chprod;
152 };
153
154 void setup_problem(rvec x0, rvec y0, Box &C, Box &D) {
155 assert(x0.size() == static_cast<length_t>(nvar));
156 assert(C.lowerbound.size() == static_cast<length_t>(nvar));
157 assert(C.upperbound.size() == static_cast<length_t>(nvar));
158 assert(y0.size() == static_cast<length_t>(ncon));
159 assert(D.lowerbound.size() == static_cast<length_t>(ncon));
160 assert(D.upperbound.size() == static_cast<length_t>(ncon));
161 equatn.resize(static_cast<length_t>(ncon));
162 linear.resize(static_cast<length_t>(ncon));
163 integer e_order = 0; // no specific order of equality constraints
164 integer l_order = 0; // no specific order of linear constraints
165 integer v_order = 0; // no specific order of linear variables
166 integer status;
167
168 LOAD_DL_FUNC(CUTEST_csetup)
169 (&status, &funit, &iout, &io_buffer, &nvar, &ncon, x0.data(),
170 C.lowerbound.data(), C.upperbound.data(), y0.data(),
171 D.lowerbound.data(), D.upperbound.data(), equatn.data(), linear.data(),
172 &e_order, &l_order, &v_order);
173 throw_if_error("Failed to call cutest_csetup", status);
175 if (ncon == 0)
176 throw std::runtime_error(
177 "Unconstrained CUTEst problems are currently unsupported");
178 work.resize(std::max(nvar, ncon));
179 work2.resize(std::max(nvar, ncon));
180 std::replace(C.lowerbound.begin(), C.lowerbound.end(), -CUTE_INF,
181 -inf<config_t>);
182 std::replace(C.upperbound.begin(), C.upperbound.end(), +CUTE_INF,
183 +inf<config_t>);
184 std::replace(D.lowerbound.begin(), D.lowerbound.end(), -CUTE_INF,
185 -inf<config_t>);
186 std::replace(D.upperbound.begin(), D.upperbound.end(), +CUTE_INF,
187 +inf<config_t>);
188 funcs = {
189 .cfn = LOAD_DL_FUNC(CUTEST_cfn),
190 .cofg = LOAD_DL_FUNC(CUTEST_cofg),
191 .ccfg = LOAD_DL_FUNC(CUTEST_ccfg),
192 .clfg = LOAD_DL_FUNC(CUTEST_clfg),
193 .cjprod = LOAD_DL_FUNC(CUTEST_cjprod),
194 .ccifg = LOAD_DL_FUNC(CUTEST_ccifg),
195 .cigr = LOAD_DL_FUNC(CUTEST_cigr),
196 .cdimsj = LOAD_DL_FUNC(CUTEST_cdimsj),
197 .csjp = LOAD_DL_FUNC(CUTEST_csjp),
198 .ccfsg = LOAD_DL_FUNC(CUTEST_ccfsg),
199 .cdh = LOAD_DL_FUNC(CUTEST_cdh),
200 .cdimsh = LOAD_DL_FUNC(CUTEST_cdimsh),
201 .cshp = LOAD_DL_FUNC(CUTEST_cshp),
202 .csh = LOAD_DL_FUNC(CUTEST_csh),
203 .chprod = LOAD_DL_FUNC(CUTEST_chprod),
204 };
205 }
206
207 std::string get_name() {
208 std::string name(FSTRING_LEN, ' ');
209 integer status;
210 LOAD_DL_FUNC(CUTEST_probname)(&status, name.data());
211 throw_if_error("Failed to call CUTEST_probname", status);
212 if (auto last = name.find_last_not_of(' '); last != name.npos)
213 name.resize(last + 1);
214 return name;
215 }
216
217 integer get_report(doublereal *calls, doublereal *time) {
218 integer status;
219 auto fptr_report = ncon > 0 ? LOAD_DL_FUNC(CUTEST_creport)
220 : LOAD_DL_FUNC(CUTEST_ureport);
221 fptr_report(&status, calls, time);
222 return status;
223 }
224
225 template <class T>
226 T *dlfun(const char *name) {
227 (void)dlerror();
228 auto res = reinterpret_cast<T *>(::dlsym(so_handle.get(), name));
229 if (const char *error = dlerror())
230 throw std::runtime_error(error);
231 return res;
232 }
233
234 // Order of cleanup is important!
235 std::shared_ptr<void> so_handle; ///< dlopen handle to shared library
236 cleanup_t cleanup_outsdif; ///< Responsible for closing the OUTSDIF.d file
237 cleanup_t cutest_terminate; ///< Responsible for calling CUTEST_xterminate
238
239 integer funit = 42; ///< Fortran Unit Number for OUTSDIF.d file
240 integer iout = 6; ///< Fortran Unit Number for standard output
241 integer io_buffer = 11; ///< Fortran Unit Number for internal IO
242
243 integer nvar; ///< Number of decision variabls
244 integer ncon; ///< Number of constraints
245 ConstrFuncs funcs; /// Pointers to loaded problem functions
246
247 using logical_vec = Eigen::VectorX<logical>;
248 logical_vec equatn; ///< whether the constraint is an equality
249 logical_vec linear; ///< whether the constraint is linear
250 mutable vec work, work2; ///< work vectors
251};
252
253CUTEstProblem::CUTEstProblem(const char *so_fname, const char *outsdif_fname,
254 bool sparse)
255 : BoxConstrProblem<config_t>{0, 0}, sparse{sparse} {
256 impl = std::make_unique<CUTEstLoader>(so_fname, outsdif_fname);
257 resize(static_cast<length_t>(impl->nvar),
258 static_cast<length_t>(impl->ncon));
259 x0.resize(n);
260 y0.resize(m);
261 impl->setup_problem(x0, y0, C, D);
262 name = impl->get_name();
263}
264
265CUTEstProblem::CUTEstProblem(const CUTEstProblem &) = default;
266CUTEstProblem &CUTEstProblem::operator=(const CUTEstProblem &) = default;
267CUTEstProblem::CUTEstProblem(CUTEstProblem &&) noexcept = default;
268CUTEstProblem &CUTEstProblem::operator=(CUTEstProblem &&) noexcept = default;
269CUTEstProblem::~CUTEstProblem() = default;
270
271CUTEstProblem::Report CUTEstProblem::get_report() const {
272 double calls[7];
273 double time[2];
274 Report r;
275 using stat_t = decltype(r.status);
276 r.status = static_cast<stat_t>(impl->get_report(calls, time));
277 r.name = impl->get_name();
278 r.nvar = impl->nvar;
279 r.ncon = impl->ncon;
280 r.calls.objective = static_cast<unsigned>(calls[0]);
281 r.calls.objective_grad = static_cast<unsigned>(calls[1]);
282 r.calls.objective_hess = static_cast<unsigned>(calls[2]);
283 r.calls.hessian_times_vector = static_cast<unsigned>(calls[3]);
284 r.calls.constraints = impl->ncon > 0 ? static_cast<unsigned>(calls[4]) : 0;
285 r.calls.constraints_grad =
286 impl->ncon > 0 ? static_cast<unsigned>(calls[5]) : 0;
287 r.calls.constraints_hess =
288 impl->ncon > 0 ? static_cast<unsigned>(calls[6]) : 0;
289 r.time_setup = time[0];
290 r.time = time[1];
291 return r;
292}
293
295 assert(x.size() == static_cast<length_t>(impl->nvar));
296 integer status;
297 real_t f;
298 logical grad = FALSE_;
299 impl->funcs.cofg(&status, &impl->nvar, x.data(), &f, nullptr, &grad);
300 throw_if_error("eval_f: CUTEST_cofg", status);
301 return f;
302}
303void CUTEstProblem::eval_grad_f(crvec x, rvec grad_fx) const {
304 assert(x.size() == static_cast<length_t>(impl->nvar));
305 assert(grad_fx.size() == static_cast<length_t>(impl->nvar));
306 integer status;
307 real_t f;
308 logical grad = TRUE_;
309 impl->funcs.cofg(&status, &impl->nvar, x.data(), &f, grad_fx.data(), &grad);
310 throw_if_error("eval_grad_f: CUTEST_cofg", status);
311}
313 assert(x.size() == static_cast<length_t>(impl->nvar));
314 assert(gx.size() == static_cast<length_t>(impl->ncon));
315 integer status;
316 logical jtrans = TRUE_, grad = FALSE_;
317 integer zero = 0;
318 impl->funcs.ccfg(&status, &impl->nvar, &impl->ncon, x.data(), gx.data(),
319 &jtrans, &zero, &zero, nullptr, &grad);
320 throw_if_error("eval_g: CUTEST_ccfg", status);
321}
323 assert(x.size() == static_cast<length_t>(impl->nvar));
324 assert(y.size() == static_cast<length_t>(impl->ncon));
325 assert(grad_gxy.size() == static_cast<length_t>(impl->nvar));
326 integer status;
327 auto lvector = static_cast<integer>(y.size()),
328 lresult = static_cast<integer>(grad_gxy.size());
329 logical gotj = FALSE_, jtrans = TRUE_;
330 impl->funcs.cjprod(&status, &impl->nvar, &impl->ncon, &gotj, &jtrans,
331 x.data(), y.data(), &lvector, grad_gxy.data(), &lresult);
332 throw_if_error("eval_grad_g_prod: CUTEST_cjprod", status);
333}
334
335void CUTEstProblem::eval_jac_g(crvec x, [[maybe_unused]] rindexvec inner_idx,
336 [[maybe_unused]] rindexvec outer_ptr,
337 rvec J_values) const {
338 // Compute the nonzero values
339 if (J_values.size() > 0) {
340 assert(x.size() == static_cast<length_t>(impl->nvar));
341 integer status;
342 // Sparse Jacobian
343 if (sparse) {
344 assert(nnz_J >= 0);
345 assert(J_values.size() == static_cast<length_t>(nnz_J));
346 assert(J_work.size() == static_cast<length_t>(nnz_J));
347 assert(inner_idx.size() == static_cast<length_t>(nnz_J));
348 assert(outer_ptr.size() == static_cast<length_t>(impl->nvar + 1));
349 const integer nnz = nnz_J;
350 logical grad = TRUE_;
351 impl->funcs.ccfsg(&status, &impl->nvar, &impl->ncon, x.data(),
352 impl->work.data(), &nnz_J, &nnz, J_work.data(),
353 J_col.data(), J_row.data(), &grad);
354 throw_if_error("eval_jac_g: CUTEST_ccfsg", status);
355 auto t0 = std::chrono::steady_clock::now();
356 J_values = J_work(J_perm);
357 auto t1 = std::chrono::steady_clock::now();
358 std::cout << "Permutation of J took: "
359 << std::chrono::duration<double>{t1 - t0}.count() * 1e6
360 << " µs\n";
361 }
362 // Dense Jacobian
363 else {
364 assert(J_values.size() == static_cast<length_t>(impl->nvar) *
365 static_cast<length_t>(impl->ncon));
366 integer status;
367 logical jtrans = FALSE_, grad = TRUE_;
368 impl->funcs.ccfg(&status, &impl->nvar, &impl->ncon, x.data(),
369 impl->work.data(), &jtrans, &impl->ncon,
370 &impl->nvar, J_values.data(), &grad);
371 throw_if_error("eval_jac_g: CUTEST_ccfg", status);
372 }
373 }
374 // Compute sparsity pattern without values
375 else {
376 assert(nnz_J >= 0);
377 assert(inner_idx.size() == static_cast<length_t>(nnz_J));
378 assert(outer_ptr.size() == static_cast<length_t>(impl->nvar + 1));
379 integer status;
380 const integer nnz = nnz_J;
381 impl->funcs.csjp(&status, &nnz_J, &nnz, J_col.data(), J_row.data());
382 throw_if_error("eval_jac_g: CUTEST_csjp", status);
383 std::iota(J_perm.begin(), J_perm.end(), index_t{0});
384 util::sort_triplets(J_row, J_col, J_perm);
385 util::convert_triplets_to_ccs<config_t>(J_row, J_col, inner_idx,
386 outer_ptr, 1);
387 }
388}
390 if (!sparse)
391 return -1;
392 if (nnz_J < 0) {
393 integer status;
394 impl->funcs.cdimsj(&status, &nnz_J);
395 throw_if_error("get_jac_g_num_nonzeros: CUTEST_cdimsj", status);
396 nnz_J -= impl->nvar;
397 assert(nnz_J >= 0);
398 J_col.resize(nnz_J);
399 J_row.resize(nnz_J);
400 J_perm.resize(nnz_J);
401 J_work.resize(nnz_J);
402 }
403 return nnz_J;
404}
406 assert(x.size() == static_cast<length_t>(impl->nvar));
407 assert(grad_gi.size() == static_cast<length_t>(impl->nvar));
408 integer status;
409 auto iprob = static_cast<integer>(i + 1);
410 impl->funcs.cigr(&status, &impl->nvar, &iprob, x.data(), grad_gi.data());
411 throw_if_error("eval_grad_gi: CUTEST_cigr", status);
412}
414 rvec Hv) const {
415 assert(x.size() == static_cast<length_t>(impl->nvar));
416 assert(y.size() == static_cast<length_t>(impl->ncon));
417 assert(v.size() == static_cast<length_t>(impl->nvar));
418 assert(Hv.size() == static_cast<length_t>(impl->nvar));
419 const auto *mult = y.data();
420 if (scale != 1) {
421 impl->work = y * (real_t(1) / scale);
422 mult = impl->work.data();
423 }
424 integer status;
425 logical goth = FALSE_;
426 impl->funcs.chprod(&status, &impl->nvar, &impl->ncon, &goth, x.data(), mult,
427 const_cast<real_t *>(v.data()), Hv.data());
428 throw_if_error("eval_hess_L_prod: CUTEST_chprod", status);
429 if (scale != 1)
430 Hv *= scale;
431}
433 crvec v, rvec Hv) const {
434 assert(x.size() == static_cast<length_t>(impl->nvar));
435 assert(y.size() == static_cast<length_t>(impl->ncon));
436 assert(Σ.size() == static_cast<length_t>(impl->ncon));
437 assert(v.size() == static_cast<length_t>(impl->nvar));
438 assert(Hv.size() == static_cast<length_t>(impl->nvar));
439 auto &&ζ = impl->work.topRows(impl->ncon);
440 auto &&ŷ = impl->work2.topRows(impl->ncon);
441 // ζ = g(x) + Σ⁻¹y
442 eval_g(x, ζ);
443 ζ += Σ.asDiagonal().inverse() * y;
444 // d = ζ - Π(ζ, D)
445 this->eval_proj_diff_g(ζ, ŷ);
446 // ŷ = Σ d
447 ŷ.array() *= Σ.array();
448 // Hv = ∇²ℒ(x, ŷ) v
449 eval_hess_L_prod(x, ŷ, scale, v, Hv);
450 // Find active constraints
451 for (index_t i = 0; i < impl->ncon; ++i)
452 ζ(i) = (ζ(i) <= D.lowerbound(i)) || (D.upperbound(i) <= ζ(i))
453 ? Σ(i)
454 : real_t(0);
455 // Jg(x) v
456 auto &&Jv = impl->work2.topRows(impl->ncon);
457 integer status;
458 auto lvector = static_cast<integer>(v.size()),
459 lresult = static_cast<integer>(Jv.size());
460 logical gotj = FALSE_, jtrans = FALSE_;
461 impl->funcs.cjprod(&status, &impl->nvar, &impl->ncon, &gotj, &jtrans,
462 x.data(), v.data(), &lvector, Jv.data(), &lresult);
463 throw_if_error("eval_hess_ψ_prod: CUTEST_cjprod-1", status);
464 // Σ Jg v
465 Jv.array() *= ζ.array();
466 // Jgᵀ Σ Jg v
467 std::swap(lvector, lresult);
468 gotj = jtrans = TRUE_;
469 auto &&JΣJv = impl->work.topRows(impl->nvar);
470 impl->funcs.cjprod(&status, &impl->nvar, &impl->ncon, &gotj, &jtrans,
471 x.data(), Jv.data(), &lvector, JΣJv.data(), &lresult);
472 throw_if_error("eval_hess_ψ_prod: CUTEST_cjprod-2", status);
473 Hv += JΣJv;
474}
476 rindexvec inner_idx, rindexvec outer_ptr,
477 rvec H_values) const {
478 // Compute the nonzero values
479 if (H_values.size() > 0) {
480 assert(x.size() == static_cast<length_t>(impl->nvar));
481 assert(y.size() == static_cast<length_t>(impl->ncon));
482 const auto *mult = y.data();
483 if (scale != 1) {
484 impl->work = y * (real_t(1) / scale);
485 mult = impl->work.data();
486 }
487 integer status;
488 // Sparse Hessian
489 if (sparse) {
490 assert(nnz_H >= 0);
491 assert(H_values.size() == static_cast<length_t>(nnz_H));
492 assert(H_work.size() == static_cast<length_t>(nnz_H));
493 assert(inner_idx.size() == static_cast<length_t>(nnz_H));
494 assert(outer_ptr.size() == static_cast<length_t>(impl->nvar + 1));
495 const integer nnz = nnz_H;
496 impl->funcs.csh(&status, &impl->nvar, &impl->ncon, x.data(),
497 y.data(), &nnz_H, &nnz, H_work.data(), H_col.data(),
498 H_row.data());
499 throw_if_error("eval_hess_L: CUTEST_csh", status);
500 auto t0 = std::chrono::steady_clock::now();
501 H_values = H_work(H_perm);
502 auto t1 = std::chrono::steady_clock::now();
503 std::cout << "Permutation of H took: "
504 << std::chrono::duration<double>{t1 - t0}.count() * 1e6
505 << " µs\n";
506 }
507 // Dense Hessian
508 else {
509 assert(H_values.size() == static_cast<length_t>(impl->nvar) *
510 static_cast<length_t>(impl->nvar));
511 impl->funcs.cdh(&status, &impl->nvar, &impl->ncon, x.data(), mult,
512 &impl->nvar, H_values.data());
513 throw_if_error("eval_hess_L: CUTEST_cdh", status);
514 }
515 if (scale != 1)
516 H_values *= scale;
517 }
518 // Compute sparsity pattern without values
519 else {
520 assert(nnz_H >= 0);
521 assert(inner_idx.size() == static_cast<length_t>(nnz_H));
522 assert(outer_ptr.size() == static_cast<length_t>(impl->nvar + 1));
523 integer status;
524 const integer nnz = nnz_H;
525 impl->funcs.cshp(&status, &impl->nvar, &nnz_H, &nnz, H_row.data(),
526 H_col.data());
527 throw_if_error("eval_hess_L: CUTEST_cshp", status);
528 std::iota(H_perm.begin(), H_perm.end(), index_t{0});
529 util::sort_triplets(H_row, H_col, H_perm);
530 util::convert_triplets_to_ccs<config_t>(H_row, H_col, inner_idx,
531 outer_ptr, 1);
532 }
533}
535 if (!sparse)
536 return -1;
537 if (nnz_H < 0) {
538 integer status;
539 impl->funcs.cdimsh(&status, &nnz_H);
540 throw_if_error("get_hess_L_num_nonzeros: CUTEST_cdimsh", status);
541 assert(nnz_H >= 0);
542 H_col.resize(nnz_H);
543 H_row.resize(nnz_H);
544 H_perm.resize(nnz_H);
545 H_work.resize(nnz_H);
546 }
547 return nnz_H;
548}
550 assert(x.size() == static_cast<length_t>(impl->nvar));
551 assert(grad_fx.size() == static_cast<length_t>(impl->nvar));
552 integer status;
553 real_t f;
554 logical grad = TRUE_;
555 impl->funcs.cofg(&status, &impl->nvar, x.data(), &f, grad_fx.data(), &grad);
556 throw_if_error("eval_f_grad_f: CUTEST_cofg", status);
557 return f;
558}
560 assert(x.size() == static_cast<length_t>(impl->nvar));
561 assert(g.size() == static_cast<length_t>(impl->ncon));
562 integer status;
563 real_t f;
564 impl->funcs.cfn(&status, &impl->nvar, &impl->ncon, x.data(), &f, g.data());
565 throw_if_error("eval_f_g: CUTEST_cfn", status);
566 return f;
567}
568void CUTEstProblem::eval_grad_L(crvec x, crvec y, rvec grad_L, rvec) const {
569 assert(x.size() == static_cast<length_t>(impl->nvar));
570 assert(y.size() == static_cast<length_t>(impl->ncon));
571 assert(grad_L.size() == static_cast<length_t>(impl->nvar));
572 integer status;
573 real_t L;
574 logical grad = TRUE_;
575 impl->funcs.clfg(&status, &impl->nvar, &impl->ncon, x.data(), y.data(), &L,
576 grad_L.data(), &grad);
577 throw_if_error("eval_f_g: CUTEST_clfg", status);
578}
579
581 using Status = CUTEstProblem::Report::Status;
582 switch (s) {
583 case Status::Success: return "Success";
584 case Status::AllocationError: return "AllocationError";
585 case Status::ArrayBoundError: return "ArrayBoundError";
586 case Status::EvaluationError: return "EvaluationError";
587 default:;
588 }
589 throw std::out_of_range(
590 "invalid value for pa::CUTEstProblem::Report::Status");
591}
592
593std::ostream &operator<<(std::ostream &os, CUTEstProblem::Report::Status s) {
594 return os << enum_name(s);
595}
596
597std::ostream &operator<<(std::ostream &os, const CUTEstProblem::Report &r) {
598 os << "CUTEst problem: " << r.name << "\r\n\n" //
599 << "Number of variables: " << r.nvar << "\r\n" //
600 << "Number of constraints: " << r.ncon << "\r\n\n" //
601 << "Status: " << r.status << " (" << +r.status << ")\r\n\n" //
602 << "Objective function evaluations: " << r.calls.objective
603 << "\r\n"
604 << "Objective function gradient evaluations: "
605 << r.calls.objective_grad << "\r\n"
606 << "Objective function Hessian evaluations: "
607 << r.calls.objective_hess << "\r\n"
608 << "Hessian times vector products: "
609 << r.calls.objective_hess << "\r\n\n";
610 if (r.ncon > 0) {
611 os << "Constraint function evaluations: "
612 << r.calls.constraints << "\r\n"
613 << "Constraint function gradients evaluations: "
614 << r.calls.constraints_grad << "\r\n"
615 << "Constraint function Hessian evaluations: "
616 << r.calls.constraints_hess << "\r\n\n";
617 }
618 return os << "Setup time: " << r.time_setup << "s\r\n"
619 << "Time since setup: " << r.time << "s";
620}
621
622} // namespace alpaqa
Implements common problem functions for minimization problems with box constraints.
cleanup_t cutest_terminate
Responsible for calling CUTEST_xterminate.
decltype(CUTEST_cigr) * cigr
decltype(CUTEST_ccfg) * ccfg
integer ncon
Number of constraints.
decltype(CUTEST_cjprod) * cjprod
std::shared_ptr< void > cleanup_t
cleanup_t cleanup_outsdif
Responsible for closing the OUTSDIF.d file.
decltype(CUTEST_cofg) * cofg
decltype(CUTEST_chprod) * chprod
CUTEstLoader(const char *so_fname, const char *outsdif_fname)
decltype(CUTEST_ccifg) * ccifg
integer iout
Fortran Unit Number for standard output.
integer get_report(doublereal *calls, doublereal *time)
std::string get_name()
integer funit
Fortran Unit Number for OUTSDIF.d file.
decltype(CUTEST_clfg) * clfg
cleanup_t load_outsdif(const char *outsdif_fname)
decltype(CUTEST_cdimsj) * cdimsj
cleanup_t cleanup(F &&func)
integer nvar
Number of decision variabls.
T * dlfun(const char *name)
std::shared_ptr< void > so_handle
dlopen handle to shared library
decltype(CUTEST_cdimsh) * cdimsh
Eigen::VectorX< logical > logical_vec
Pointers to loaded problem functions.
decltype(FUNDERSCORE(cutest_csjp)) * csjp
decltype(CUTEST_ccfsg) * ccfsg
void setup_problem(rvec x0, rvec y0, Box &C, Box &D)
logical_vec equatn
whether the constraint is an equality
decltype(CUTEST_cshp) * cshp
vec work2
work vectors
logical_vec linear
whether the constraint is linear
integer io_buffer
Fortran Unit Number for internal IO.
Wrapper for CUTEst problems loaded from an external shared library.
void eval_grad_gi(crvec x, index_t i, rvec grad_gi) const
real_t eval_f_g(crvec x, rvec g) const
CUTEstProblem(const char *so_fname, const char *outsdif_fname, bool sparse=false)
Load a CUTEst problem from the given shared library and OUTSDIF.d file.
void eval_grad_L(crvec x, crvec y, rvec grad_L, rvec work_n) const
Eigen::VectorX< int > J_row
Eigen::VectorX< int > J_col
Eigen::VectorX< int > H_col
void eval_hess_L(crvec x, crvec y, real_t scale, rindexvec inner_idx, rindexvec outer_ptr, rvec H_values) const
util::copyable_unique_ptr< class CUTEstLoader > impl
real_t eval_f_grad_f(crvec x, rvec grad_fx) const
void eval_grad_g_prod(crvec x, crvec y, rvec grad_gxy) const
void eval_hess_L_prod(crvec x, crvec y, real_t scale, crvec v, rvec Hv) const
Eigen::VectorX< int > H_row
void eval_grad_f(crvec x, rvec grad_fx) const
real_t eval_f(crvec x) const
void eval_g(crvec x, rvec gx) const
length_t get_jac_g_num_nonzeros() const
CUTEstProblem & operator=(const CUTEstProblem &)
void eval_jac_g(crvec x, rindexvec inner_idx, rindexvec outer_ptr, rvec J_values) const
void eval_hess_ψ_prod(crvec x, crvec y, crvec Σ, real_t scale, crvec v, rvec Hv) const
length_t get_hess_L_num_nonzeros() const
#define USING_ALPAQA_CONFIG(Conf)
Definition: config.hpp:54
#define CUTEST_csjp
#define LOAD_DL_FUNC(f)
std::ostream & operator<<(std::ostream &os, PANOCStopCrit s)
typename Conf::real_t real_t
Definition: config.hpp:63
typename Conf::rindexvec rindexvec
Definition: config.hpp:77
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
typename Conf::crvec crvec
Definition: config.hpp:68
typename Conf::vec vec
Definition: config.hpp:64
constexpr const char * enum_name(PANOCStopCrit s)
vec upperbound
Definition: box.hpp:26
vec lowerbound
Definition: box.hpp:25
The report generated by CUTEst.
int ncon
Number of constraints.
double time_setup
CPU time (in seconds) for CUTEST_csetup.
enum alpaqa::CUTEstProblem::Report::Status status
Exit status.
int nvar
Number of independent variables.
Status
Status returned by CUTEst.
struct alpaqa::CUTEstProblem::Report::@0 calls
Function call counters.
std::string name
Name of the problem.
double time
CPU time (in seconds) since the end of CUTEST_csetup.