Nonconvex constrained optimization
Loading...
Searching...
No Matches
problem-loader.cpp
Go to the documentation of this file.
1#include <alpaqa/export.h>
6#include <guanaqo/dl-flags.hpp>
7#if ALPAQA_WITH_DL
9#endif
10#if ALPAQA_WITH_CASADI
12#endif
13#ifdef ALPAQA_WITH_CUTEST
15#endif
16
17#include <filesystem>
18#include <optional>
19#include <span>
20#include <stdexcept>
21#include <string>
22#ifndef ALPAQA_WITHOUT_MUTEX
23#include <mutex>
24#endif
25namespace fs = std::filesystem;
26
28
29namespace {
30
33
34[[maybe_unused]]
35std::string get_reg_name_option(std::span<const std::string_view> prob_opts) {
36 std::string name = "register_alpaqa_problem";
37 std::string_view name_key = "register=";
38 auto name_it = std::find_if(
39 prob_opts.rbegin(), prob_opts.rend(),
40 [&](std::string_view opt) { return opt.starts_with(name_key); });
41 if (name_it != prob_opts.rend())
42 name = name_it->substr(name_key.size());
43 return name;
44}
45
46[[maybe_unused]]
47guanaqo::DynamicLoadFlags get_dl_flags(alpaqa::Options &opts) {
48 guanaqo::DynamicLoadFlags flags;
49 set_params(flags, "dl_flags", opts);
50 return flags;
51}
52
53[[maybe_unused]]
55 const auto n = problem.problem.get_num_variables(),
56 m = problem.problem.get_num_constraints();
58 set_params(x0, "x0", opts);
59 if (x0.value)
60 problem.initial_guess_x = std::move(*x0.value);
61 set_params(y0, "mul_g0", opts);
62 if (y0.value)
63 problem.initial_guess_y = std::move(*y0.value);
64 set_params(w0, "mul_x0", opts);
65 if (w0.value)
66 problem.initial_guess_w = std::move(*w0.value);
67}
68
70 const auto n = C.lower.size();
71 cnt.lb = 0;
72 cnt.ub = 0;
73 cnt.lbub = 0;
74 cnt.eq = 0;
75 for (index_t i = 0; i < n; ++i) {
76 bool lb = C.lower(i) > -alpaqa::inf<config_t>;
77 bool ub = C.upper(i) < +alpaqa::inf<config_t>;
78 bool eq = C.lower(i) == C.upper(i);
79 if (eq)
80 ++cnt.eq;
81 else if (lb && ub)
82 ++cnt.lbub;
83 else if (lb)
84 ++cnt.lb;
85 else if (ub)
86 ++cnt.ub;
87 }
88}
89
90[[maybe_unused]]
106
107#if ALPAQA_WITH_DL
108LoadedProblem load_dl_problem(const fs::path &full_path,
109 std::span<std::string_view> prob_opts,
110 alpaqa::Options &opts) {
111 using TEProblem = alpaqa::TypeErasedProblem<config_t>;
112 using DLProblem = alpaqa::dl::DLProblem;
114 auto register_name = get_reg_name_option(prob_opts);
115 auto flags = get_dl_flags(opts);
116 LoadedProblem problem{
117 .problem = TEProblem::make<CntProblem>(std::in_place, full_path,
118 register_name, prob_opts, flags),
119 .abs_path = fs::absolute(full_path),
120 .path = full_path,
121 };
122 auto &cnt_problem = problem.problem.as<CntProblem>();
123 problem.name = cnt_problem.problem.get_name();
124 problem.evaluations = cnt_problem.evaluations;
125 load_initial_guess(opts, problem);
126 count_problem(problem);
127 return problem;
128}
129#endif
130
131#if ALPAQA_WITH_CASADI
132template <bool = true>
133LoadedProblem load_cs_problem(const fs::path &full_path,
134 std::span<std::string_view> prob_opts,
135 alpaqa::Options &opts) {
136#ifndef ALPAQA_WITHOUT_MUTEX
137 static std::mutex mtx;
138 std::unique_lock lck{mtx};
139#endif
140 using TEProblem = alpaqa::TypeErasedProblem<config_t>;
141 using CsProblem = alpaqa::CasADiProblem<config_t>;
142 using CntProblem = alpaqa::ProblemWithCounters<CsProblem>;
143 auto flags = get_dl_flags(opts);
144 LoadedProblem problem{
145 .problem = TEProblem::make<CntProblem>(
146 std::in_place, full_path.string().c_str(), flags),
147 .abs_path = fs::absolute(full_path),
148 .path = full_path,
149 };
150#ifndef ALPAQA_WITHOUT_MUTEX
151 lck.unlock();
152#endif
153 auto &cnt_problem = problem.problem.as<CntProblem>();
154 auto &cs_problem = cnt_problem.problem;
155 problem.name = cs_problem.get_name();
156 problem.evaluations = cnt_problem.evaluations;
157 auto param_size = cs_problem.param.size();
158 alpaqa::params::set_params(cs_problem.param, "param", prob_opts);
159 if (cs_problem.param.size() != param_size)
160 throw alpaqa::params::invalid_param(
161 "Incorrect problem parameter size (expected " +
162 std::to_string(param_size) + ", but got " +
163 std::to_string(cs_problem.param.size()) + ")");
164 load_initial_guess(opts, problem);
165 count_problem(problem);
166 return problem;
167}
168#endif
169
170#ifdef ALPAQA_WITH_CUTEST
171template <bool = true>
172LoadedProblem load_cu_problem(const fs::path &full_path,
173 std::span<std::string_view> prob_opts,
174 alpaqa::Options &opts) {
175 std::string outsdif_path;
176 alpaqa::params::set_params(outsdif_path, "outsdif", prob_opts);
177 bool sparse = false;
178 alpaqa::params::set_params(sparse, "sparse", prob_opts);
179#ifndef ALPAQA_WITHOUT_MUTEX
180 static std::mutex mtx;
181 std::unique_lock lck{mtx};
182#endif
183 using TEProblem = alpaqa::TypeErasedProblem<config_t>;
184 using CuProblem = alpaqa::CUTEstProblem;
185 using CntProblem = alpaqa::ProblemWithCounters<CuProblem>;
186 auto flags = get_dl_flags(opts);
187 LoadedProblem problem{
188 .problem =
189 TEProblem::make<CntProblem>(std::in_place, full_path.c_str(),
190 outsdif_path.c_str(), sparse, flags),
191 .abs_path = fs::absolute(full_path),
192 .path = full_path,
193 };
194#ifndef ALPAQA_WITHOUT_MUTEX
195 lck.unlock();
196#endif
197 auto &cnt_problem = problem.problem.as<CntProblem>();
198 auto &cu_problem = cnt_problem.problem;
199 problem.name = cu_problem.get_name();
200 problem.evaluations = cnt_problem.evaluations;
201 problem.initial_guess_x = std::move(cu_problem.x0);
202 problem.initial_guess_y = std::move(cu_problem.y0);
203 load_initial_guess(opts, problem);
204 count_problem(problem);
205 return problem;
206}
207#endif
208
209} // namespace
210
211namespace alpaqa {
212
213LoadedProblem load_problem(std::string_view type, const fs::path &file,
214 Options &opts) {
216 // Isolate problem-specific options
217 std::vector<std::string_view> prob_opts;
218 std::string_view prob_prefix = "problem.";
219 auto options = opts.options();
220 auto used = opts.used();
221 for (auto opt = options.begin(); opt != options.end(); ++opt) {
222 if (opt->starts_with(prob_prefix)) {
223 prob_opts.push_back(opt->substr(prob_prefix.size()));
224 ++used.begin()[opt - options.begin()];
225 }
226 }
227 // Load problem
228 if (type == "dl" || type.empty()) {
229#if ALPAQA_WITH_DL
230 return load_dl_problem(file, prob_opts, opts);
231#else
232 (void)file;
233 throw std::logic_error("This version of alpaqa was compiled without "
234 "support for dynamic problem loading");
235#endif
236 } else if (type == "cs") {
237#if ALPAQA_WITH_CASADI
238 if constexpr (std::is_same_v<config_t, alpaqa::EigenConfigd>)
239 return load_cs_problem(file, prob_opts, opts);
240 else
241 throw std::logic_error("CasADi only supports double precision.");
242#else
243 throw std::logic_error(
244 "This version of alpaqa was compiled without CasADi support");
245#endif
246 } else if (type == "cu") {
247#ifdef ALPAQA_WITH_CUTEST
248 if constexpr (std::is_same_v<config_t, alpaqa::EigenConfigd>)
249 return load_cu_problem(file, prob_opts, opts);
250 else
251 throw std::logic_error("CUTEst only supports double precision.");
252#else
253 throw std::logic_error(
254 "This version of alpaqa was compiled without CUTEst support");
255#endif
256 }
257 throw std::invalid_argument("Unknown problem type '" + std::string(type) +
258 "'");
259}
260
261void print_problem_description(std::ostream &os, LoadedProblem &problem,
262 bool show_funcs) {
263 os << "Loaded problem \"" << problem.name << "\"\n"
264 << "Number of variables: " << problem.problem.get_num_variables()
265 << "\n"
266 << "Number of constraints: " << problem.problem.get_num_constraints()
267 << "\n";
268 if (problem.nnz_jac_g)
269 os << "Nonzeros in Jg: " << *problem.nnz_jac_g << "\n";
270 if (problem.nnz_hess_L)
271 os << "Nonzeros in ∇²L: " << *problem.nnz_hess_L << "\n";
272 if (problem.nnz_hess_ψ)
273 os << "Nonzeros in ∇²ψ: " << *problem.nnz_hess_ψ << "\n";
274 if (problem.box_constr_count)
275 os << "Box constraints:" //
276 << "\n Fixed variables: " << problem.box_constr_count->eq
277 << "\n Bilateral: " << problem.box_constr_count->lbub
278 << "\n Lower bound only: " << problem.box_constr_count->lb
279 << "\n Upper bound only: " << problem.box_constr_count->ub
280 << "\n";
281 if (problem.general_constr_count)
282 os << "General constraints:" //
283 << "\n Equality: " << problem.general_constr_count->eq
284 << "\n Bilateral: " << problem.general_constr_count->lbub
285 << "\n Lower bound only: " << problem.general_constr_count->lb
286 << "\n Upper bound only: " << problem.general_constr_count->ub
287 << "\n";
288 if (show_funcs) {
289 os << "Provided functions:\n";
291 }
292}
293
294} // namespace alpaqa
std::span< unsigned > used()
Definition options.hpp:63
std::span< const std::string_view > options() const
Definition options.hpp:60
The main polymorphic minimization problem interface.
Sparsity get_lagrangian_hessian_sparsity() const
[Optional] Function that returns (a view of) the sparsity pattern of the Hessian of the Lagrangian.
const Box & get_variable_bounds() const
[Optional] Get the rectangular constraint set of the decision variables, .
length_t get_num_constraints() const
[Required] Number of constraints.
bool provides_get_lagrangian_hessian_sparsity() const
Returns true if the problem provides an implementation of get_lagrangian_hessian_sparsity.
bool provides_get_constraints_jacobian_sparsity() const
Returns true if the problem provides an implementation of get_constraints_jacobian_sparsity.
length_t get_num_variables() const
[Required] Number of decision variables.
Sparsity get_constraints_jacobian_sparsity() const
[Optional] Function that returns (a view of) the sparsity pattern of the Jacobian of the constraints.
const Box & get_general_bounds() const
[Optional] Get the rectangular constraint set of the general constraint function, .
bool provides_get_variable_bounds() const
Returns true if the problem provides an implementation of get_variable_bounds.
bool provides_get_general_bounds() const
Returns true if the problem provides an implementation of get_general_bounds.
bool provides_get_augmented_lagrangian_hessian_sparsity() const
Returns true if the problem provides an implementation of get_augmented_lagrangian_hessian_sparsity.
Sparsity get_augmented_lagrangian_hessian_sparsity() const
[Optional] Function that returns (a view of) the sparsity pattern of the Hessian of the augmented Lag...
Class that loads a problem using dlopen.
#define USING_ALPAQA_CONFIG(Conf)
Definition config.hpp:77
void print_provided_functions(std::ostream &os, const TypeErasedProblem< Conf > &problem)
void set_params(T &t, std::string_view prefix, std::span< const std::string_view > options, std::optional< std::span< unsigned > > used=std::nullopt)
Overwrites t based on the options that start with prefix.
Definition params.hpp:49
vec initial_guess_y
Multipliers g.
PROBLEM_LOADER_EXPORT LoadedProblem load_problem(std::string_view type, const fs::path &file, Options &opts)
vec initial_guess_w
Multipliers bounds.
std::optional< length_t > nnz_hess_L
std::optional< ConstrCount > general_constr_count
EigenConfigd DefaultConfig
Definition config.hpp:31
std::optional< length_t > nnz_hess_ψ
alpaqa::TypeErasedProblem< config_t > problem
constexpr const auto inf
Definition config.hpp:112
std::optional< ConstrCount > box_constr_count
std::optional< length_t > nnz_jac_g
PROBLEM_LOADER_EXPORT void print_problem_description(std::ostream &os, LoadedProblem &problem, bool show_funcs=true)
guanaqo::DynamicLoadFlags get_dl_flags(alpaqa::Options &opts)
std::string get_reg_name_option(std::span< const std::string_view > prob_opts)
void load_initial_guess(alpaqa::Options &opts, LoadedProblem &problem)
alpaqa::TypeErasedProblem< config_t > problem
void count_constr(alpaqa::ConstrCount &cnt, const alpaqa::Box< config_t > &C)
std::shared_ptr< alpaqa::EvalCounter > evaluations
length_t ub
Number of variables with only upper bound.
length_t eq
Number of variables with equal bounds.
length_t lb
Number of variables with only lower bound.
length_t lbub
Number of variables with both bounds.
Problem wrapper that keeps track of the number of evaluations and the run time of each function.