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 <mutex>
19#include <optional>
20#include <span>
21#include <stdexcept>
22#include <string>
23namespace fs = std::filesystem;
24
26
27namespace {
28
31
32std::string get_reg_name_option(std::span<const std::string_view> prob_opts) {
33 std::string name = "register_alpaqa_problem";
34 std::string_view name_key = "register=";
35 auto name_it = std::find_if(
36 prob_opts.rbegin(), prob_opts.rend(),
37 [&](std::string_view opt) { return opt.starts_with(name_key); });
38 if (name_it != prob_opts.rend())
39 name = name_it->substr(name_key.size());
40 return name;
41}
42
43guanaqo::DynamicLoadFlags get_dl_flags(alpaqa::Options &opts) {
44 guanaqo::DynamicLoadFlags flags;
45 set_params(flags, "dl_flags", opts);
46 return flags;
47}
48
50 const auto n = problem.problem.get_num_variables(),
51 m = problem.problem.get_num_constraints();
53 set_params(x0, "x0", opts);
54 if (x0.value)
55 problem.initial_guess_x = std::move(*x0.value);
56 set_params(y0, "mul_g0", opts);
57 if (y0.value)
58 problem.initial_guess_y = std::move(*y0.value);
59 set_params(w0, "mul_x0", opts);
60 if (w0.value)
61 problem.initial_guess_w = std::move(*w0.value);
62}
63
65 const auto n = C.lower.size();
66 cnt.lb = 0;
67 cnt.ub = 0;
68 cnt.lbub = 0;
69 cnt.eq = 0;
70 for (index_t i = 0; i < n; ++i) {
71 bool lb = C.lower(i) > -alpaqa::inf<config_t>;
72 bool ub = C.upper(i) < +alpaqa::inf<config_t>;
73 bool eq = C.lower(i) == C.upper(i);
74 if (eq)
75 ++cnt.eq;
76 else if (lb && ub)
77 ++cnt.lbub;
78 else if (lb)
79 ++cnt.lb;
80 else if (ub)
81 ++cnt.ub;
82 }
83}
84
100
101#if ALPAQA_WITH_DL
102LoadedProblem load_dl_problem(const fs::path &full_path,
103 std::span<std::string_view> prob_opts,
104 alpaqa::Options &opts) {
105 using TEProblem = alpaqa::TypeErasedProblem<config_t>;
106 using DLProblem = alpaqa::dl::DLProblem;
108 auto register_name = get_reg_name_option(prob_opts);
109 auto flags = get_dl_flags(opts);
110 LoadedProblem problem{
111 .problem = TEProblem::make<CntProblem>(std::in_place, full_path,
112 register_name, prob_opts, flags),
113 .abs_path = fs::absolute(full_path),
114 .path = full_path,
115 };
116 auto &cnt_problem = problem.problem.as<CntProblem>();
117 problem.name = cnt_problem.problem.get_name();
118 problem.evaluations = cnt_problem.evaluations;
119 load_initial_guess(opts, problem);
120 count_problem(problem);
121 return problem;
122}
123#endif
124
125#if ALPAQA_WITH_CASADI
126template <bool = true>
127LoadedProblem load_cs_problem(const fs::path &full_path,
128 std::span<std::string_view> prob_opts,
129 alpaqa::Options &opts) {
130 static std::mutex mtx;
131 std::unique_lock lck{mtx};
132 using TEProblem = alpaqa::TypeErasedProblem<config_t>;
133 using CsProblem = alpaqa::CasADiProblem<config_t>;
134 using CntProblem = alpaqa::ProblemWithCounters<CsProblem>;
135 auto flags = get_dl_flags(opts);
136 LoadedProblem problem{
137 .problem = TEProblem::make<CntProblem>(
138 std::in_place, full_path.string().c_str(), flags),
139 .abs_path = fs::absolute(full_path),
140 .path = full_path,
141 };
142 lck.unlock();
143 auto &cnt_problem = problem.problem.as<CntProblem>();
144 auto &cs_problem = cnt_problem.problem;
145 problem.name = cs_problem.get_name();
146 problem.evaluations = cnt_problem.evaluations;
147 auto param_size = cs_problem.param.size();
148 alpaqa::params::set_params(cs_problem.param, "param", prob_opts);
149 if (cs_problem.param.size() != param_size)
150 throw alpaqa::params::invalid_param(
151 "Incorrect problem parameter size (expected " +
152 std::to_string(param_size) + ", but got " +
153 std::to_string(cs_problem.param.size()) + ")");
154 load_initial_guess(opts, problem);
155 count_problem(problem);
156 return problem;
157}
158#endif
159
160#ifdef ALPAQA_WITH_CUTEST
161template <bool = true>
162LoadedProblem load_cu_problem(const fs::path &full_path,
163 std::span<std::string_view> prob_opts,
164 alpaqa::Options &opts) {
165 std::string outsdif_path;
166 alpaqa::params::set_params(outsdif_path, "outsdif", prob_opts);
167 bool sparse = false;
168 alpaqa::params::set_params(sparse, "sparse", prob_opts);
169 static std::mutex mtx;
170 std::unique_lock lck{mtx};
171 using TEProblem = alpaqa::TypeErasedProblem<config_t>;
172 using CuProblem = alpaqa::CUTEstProblem;
173 using CntProblem = alpaqa::ProblemWithCounters<CuProblem>;
174 auto flags = get_dl_flags(opts);
175 LoadedProblem problem{
176 .problem =
177 TEProblem::make<CntProblem>(std::in_place, full_path.c_str(),
178 outsdif_path.c_str(), sparse, flags),
179 .abs_path = fs::absolute(full_path),
180 .path = full_path,
181 };
182 lck.unlock();
183 auto &cnt_problem = problem.problem.as<CntProblem>();
184 auto &cu_problem = cnt_problem.problem;
185 problem.name = cu_problem.get_name();
186 problem.evaluations = cnt_problem.evaluations;
187 problem.initial_guess_x = std::move(cu_problem.x0);
188 problem.initial_guess_y = std::move(cu_problem.y0);
189 load_initial_guess(opts, problem);
190 count_problem(problem);
191 return problem;
192}
193#endif
194
195} // namespace
196
197namespace alpaqa {
198
199LoadedProblem load_problem(std::string_view type, const fs::path &file,
200 Options &opts) {
202 // Isolate problem-specific options
203 std::vector<std::string_view> prob_opts;
204 std::string_view prob_prefix = "problem.";
205 auto options = opts.options();
206 auto used = opts.used();
207 for (auto opt = options.begin(); opt != options.end(); ++opt) {
208 if (opt->starts_with(prob_prefix)) {
209 prob_opts.push_back(opt->substr(prob_prefix.size()));
210 ++used.begin()[opt - options.begin()];
211 }
212 }
213 // Load problem
214 if (type == "dl" || type.empty()) {
215#if ALPAQA_WITH_DL
216 return load_dl_problem(file, prob_opts, opts);
217#else
218 throw std::logic_error("This version of alpaqa was compiled without "
219 "support for dynamic problem loading");
220#endif
221 } else if (type == "cs") {
222#if ALPAQA_WITH_CASADI
223 if constexpr (std::is_same_v<config_t, alpaqa::EigenConfigd>)
224 return load_cs_problem(file, prob_opts, opts);
225 else
226 throw std::logic_error("CasADi only supports double precision.");
227#else
228 throw std::logic_error(
229 "This version of alpaqa was compiled without CasADi support");
230#endif
231 } else if (type == "cu") {
232#ifdef ALPAQA_WITH_CUTEST
233 if constexpr (std::is_same_v<config_t, alpaqa::EigenConfigd>)
234 return load_cu_problem(file, prob_opts, opts);
235 else
236 throw std::logic_error("CUTEst only supports double precision.");
237#else
238 throw std::logic_error(
239 "This version of alpaqa was compiled without CUTEst support");
240#endif
241 }
242 throw std::invalid_argument("Unknown problem type '" + std::string(type) +
243 "'");
244}
245
246void print_problem_description(std::ostream &os, LoadedProblem &problem,
247 bool show_funcs) {
248 os << "Loaded problem \"" << problem.name << "\"\n"
249 << "Number of variables: " << problem.problem.get_num_variables()
250 << "\n"
251 << "Number of constraints: " << problem.problem.get_num_constraints()
252 << "\n";
253 if (problem.nnz_jac_g)
254 os << "Nonzeros in Jg: " << *problem.nnz_jac_g << "\n";
255 if (problem.nnz_hess_L)
256 os << "Nonzeros in ∇²L: " << *problem.nnz_hess_L << "\n";
257 if (problem.nnz_hess_ψ)
258 os << "Nonzeros in ∇²ψ: " << *problem.nnz_hess_ψ << "\n";
259 if (problem.box_constr_count)
260 os << "Box constraints:" //
261 << "\n Fixed variables: " << problem.box_constr_count->eq
262 << "\n Bilateral: " << problem.box_constr_count->lbub
263 << "\n Lower bound only: " << problem.box_constr_count->lb
264 << "\n Upper bound only: " << problem.box_constr_count->ub
265 << "\n";
266 if (problem.general_constr_count)
267 os << "General constraints:" //
268 << "\n Equality: " << problem.general_constr_count->eq
269 << "\n Bilateral: " << problem.general_constr_count->lbub
270 << "\n Lower bound only: " << problem.general_constr_count->lb
271 << "\n Upper bound only: " << problem.general_constr_count->ub
272 << "\n";
273 if (show_funcs) {
274 os << "Provided functions:\n";
276 }
277}
278
279} // 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.