alpaqa develop
Nonconvex constrained optimization
Loading...
Searching...
No Matches
dl-problem.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <alpaqa/dl-loader-export.h>
9#include <alpaqa/util/dl.hpp>
10
11#include <filesystem>
12#include <memory>
13#include <span>
14#include <stdexcept>
15#include <string>
16#include <string_view>
17#include <type_traits>
18
19namespace alpaqa::dl {
20
22
24 using dynamic_load_error::dynamic_load_error;
25};
26
28 public:
29 /// Unique type for calling an extra function that is a member function.
30 struct instance_t;
31
32 ExtraFuncs() = default;
33 ExtraFuncs(std::shared_ptr<function_dict_t> &&extra_funcs)
34 : extra_functions(std::move(extra_funcs)) {}
35
36 /// An associative array of additional functions exposed by the problem.
37 std::shared_ptr<function_dict_t> extra_functions;
38
39 template <class Signature>
40 requires std::is_function_v<Signature>
41 const std::function<Signature> &extra_func(const std::string &name) const {
42 if (!extra_functions)
43 throw std::out_of_range("DLProblem: no extra functions");
44 auto it = extra_functions->dict.find(name);
45 if (it == extra_functions->dict.end())
46 throw std::out_of_range("DLProblem: no extra function named \"" +
47 name + '"');
48 try {
50 return std::any_cast<const func_t &>(it->second).function;
51 } catch (const std::bad_any_cast &) {
52 throw std::logic_error(
53 "DLProblem: incorrect type for extra function \"" + name +
54 "\" (stored type: " + demangled_typename(it->second.type()) +
55 ')');
56 }
57 }
58
59 template <class Func>
60 struct FuncTag {};
61
62 template <class Ret, class... FArgs, class... Args>
63 decltype(auto)
64 call_extra_func_helper(const void *instance,
65 FuncTag<Ret(const instance_t *, FArgs...)>,
66 const std::string &name, Args &&...args) const {
67 return extra_func<Ret(const void *, FArgs...)>(name)(
68 instance, std::forward<Args>(args)...);
69 }
70
71 template <class Ret, class... FArgs, class... Args>
72 decltype(auto)
73 call_extra_func_helper(void *instance, FuncTag<Ret(instance_t *, FArgs...)>,
74 const std::string &name, Args &&...args) {
75 return extra_func<Ret(void *, FArgs...)>(name)(
76 instance, std::forward<Args>(args)...);
77 }
78
79 template <class Ret, class... FArgs, class... Args>
80 decltype(auto) call_extra_func_helper(const void *, FuncTag<Ret(FArgs...)>,
81 const std::string &name,
82 Args &&...args) const {
83 return extra_func<Ret(FArgs...)>(name)(std::forward<Args>(args)...);
84 }
85};
86
87/// Class that loads a problem using `dlopen`.
88///
89/// The shared library should export a C function with the name @c function_name
90/// that accepts a void pointer with user data, and returns a struct of type
91/// @ref alpaqa_problem_register_t that contains all data to represent the
92/// problem, as well as function pointers for all required operations.
93/// See @ref C++/DLProblem/main.cpp and
94/// @ref problems/sparse-logistic-regression.cpp for examples.
95///
96/// @note Copies are shallow, they all share the same problem instance, take
97/// that into account when using multiple threads.
98///
99/// @ingroup grp_Problems
100/// @see @ref TypeErasedProblem
101/// @see @ref alpaqa_problem_functions_t
102/// @see @ref alpaqa_problem_register_t
103class DL_LOADER_EXPORT DLProblem : public BoxConstrProblem<DefaultConfig> {
104 public:
107
108 /// Load a problem from a shared library.
109 DLProblem(
110 /// Filename of the shared library to load.
111 const std::filesystem::path &so_filename,
112 /// Name of the problem registration function.
113 /// Should have signature
114 /// `alpaqa_problem_register_t(alpaqa_register_arg_t user_param)`.
115 const std::string &function_name = "register_alpaqa_problem",
116 /// Pointer to custom user data to pass to the registration function.
117 alpaqa_register_arg_t user_param = {},
118 /// Flags passed to dlopen when loading the problem.
119 DynamicLoadFlags dl_flags = {});
120 /// Load a problem from a shared library.
121 DLProblem(
122 /// Filename of the shared library to load.
123 const std::filesystem::path &so_filename,
124 /// Name of the problem registration function.
125 /// Should have signature
126 /// `alpaqa_problem_register_t(alpaqa_register_arg_t user_param)`.
127 const std::string &function_name,
128 /// Custom user data to pass to the registration function.
129 std::any &user_param,
130 /// Flags passed to dlopen when loading the problem.
131 DynamicLoadFlags dl_flags = {});
132 /// Load a problem from a shared library.
133 DLProblem(
134 /// Filename of the shared library to load.
135 const std::filesystem::path &so_filename,
136 /// Name of the problem registration function.
137 /// Should have signature
138 /// `alpaqa_problem_register_t(alpaqa_register_arg_t user_param)`.
139 const std::string &function_name,
140 /// Custom string arguments to pass to the registration function.
141 std::span<std::string_view> user_param,
142 /// Flags passed to dlopen when loading the problem.
143 DynamicLoadFlags dl_flags = {});
144
145 private:
146 /// Path to the shared module file.
147 std::filesystem::path file;
148 /// Handle to the shared module defining the problem.
149 std::shared_ptr<void> handle;
150 /// Problem instance created by the registration function, including the
151 /// deleter to destroy it.
152 std::shared_ptr<void> instance;
153 /// Pointer to the struct of function pointers for evaluating the objective,
154 /// constraints, their gradients, etc.
155 problem_functions_t *functions = nullptr;
156 /// Dictionary of extra functions that were registered by the problem.
158
159 public:
160 // clang-format off
161 void eval_proj_diff_g(crvec z, rvec e) const;
162 void eval_proj_multipliers(rvec y, real_t M) const;
163 real_t eval_prox_grad_step(real_t γ, crvec x, crvec grad_ψ, rvec x̂, rvec p) const;
164 index_t eval_inactive_indices_res_lna(real_t γ, crvec x, crvec grad_ψ, rindexvec J) const;
165 real_t eval_f(crvec x) const;
166 void eval_grad_f(crvec x, rvec grad_fx) const;
167 void eval_g(crvec x, rvec gx) const;
168 void eval_grad_g_prod(crvec x, crvec y, rvec grad_gxy) const;
169 void eval_jac_g(crvec x, rvec J_values) const;
170 Sparsity get_jac_g_sparsity() const;
171 void eval_grad_gi(crvec x, index_t i, rvec grad_gi) const;
172 void eval_hess_L_prod(crvec x, crvec y, real_t scale, crvec v, rvec Hv) const;
173 void eval_hess_L(crvec x, crvec y, real_t scale, rvec H_values) const;
174 Sparsity get_hess_L_sparsity() const;
175 void eval_hess_ψ_prod(crvec x, crvec y, crvec Σ, real_t scale, crvec v, rvec Hv) const;
176 void eval_hess_ψ(crvec x, crvec y, crvec Σ, real_t scale, rvec H_values) const;
177 Sparsity get_hess_ψ_sparsity() const;
178 real_t eval_f_grad_f(crvec x, rvec grad_fx) const;
179 real_t eval_f_g(crvec x, rvec g) const;
180 void eval_grad_f_grad_g_prod(crvec x, crvec y, rvec grad_f, rvec grad_gxy) const;
181 void eval_grad_L(crvec x, crvec y, rvec grad_L, rvec work_n) const;
182 real_t eval_ψ(crvec x, crvec y, crvec Σ, rvec ŷ) const;
183 void eval_grad_ψ(crvec x, crvec y, crvec Σ, rvec grad_ψ, rvec work_n, rvec work_m) const;
184 real_t eval_ψ_grad_ψ(crvec x, crvec y, crvec Σ, rvec grad_ψ, rvec work_n, rvec work_m) const;
185 std::string get_name() const;
186
187 [[nodiscard]] bool provides_eval_f() const;
188 [[nodiscard]] bool provides_eval_grad_f() const;
189 [[nodiscard]] bool provides_eval_g() const;
190 [[nodiscard]] bool provides_eval_grad_g_prod() const;
191 [[nodiscard]] bool provides_eval_jac_g() const;
192 [[nodiscard]] bool provides_get_jac_g_sparsity() const;
193 [[nodiscard]] bool provides_eval_grad_gi() const;
194 [[nodiscard]] bool provides_eval_hess_L_prod() const;
195 [[nodiscard]] bool provides_eval_hess_L() const;
196 [[nodiscard]] bool provides_get_hess_L_sparsity() const;
197 [[nodiscard]] bool provides_eval_hess_ψ_prod() const;
198 [[nodiscard]] bool provides_eval_hess_ψ() const;
199 [[nodiscard]] bool provides_get_hess_ψ_sparsity() const;
200 [[nodiscard]] bool provides_eval_f_grad_f() const;
201 [[nodiscard]] bool provides_eval_f_g() const;
202 [[nodiscard]] bool provides_eval_grad_f_grad_g_prod() const;
203 [[nodiscard]] bool provides_eval_grad_L() const;
204 [[nodiscard]] bool provides_eval_ψ() const;
205 [[nodiscard]] bool provides_eval_grad_ψ() const;
206 [[nodiscard]] bool provides_eval_ψ_grad_ψ() const;
207 [[nodiscard]] bool provides_get_box_C() const;
208 [[nodiscard]] bool provides_get_box_D() const;
209 [[nodiscard]] bool provides_eval_inactive_indices_res_lna() const;
210 // clang-format on
211
212 using instance_t = ExtraFuncs::instance_t;
213
214 template <class Signature, class... Args>
215 decltype(auto) call_extra_func(const std::string &name,
216 Args &&...args) const {
217 return call_extra_func_helper(instance.get(),
219 std::forward<Args>(args)...);
220 }
221
222 template <class Signature, class... Args>
223 decltype(auto) call_extra_func(const std::string &name, Args &&...args) {
224 return extra_funcs.call_extra_func_helper(
225 instance.get(), ExtraFuncs::FuncTag<Signature>{}, name,
226 std::forward<Args>(args)...);
227 }
228};
229
230#if ALPAQA_WITH_OCP
231
232/// Class that loads an optimal control problem using `dlopen`.
233///
234/// The shared library should export a C function with the name @c function_name
235/// that accepts a void pointer with user data, and returns a struct of type
236/// @ref alpaqa_control_problem_register_t that contains all data to represent
237/// the problem, as well as function pointers for all required operations.
238///
239/// @note Copies are shallow, they all share the same problem instance, take
240/// that into account when using multiple threads.
241///
242/// @ingroup grp_Problems
243/// @see @ref TypeErasedControlProblem
245 public:
248
249 /// Load a problem from a shared library.
251 /// Filename of the shared library to load.
252 const std::filesystem::path &so_filename,
253 /// Name of the problem registration function.
254 /// Should have signature
255 /// `alpaqa_control_problem_register_t(alpaqa_register_arg_t user_param)`.
256 const std::string &function_name = "register_alpaqa_control_problem",
257 /// Pointer to custom user data to pass to the registration function.
258 alpaqa_register_arg_t user_param = {},
259 /// Flags passed to dlopen when loading the problem.
260 DynamicLoadFlags dl_flags = {});
261
262 private:
263 /// Handle to the shared module defining the problem.
264 std::shared_ptr<void> handle;
265 /// Problem instance created by the registration function, including the
266 /// deleter to destroy it.
267 std::shared_ptr<void> instance;
268 /// Pointer to the struct of function pointers for evaluating the objective,
269 /// constraints, their gradients, etc.
270 control_problem_functions_t *functions = nullptr;
271 /// Dictionary of extra functions that were registered by the problem.
273
274 public:
275 length_t get_N() const { return functions->N; }
276 length_t get_nx() const { return functions->nx; }
277 length_t get_nu() const { return functions->nu; }
278 length_t get_nh() const { return functions->nh; }
279 length_t get_nh_N() const { return functions->nh_N; }
280 length_t get_nc() const { return functions->nc; }
281 length_t get_nc_N() const { return functions->nc_N; }
282
283 void check() const {} // TODO
284
285 // clang-format off
286 void get_U(Box &U) const;
287 void get_D(Box &D) const;
288 void get_D_N(Box &D) const;
289 void get_x_init(rvec x_init) const;
290 void eval_f(index_t timestep, crvec x, crvec u, rvec fxu) const;
291 void eval_jac_f(index_t timestep, crvec x, crvec u, rmat J_fxu) const;
292 void eval_grad_f_prod(index_t timestep, crvec x, crvec u, crvec p, rvec grad_fxu_p) const;
293 void eval_h(index_t timestep, crvec x, crvec u, rvec h) const;
294 void eval_h_N(crvec x, rvec h) const;
295 [[nodiscard]] real_t eval_l(index_t timestep, crvec h) const;
296 [[nodiscard]] real_t eval_l_N(crvec h) const;
297 void eval_qr(index_t timestep, crvec xu, crvec h, rvec qr) const;
298 void eval_q_N(crvec x, crvec h, rvec q) const;
299 void eval_add_Q(index_t timestep, crvec xu, crvec h, rmat Q) const;
300 void eval_add_Q_N(crvec x, crvec h, rmat Q) const;
301 void eval_add_R_masked(index_t timestep, crvec xu, crvec h, crindexvec mask, rmat R, rvec work) const;
302 void eval_add_S_masked(index_t timestep, crvec xu, crvec h, crindexvec mask, rmat S, rvec work) const;
303 void eval_add_R_prod_masked(index_t timestep, crvec xu, crvec h, crindexvec mask_J, crindexvec mask_K, crvec v, rvec out, rvec work) const;
304 void eval_add_S_prod_masked(index_t timestep, crvec xu, crvec h, crindexvec mask_K, crvec v, rvec out, rvec work) const;
305 [[nodiscard]] length_t get_R_work_size() const;
306 [[nodiscard]] length_t get_S_work_size() const;
307 void eval_constr(index_t timestep, crvec x, rvec c) const;
308 void eval_constr_N(crvec x, rvec c) const;
309 void eval_grad_constr_prod(index_t timestep, crvec x, crvec p, rvec grad_cx_p) const;
310 void eval_grad_constr_prod_N(crvec x, crvec p, rvec grad_cx_p) const;
311 void eval_add_gn_hess_constr(index_t timestep, crvec x, crvec M, rmat out) const;
312 void eval_add_gn_hess_constr_N(crvec x, crvec M, rmat out) const;
313
314 [[nodiscard]] bool provides_get_D() const;
315 [[nodiscard]] bool provides_get_D_N() const;
316 [[nodiscard]] bool provides_eval_add_Q_N() const;
317 [[nodiscard]] bool provides_eval_add_R_prod_masked() const;
318 [[nodiscard]] bool provides_eval_add_S_prod_masked() const;
319 [[nodiscard]] bool provides_get_R_work_size() const;
320 [[nodiscard]] bool provides_get_S_work_size() const;
321 [[nodiscard]] bool provides_eval_constr() const;
322 [[nodiscard]] bool provides_eval_constr_N() const;
323 [[nodiscard]] bool provides_eval_grad_constr_prod() const;
324 [[nodiscard]] bool provides_eval_grad_constr_prod_N() const;
325 [[nodiscard]] bool provides_eval_add_gn_hess_constr() const;
326 [[nodiscard]] bool provides_eval_add_gn_hess_constr_N() const;
327 // clang-format on
328
329 using instance_t = ExtraFuncs::instance_t;
330
331 template <class Signature, class... Args>
332 decltype(auto) call_extra_func(const std::string &name,
333 Args &&...args) const {
334 return extra_funcs.call_extra_func_helper(
335 instance.get(), ExtraFuncs::FuncTag<Signature>{}, name,
336 std::forward<Args>(args)...);
337 }
338
339 template <class Signature, class... Args>
340 decltype(auto) call_extra_func(const std::string &name, Args &&...args) {
341 return extra_funcs.call_extra_func_helper(
342 instance.get(), ExtraFuncs::FuncTag<Signature>{}, name,
343 std::forward<Args>(args)...);
344 }
345};
346
347#endif
348
349} // namespace alpaqa::dl
Implements common problem functions for minimization problems with box constraints.
Class that loads an optimal control problem using dlopen.
ExtraFuncs::instance_t instance_t
std::shared_ptr< void > instance
Problem instance created by the registration function, including the deleter to destroy it.
decltype(auto) call_extra_func(const std::string &name, Args &&...args) const
decltype(auto) call_extra_func(const std::string &name, Args &&...args)
std::shared_ptr< void > handle
Handle to the shared module defining the problem.
ExtraFuncs extra_funcs
Dictionary of extra functions that were registered by the problem.
Class that loads a problem using dlopen.
ExtraFuncs::instance_t instance_t
std::shared_ptr< void > instance
Problem instance created by the registration function, including the deleter to destroy it.
decltype(auto) call_extra_func(const std::string &name, Args &&...args) const
std::filesystem::path file
Path to the shared module file.
decltype(auto) call_extra_func(const std::string &name, Args &&...args)
std::shared_ptr< void > handle
Handle to the shared module defining the problem.
ExtraFuncs extra_funcs
Dictionary of extra functions that were registered by the problem.
decltype(auto) call_extra_func_helper(const void *instance, FuncTag< Ret(const instance_t *, FArgs...)>, const std::string &name, Args &&...args) const
decltype(auto) call_extra_func_helper(void *instance, FuncTag< Ret(instance_t *, FArgs...)>, const std::string &name, Args &&...args)
const std::function< Signature > & extra_func(const std::string &name) const
std::shared_ptr< function_dict_t > extra_functions
An associative array of additional functions exposed by the problem.
ExtraFuncs(std::shared_ptr< function_dict_t > &&extra_funcs)
decltype(auto) call_extra_func_helper(const void *, FuncTag< Ret(FArgs...)>, const std::string &name, Args &&...args) const
#define USING_ALPAQA_CONFIG(Conf)
Definition config.hpp:77
std::string demangled_typename(const std::type_info &t)
Get the pretty name of the given type as a string.
User-provided argument that is passed to the problem registration functions.
Definition dl-problem.h:65
std::function< Signature > function
Definition dl-problem.h:700
Custom type for which we can export the RTTI to support std::any across shared library boundaries whe...
Definition dl-problem.h:699
typename Conf::rmat rmat
Definition config.hpp:96
typename Conf::real_t real_t
Definition config.hpp:86
typename Conf::rindexvec rindexvec
Definition config.hpp:106
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
typename Conf::crvec crvec
Definition config.hpp:92
typename Conf::crindexvec crindexvec
Definition config.hpp:107
Result(const MemberGetter &) func_t
Flags to be passed to dlopen.
Definition dl-flags.hpp:8
Double-precision double configuration.
Definition config.hpp:176
Stores any of the supported sparsity patterns.
Definition sparsity.hpp:106
Failed to load a DLL or SO file, or failed to access a function in it.
Definition dl.hpp:12
C API providing function pointers to problem functions.
Definition dl-problem.h:210