PANOC-ALM  quadratic-penalty
Nonconvex constrained optimization
CUTEstLoader.cpp
Go to the documentation of this file.
2 
3 #include <cutest.h>
4 #include <dlfcn.h>
5 
6 #include <functional>
7 #include <iostream>
8 #include <stdexcept>
9 #include <string>
10 #include <string_view>
11 
12 using namespace std::string_literals;
13 
14 class CUTEstLoader {
15  private:
16  static void throw_error(std::string s, int code) {
17  throw std::runtime_error(s + " (" + std::to_string(code) + ")");
18  }
19  static void throw_if_error(std::string s, int code) {
20  if (code)
21  throw_error(s, code);
22  }
23 
24  public:
25  CUTEstLoader(const char *so_fname, const char *outsdif_fname) {
26  // Open the shared library
27  so_handle = dlopen(so_fname, RTLD_LAZY);
28  if (!so_handle)
29  throw std::runtime_error("Failed to open "s + so_fname);
30 
31  // Open the OUTSDIF.d file
32  integer ierr;
33  auto fptr_open = dlfun<decltype(FORTRAN_open)>("fortran_open_");
34  fptr_open(&funit, outsdif_fname, &ierr);
35  if (ierr)
36  throw std::runtime_error("Failed to open "s + outsdif_fname);
37 
38  // Get the dimensions of the problem
39  integer status;
40  auto fptr_cdimen = dlfun<decltype(CUTEST_cdimen)>("cutest_cdimen_");
41  fptr_cdimen(&status, &funit, &nvar, &ncon);
42  throw_if_error("Failed to call cutest_cdimen", status);
43 
44  // Set up the datastructures
45  x.resize(nvar);
46  x_l.resize(nvar);
47  x_u.resize(nvar);
48  y.resize(ncon);
49  c_l.resize(ncon);
50  c_u.resize(ncon);
51  equatn.resize(ncon);
52  linear.resize(ncon);
53  integer e_order = 0;
54  integer l_order = 0;
55  integer v_order = 0;
56 
57  // Constrained problem
58  if (ncon > 0) {
59  auto fptr_csetup =
60  dlfun<decltype(CUTEST_csetup)>("cutest_cint_csetup_");
61  fptr_csetup(&status, &funit, &iout, &io_buffer, &nvar, &ncon,
62  x.data(), x_l.data(), x_u.data(), y.data(), c_l.data(),
63  c_u.data(), (logical *)equatn.data(),
64  (logical *)linear.data(), &e_order, &l_order, &v_order);
65  throw_if_error("Failed to call cutest_csetup", status);
66  }
67  // Unconstrained problem
68  else {
69  auto fptr_usetup = dlfun<decltype(CUTEST_usetup)>("cutest_usetup_");
70  fptr_usetup(&status, &funit, &iout, &io_buffer, &nvar, x.data(),
71  x_l.data(), x_u.data());
72  throw_if_error("Failed to call cutest_usetup", status);
73  }
74  if (ncon > 0)
75  work.resize(std::max(nvar, ncon));
76 
77  eval_obj_p = ncon > 0 //
78  ? dlfun<void>("cutest_cfn_") //
79  : dlfun<void>("cutest_ufn_");
80  eval_obj_grad_p = ncon > 0 //
81  ? dlfun<void>("cutest_cint_cofg_") //
82  : dlfun<void>("cutest_ugr_");
83  eval_constr_p = ncon > 0 //
84  ? eval_obj_p //
85  : nullptr;
86  eval_constr_grad_prod_p = ncon > 0 //
87  ? dlfun<void>("cutest_cint_cjprod_") //
88  : nullptr;
89  eval_constr_i_grad_p = ncon > 0 //
90  ? dlfun<void>("cutest_cint_ccifg_") //
91  : nullptr;
92  eval_lagr_hess_prod_p = ncon > 0 //
93  ? dlfun<void>("cutest_cint_chprod_")
94  : dlfun<void>("cutest_cint_uhprod_");
95  eval_lagr_hess_p = ncon > 0 //
96  ? dlfun<void>("cutest_cdh_") //
97  : dlfun<void>("cutest_udh_");
98  }
99 
100  template <class F>
101  static inline constexpr auto call_as(void *funp) {
102  return (F *)funp;
103  }
104 
106  assert(x.size() == nvar);
107  assert(ncon > 0);
108  integer status;
109  doublereal f;
110  call_as<decltype(CUTEST_cfn)>(eval_obj_p)(&status, &nvar, &ncon,
111  x.data(), &f, work.data());
112  throw_if_error("Failed to call cutest_cfn", status);
113  return f;
114  }
115 
117  assert(x.size() == nvar);
118  assert(ncon == 0);
119  integer status;
120  doublereal f;
121  call_as<decltype(CUTEST_ufn)>(eval_obj_p)(&status, &nvar, x.data(), &f);
122  throw_if_error("Failed to call cutest_ufn", status);
123  return f;
124  }
125 
127  assert(x.size() == nvar);
128  assert(grad_f.size() == nvar);
129  assert(ncon > 0);
130  integer status;
131  logical grad = true;
132  call_as<decltype(CUTEST_cofg)>(eval_obj_grad_p)(
133  &status, &nvar, x.data(), work.data(), grad_f.data(), &grad);
134  throw_if_error("Failed to call cutest_cfn", status);
135  }
136 
138  assert(x.size() == nvar);
139  assert(grad_f.size() == nvar);
140  assert(ncon == 0);
141  integer status;
142  call_as<decltype(CUTEST_ugr)>(eval_obj_grad_p)(&status, &nvar, x.data(),
143  grad_f.data());
144  throw_if_error("Failed to call cutest_ugr", status);
145  }
146 
148  assert(x.size() == nvar);
149  assert(g.size() == ncon);
150  if (ncon == 0)
151  return;
152  integer status;
153  call_as<decltype(CUTEST_cfn)>(eval_constr_p)(
154  &status, &nvar, &ncon, x.data(), work.data(), g.data());
155  throw_if_error("Failed to call cutest_cfn", status);
156  }
157 
159  pa::rvec grad_g_v) const {
160  assert(x.size() == nvar);
161  assert(v.size() == ncon);
162  assert(grad_g_v.size() == nvar);
163  if (ncon == 0) {
164  grad_g_v.setZero();
165  return;
166  }
167  integer status;
168  logical gotJ = false;
169  logical jtrans = true;
170  call_as<decltype(CUTEST_cjprod)>(eval_constr_grad_prod_p)(
171  &status, &nvar, &ncon, &gotJ, &jtrans, x.data(), v.data(), &ncon,
172  grad_g_v.data(), &nvar);
173  throw_if_error("Failed to call cutest_cjprod", status);
174  }
175 
177  pa::rvec grad_gi) const {
178  assert(x.size() == nvar);
179  assert(grad_gi.size() == nvar);
180  if (ncon == 0) {
181  grad_gi.setZero();
182  return;
183  }
184  integer status;
185  integer icon = i + 1;
186  assert(icon >= 1);
187  assert(icon <= ncon);
188  pa::real_t ci;
189  logical grad = true;
190  call_as<decltype(CUTEST_ccifg)>(eval_constr_i_grad_p)(
191  &status, &nvar, &icon, x.data(), &ci, grad_gi.data(), &grad);
192  throw_if_error("Failed to call cutest_ccifg", status);
193  }
194 
196  pa::rvec Hv) const {
197  assert(x.size() == nvar);
198  assert(y.size() == ncon);
199  assert(v.rows() == nvar);
200  assert(Hv.rows() == nvar);
201  integer status;
202  logical gotH = false;
203  if (ncon == 0) {
204  call_as<decltype(CUTEST_uhprod)>(eval_lagr_hess_prod_p)(
205  &status, &nvar, &gotH, x.data(), v.data(), Hv.data());
206  throw_if_error("Failed to call cutest_uhprod", status);
207  } else {
208  call_as<decltype(CUTEST_chprod)>(eval_lagr_hess_prod_p)(
209  &status, &nvar, &ncon, &gotH, x.data(), y.data(),
210  const_cast<pa::real_t *>(v.data()), Hv.data());
211  // TODO: why is the VECTOR argument not const?
212  throw_if_error("Failed to call cutest_chprod", status);
213  }
214  }
215 
217  assert(x.size() == nvar);
218  assert(y.size() == ncon);
219  assert(H.rows() >= nvar);
220  assert(H.cols() >= nvar);
221  integer status;
222  integer ldH = H.rows();
223  if (ncon == 0) {
224  call_as<decltype(CUTEST_udh)>(eval_lagr_hess_p)(
225  &status, &nvar, x.data(), &ldH, H.data());
226  throw_if_error("Failed to call cutest_udh", status);
227  } else {
228  call_as<decltype(CUTEST_cdh)>(eval_lagr_hess_p)(
229  &status, &nvar, &ncon, x.data(), y.data(), &ldH, H.data());
230  throw_if_error("Failed to call cutest_cdh", status);
231  }
232  }
233 
234  unsigned count_box_constraints() const {
235  return std::count_if(x_l.data(), x_l.data() + nvar,
236  [](pa::real_t x) { return x > -CUTE_INF; }) +
237  std::count_if(x_u.data(), x_u.data() + nvar,
238  [](pa::real_t x) { return x < +CUTE_INF; });
239  }
240 
241  std::string get_name() {
242  std::string name;
243  name.resize(10);
244  integer status;
245  dlfun<decltype(CUTEST_probname)>("cutest_probname_")(&status,
246  name.data());
247  throw_if_error("Failed to call cutest_probname", status);
248  auto nspace = std::find_if(name.rbegin(), name.rend(),
249  [](char c) { return c != ' '; });
250  name.resize(name.size() - (nspace - name.rbegin()));
251  return name;
252  }
253 
254  integer get_report(doublereal *calls, doublereal *time) {
255  integer status;
256  auto fptr_report =
257  ncon > 0 ? dlfun<decltype(CUTEST_creport)>("cutest_creport_")
258  : dlfun<decltype(CUTEST_ureport)>("cutest_ureport_");
259  fptr_report(&status, calls, time);
260  return status;
261  }
262 
264  // Terminate CUTEst
265  if (ncon > 0) {
266  integer status;
267  auto fptr_cterminate =
268  dlfun<decltype(CUTEST_cterminate)>("cutest_cterminate_");
269  fptr_cterminate(&status);
270  throw_if_error("Failed to call cutest_cterminate", status);
271  } else {
272  integer status;
273  auto fptr_uterminate =
274  dlfun<decltype(CUTEST_uterminate)>("cutest_uterminate_");
275  fptr_uterminate(&status);
276  throw_if_error("Failed to call cutest_uterminate", status);
277  }
278 
279  // Close the OUTSDIF.d file
280  integer ierr;
281  auto fptr_close = dlfun<decltype(FORTRAN_close)>("fortran_close_");
282  fptr_close(&funit, &ierr);
283  throw_if_error("Failed to close OUTSDIF.d file", ierr);
284 
285  // Close the shared library
286  if (so_handle)
287  dlclose(so_handle);
288  }
289 
290  template <class T>
291  T *dlfun(const char *name) {
292  (void)dlerror();
293  auto res = (T *)dlsym(so_handle, name);
294  if (const char *error = dlerror(); error)
295  throw std::runtime_error(error);
296  return res;
297  }
298 
299  void *so_handle = nullptr;
300 
301  integer funit = 42;
302  integer iout = 6;
303  integer io_buffer = 11;
304 
305  integer nvar;
306  integer ncon;
307 
308  using logical_vec = Eigen::Matrix<logical, Eigen::Dynamic, 1>;
317  mutable pa::vec work;
318 
319  void *eval_obj_p = nullptr;
320  void *eval_obj_grad_p = nullptr;
321  void *eval_constr_p = nullptr;
322  void *eval_constr_grad_prod_p = nullptr;
323  void *eval_constr_i_grad_p = nullptr;
324  void *eval_lagr_hess_prod_p = nullptr;
325  void *eval_lagr_hess_p = nullptr;
326 };
327 
328 CUTEstProblem::CUTEstProblem(const char *so_fname, const char *outsdif_fname) {
329  implementation = std::make_unique<CUTEstLoader>(so_fname, outsdif_fname);
330  auto *l = implementation.get();
331  name = l->get_name();
332  number_box_constraints = l->count_box_constraints();
333  problem.n = l->nvar;
334  problem.m = l->ncon;
335  problem.C.lowerbound = std::move(l->x_l);
336  problem.C.upperbound = std::move(l->x_u);
337  problem.D.lowerbound = std::move(l->c_l);
338  problem.D.upperbound = std::move(l->c_u);
339  using namespace std::placeholders;
340  if (problem.m > 0) {
342  problem.grad_f = std::bind(
344  } else {
345  problem.f =
347  problem.grad_f = std::bind(
349  }
350  problem.g = std::bind(&CUTEstLoader::eval_constraints, l, _1, _2);
351  problem.grad_g_prod =
352  std::bind(&CUTEstLoader::eval_constraints_grad_prod, l, _1, _2, _3);
353  problem.grad_gi =
354  std::bind(&CUTEstLoader::eval_constraint_i_grad, l, _1, _2, _3);
355  problem.hess_L_prod =
356  std::bind(&CUTEstLoader::eval_lagr_hess_prod, l, _1, _2, _3, _4);
357  problem.hess_L = std::bind(&CUTEstLoader::eval_lagr_hess, l, _1, _2, _3);
358  x0 = std::move(l->x);
359  y0 = std::move(l->y);
360 }
361 
362 CUTEstProblem::CUTEstProblem(const std::string &so_fname,
363  const std::string &outsdif_fname)
364  : CUTEstProblem(so_fname.c_str(), outsdif_fname.c_str()) {}
365 
369 
371  doublereal calls[7];
372  doublereal time[2];
373  Report r;
374  using stat_t = decltype(r.status);
375  r.status = static_cast<stat_t>(implementation->get_report(calls, time));
376  r.name = implementation->get_name();
377  r.nvar = implementation->nvar;
378  r.ncon = implementation->ncon;
379  r.calls.objective = calls[0];
380  r.calls.objective_grad = calls[1];
381  r.calls.objective_hess = calls[2];
382  r.calls.hessian_times_vector = calls[3];
383  r.calls.constraints = implementation->ncon > 0 ? calls[4] : 0;
384  r.calls.constraints_grad = implementation->ncon > 0 ? calls[5] : 0;
385  r.calls.constraints_hess = implementation->ncon > 0 ? calls[6] : 0;
386  r.time_setup = time[0];
387  r.time = time[1];
388  return r;
389 }
390 
392  using Status = CUTEstProblem::Report::Status;
393  switch (s) {
394  case Status::Success: return "Success";
395  case Status::AllocationError: return "AllocationError";
396  case Status::ArrayBoundError: return "ArrayBoundError";
397  case Status::EvaluationError: return "EvaluationError";
398  }
399  throw std::out_of_range(
400  "invalid value for pa::CUTEstProblem::Report::Status");
401 }
402 
403 std::ostream &operator<<(std::ostream &os, CUTEstProblem::Report::Status s) {
404  return os << enum_name(s);
405 }
406 
407 std::ostream &operator<<(std::ostream &os, const CUTEstProblem::Report &r) {
408  os << "CUTEst problem: " << r.name << "\r\n\n" //
409  << "Number of variables: " << r.nvar << "\r\n" //
410  << "Number of constraints: " << r.ncon << "\r\n\n" //
411  << "Status: " << r.status << " (" << +r.status << ")\r\n\n" //
412  << "Objective function evaluations: " << r.calls.objective
413  << "\r\n"
414  << "Objective function gradient evaluations: "
415  << r.calls.objective_grad << "\r\n"
416  << "Objective function Hessian evaluations: "
417  << r.calls.objective_hess << "\r\n"
418  << "Hessian times vector products: "
419  << r.calls.objective_hess << "\r\n\n";
420  if (r.ncon > 0) {
421  os << "Constraint function evaluations: "
422  << r.calls.constraints << "\r\n"
423  << "Constraint function gradients evaluations: "
424  << r.calls.constraints_grad << "\r\n"
425  << "Constraint function Hessian evaluations: "
426  << r.calls.constraints_hess << "\r\n\n";
427  }
428  return os << "Setup time: " << r.time_setup << "s\r\n"
429  << "Time since setup: " << r.time << "s";
430 }
CUTEstLoader::eval_objective_grad_unconstrained
void eval_objective_grad_unconstrained(pa::crvec x, pa::rvec grad_f) const
Definition: CUTEstLoader.cpp:137
CUTEstLoader::x
pa::vec x
decision variable
Definition: CUTEstLoader.cpp:309
CUTEstProblem::Report::ncon
unsigned ncon
Number of constraints.
Definition: CUTEstLoader.hpp:39
CUTEstProblem::Report::time_setup
double time_setup
CPU time (in seconds) for CUTEST_csetup.
Definition: CUTEstLoader.hpp:75
enum_name
const char * enum_name(CUTEstProblem::Report::Status s)
Definition: CUTEstLoader.cpp:391
CUTEstLoader::call_as
static constexpr auto call_as(void *funp)
Definition: CUTEstLoader.cpp:101
pa::rvec
Eigen::Ref< vec > rvec
Default type for mutable references to vectors.
Definition: vec.hpp:16
CUTEstLoader::y
pa::vec y
lagrange multipliers
Definition: CUTEstLoader.cpp:312
panocpy.test.y
y
Definition: test.py:76
panocpy.test.name
string name
Definition: test.py:169
CUTEstLoader::nvar
integer nvar
Number of decision variabls.
Definition: CUTEstLoader.cpp:305
CUTEstProblem::Report::operator<<
std::ostream & operator<<(std::ostream &, const CUTEstProblem::Report &)
Definition: CUTEstLoader.cpp:407
pa::vec
realvec vec
Default type for vectors.
Definition: vec.hpp:14
pa::rmat
Eigen::Ref< mat > rmat
Default type for mutable references to matrices.
Definition: vec.hpp:22
CUTEstLoader.hpp
CUTEstLoader::get_name
std::string get_name()
Definition: CUTEstLoader.cpp:241
CUTEstLoader::throw_error
static void throw_error(std::string s, int code)
Definition: CUTEstLoader.cpp:16
CUTEstProblem::operator=
CUTEstProblem & operator=(CUTEstProblem &&)
CUTEstLoader::eval_objective_unconstrained
doublereal eval_objective_unconstrained(pa::crvec x) const
Definition: CUTEstLoader.cpp:116
CUTEstLoader::eval_constraints_grad_prod
void eval_constraints_grad_prod(pa::crvec x, pa::crvec v, pa::rvec grad_g_v) const
Definition: CUTEstLoader.cpp:158
panocpy.test.f
f
Definition: test.py:49
main.problem
problem
Definition: main.py:16
CUTEstProblem::Report::time
double time
CPU time (in seconds) since the end of CUTEST_csetup.
Definition: CUTEstLoader.hpp:77
panocpy.test.v
v
Definition: test.py:42
CUTEstProblem::~CUTEstProblem
~CUTEstProblem()
panocpy.test.x
x
Definition: test.py:40
CUTEstLoader::c_l
pa::vec c_l
lower bounds on constraints
Definition: CUTEstLoader.cpp:313
CUTEstLoader::work
pa::vec work
work vector
Definition: CUTEstLoader.cpp:317
bicycle-obstacle-avoidance-mpc.c
c
Definition: bicycle-obstacle-avoidance-mpc.py:124
CUTEstLoader::eval_objective_grad_constrained
void eval_objective_grad_constrained(pa::crvec x, pa::rvec grad_f) const
Definition: CUTEstLoader.cpp:126
CUTEstProblem::Report::status
enum CUTEstProblem::Report::Status status
Exit status.
CUTEstLoader::count_box_constraints
unsigned count_box_constraints() const
Definition: CUTEstLoader.cpp:234
CUTEstLoader::x_u
pa::vec x_u
upper bound on x
Definition: CUTEstLoader.cpp:311
panocpy.test.grad_f
grad_f
Definition: test.py:50
CUTEstLoader::ncon
integer ncon
Number of constraints.
Definition: CUTEstLoader.cpp:306
CUTEstLoader::eval_objective_constrained
doublereal eval_objective_constrained(pa::crvec x) const
Definition: CUTEstLoader.cpp:105
panocpy.test.x0
x0
Definition: test.py:68
CUTEstProblem::CUTEstProblem
CUTEstProblem(const char *so_fname, const char *outsdif_fname)
Load a CUTEst problem from the given shared library and OUTSDIF.d file.
Definition: CUTEstLoader.cpp:328
CUTEstLoader::CUTEstLoader
CUTEstLoader(const char *so_fname, const char *outsdif_fname)
Definition: CUTEstLoader.cpp:25
panocpy.test.grad_gi
grad_gi
Definition: test.py:53
pa::crvec
Eigen::Ref< const vec > crvec
Default type for immutable references to vectors.
Definition: vec.hpp:18
CUTEstLoader::x_l
pa::vec x_l
lower bound on x
Definition: CUTEstLoader.cpp:310
CUTEstProblem
Wrapper for CUTEst problems loaded from an external shared library.
Definition: CUTEstLoader.hpp:16
CUTEstLoader::eval_constraint_i_grad
void eval_constraint_i_grad(pa::crvec x, unsigned i, pa::rvec grad_gi) const
Definition: CUTEstLoader.cpp:176
CUTEstLoader::linear
logical_vec linear
whether the constraint is linear
Definition: CUTEstLoader.cpp:316
CUTEstProblem::get_report
Report get_report() const
Definition: CUTEstLoader.cpp:370
CUTEstLoader::equatn
logical_vec equatn
whether the constraint is an equality
Definition: CUTEstLoader.cpp:315
CUTEstLoader::throw_if_error
static void throw_if_error(std::string s, int code)
Definition: CUTEstLoader.cpp:19
CUTEstLoader::eval_lagr_hess_prod
void eval_lagr_hess_prod(pa::crvec x, pa::crvec y, pa::crvec v, pa::rvec Hv) const
Definition: CUTEstLoader.cpp:195
CUTEstLoader::~CUTEstLoader
~CUTEstLoader()
Definition: CUTEstLoader.cpp:263
CUTEstLoader::get_report
integer get_report(doublereal *calls, doublereal *time)
Definition: CUTEstLoader.cpp:254
panocpy.test.l
l
Definition: test.py:29
CUTEstLoader::logical_vec
Eigen::Matrix< logical, Eigen::Dynamic, 1 > logical_vec
Definition: CUTEstLoader.cpp:308
CUTEstProblem::Report::name
std::string name
Name of the problem.
Definition: CUTEstLoader.hpp:34
panocpy.test.g
g
Definition: test.py:51
CUTEstLoader::eval_lagr_hess
void eval_lagr_hess(pa::crvec x, pa::crvec y, pa::rmat H) const
Definition: CUTEstLoader.cpp:216
CUTEstProblem::Report
The report generated by CUTEst.
Definition: CUTEstLoader.hpp:32
panocpy.test.y0
y0
Definition: test.py:69
CUTEstLoader::eval_constraints
void eval_constraints(pa::crvec x, pa::rvec g) const
Definition: CUTEstLoader.cpp:147
CUTEstProblem::Report::calls
struct CUTEstProblem::Report::@0 calls
Function call counters.
CUTEstLoader::dlfun
T * dlfun(const char *name)
Definition: CUTEstLoader.cpp:291
CUTEstProblem::Report::Status
Status
Status returned by CUTEst.
Definition: CUTEstLoader.hpp:43
pa::real_t
double real_t
Default floating point type.
Definition: vec.hpp:8
CUTEstLoader
Definition: CUTEstLoader.cpp:14
CUTEstProblem::Report::nvar
unsigned nvar
Number of independent variables.
Definition: CUTEstLoader.hpp:37
main.H
H
Definition: main.py:8
CUTEstProblem::implementation
std::unique_ptr< class CUTEstLoader > implementation
Definition: CUTEstLoader.hpp:90
CUTEstLoader::c_u
pa::vec c_u
upper bounds on constraints
Definition: CUTEstLoader.cpp:314