Nonconvex constrained optimization
Loading...
Searching...
No Matches
param-complete.cpp
Go to the documentation of this file.
1#include "param-complete.hpp"
2
5#include <guanaqo/demangled-typename.hpp>
6#include <guanaqo/string-util.hpp>
7
8// For parameters
21#include <alpaqa/outer/alm.hpp>
22#include <guanaqo/dl-flags.hpp>
23#if ALPAQA_WITH_OCP
25#endif
26#if ALPAQA_WITH_LBFGSB
28#endif
29// end
30
31#include <cstdio>
32#include <iostream>
33#include <numeric>
34#include <optional>
35#include <stdexcept>
36#include <string_view>
37using namespace std::string_view_literals;
38
40
41namespace alpaqa::params {
43template <class T>
44bool is_leaf() {
45 return !requires { attribute_table<T, MemberGetter>::table; };
47
48/// Catch-all
49template <class T>
51 auto pfx = std::string_view{s.full_key.begin(), s.key.begin()};
52 return {.leaf = true, .prefix = pfx, .members = {}}; // no suggestions
54
55/// Struct types
56template <class T>
57 requires requires { attribute_table<T, MemberGetter>::table; }
60 auto [key, remainder] = guanaqo::split(s.key, ".");
61 auto it = m.find(key);
62 if (it == m.end()) {
63 auto pfx = std::string_view{s.full_key.begin(), s.key.begin()};
64 if (key.end() != s.key.end() || s.value)
65 return {.leaf = false, .prefix = pfx, .members = {}};
66 auto members = std::views::transform(m, [](const auto &e) {
67 return Result::Member{
68 .name = e.first,
69 .doc = e.second.doc,
70 .suffix = e.second.leaf ? '=' : '.',
71 };
72 });
73 return {
74 .leaf = false,
75 .prefix = pfx,
76 .members = {members.begin(), members.end()},
77 };
78 }
79 auto recurse = s;
80 recurse.key = remainder;
81 return it->second.get(recurse);
82}
83
84/// Enum types
85template <class T>
86 requires requires { enum_table<T, MemberGetter>::table; }
89 auto pfx = std::string_view{s.full_key.begin(), s.key.begin()};
90 auto members = std::views::transform(m, [](const auto &e) {
91 return Result::Member{
92 .name = e.first,
93 .doc = e.second.doc,
94 .suffix = std::nullopt,
95 };
96 });
97 return {
98 .leaf = true,
99 .prefix = pfx,
100 .members = {members.begin(), members.end()},
101 };
102}
103
104/// True/false
105template <>
107 auto pfx = std::string_view{s.full_key.begin(), s.key.begin()};
108 return {
109 .leaf = true,
110 .prefix = pfx,
111 .members = {{.name = "true"}, {.name = "false"}},
112 };
113}
114
115struct Value {};
116struct Struct {};
117
118struct RootOpts {
119 [[no_unique_address]] Value method, out, sol, x0, mul_g0, mul_x0, num_exp;
122 guanaqo::DynamicLoadFlags dl_flags;
123};
124
126#if ALPAQA_WITH_LBFGSB
128#endif
129
131 RootOpts, //
132 PARAMS_MEMBER(method, "Solver to use"), //
133 PARAMS_MEMBER(out, "File to write output to"), //
134 PARAMS_MEMBER(sol, "Folder to write the solutions and statistics to"), //
135 PARAMS_MEMBER(x0, "Initial guess for the solution"), //
136 PARAMS_MEMBER(mul_g0,
137 "Initial guess for the general constraint multipliers"), //
138 PARAMS_MEMBER(mul_x0,
139 "Initial guess for the bound constraint multipliers"), //
140 PARAMS_MEMBER(num_exp, "Number of times to repeat the experiment"), //
141 PARAMS_MEMBER(extra_stats, "Log more per-iteration solver statistics"), //
142 PARAMS_MEMBER(show_funcs, "Print the provided problem functions"), //
143 PARAMS_MEMBER(problem, "Options to pass to the problem"), //
144 PARAMS_MEMBER(dl_flags, "Flags to pass to dlopen"), //
146
148
149} // namespace alpaqa::params
150
154
155void add_root_opts(std::vector<Result::Member> &v) {
156 MemberGetter s{};
158 for (auto &e : r.members)
159 v.push_back(e);
160}
161
162template <class S>
164 auto [key, remainder] = guanaqo::split(s.key, ".");
165 auto recurse = s;
166 recurse.key = remainder;
167
168 if (key == "alm")
170 if (key == "solver")
171 return get_members<typename S::Params>(recurse);
172 if (key == "dir")
174 if (key == "accel")
176 if (key.end() != s.key.end() || s.value) // no remainder && no '.'
177 return {.leaf = false, .prefix = "", .members = {}};
178 return {
179 .leaf = false,
180 .prefix = "",
181 .members = {{.name = "alm",
182 .doc = "Options for the augmented Lagrangian method",
183 .suffix = '.'},
184 {.name = "solver",
185 .doc = "Options for the inner solver",
186 .suffix = '.'},
187 {.name = "dir",
188 .doc = "Options for the direction provider",
189 .suffix = '.'},
190 {.name = "accel",
191 .doc = "Options for the accelerator",
192 .suffix = '.'}},
193 };
194}
195
196template <class S>
198 auto [key, remainder] = guanaqo::split(s.key, ".");
199 auto recurse = s;
200 recurse.key = remainder;
201
202 if (key == "alm")
204 if (key == "solver")
205 return get_members<typename S::Params>(recurse);
206 if (key.end() != s.key.end() || s.value) // no remainder && no '.'
207 return {.leaf = false, .prefix = "", .members = {}};
208 return {
209 .leaf = false,
210 .prefix = "",
211 .members = {{.name = "alm",
212 .doc = "Options for the augmented Lagrangian method",
213 .suffix = '.'},
214 {.name = "solver",
215 .doc = "Options for the inner solver",
216 .suffix = '.'}},
217 };
218}
219
220template <class S>
222 auto [key, remainder] = guanaqo::split(s.key, ".");
223 auto recurse = s;
224 recurse.key = remainder;
225
226 if (key == "alm")
228 if (key == "solver")
229 return get_members<typename S::Params>(recurse);
230 if (key.end() != s.key.end() || s.value)
231 return {.leaf = false, .prefix = "", .members = {}};
232 return {
233 .leaf = false,
234 .prefix = "",
235 .members = {{.name = "alm",
236 .doc = "Options for the augmented Lagrangian method",
237 .suffix = '.'},
238 {.name = "solver",
239 .doc = "Options for the inner solver",
240 .suffix = '.'}},
241 };
242}
243
244using func_t = Result(const MemberGetter &);
245struct Method {
246 // std::function<func_t> func;
248 std::string_view doc;
249};
250using dict_t = std::map<std::string_view, Method>;
251
253 // clang-format off
254 {"panoc", {get_results_panoc_like<alpaqa::PANOCSolver<alpaqa::LBFGSDirection<config_t>>>, "PANOC + LBFGS solver (default)"}},
255 {"panoc.lbfgs", {get_results_panoc_like<alpaqa::PANOCSolver<alpaqa::LBFGSDirection<config_t>>>, "PANOC + LBFGS solver"}},
256 {"panoc.struclbfgs", {get_results_panoc_like<alpaqa::PANOCSolver<alpaqa::StructuredLBFGSDirection<config_t>>>, "PANOC + Structured LBFGS solver"}},
257 {"panoc.anderson", {get_results_panoc_like<alpaqa::PANOCSolver<alpaqa::AndersonDirection<config_t>>>, "PANOC + Anderson acceleration solver"}},
258 {"panoc.convex-newton", {get_results_panoc_like<alpaqa::PANOCSolver<alpaqa::ConvexNewtonDirection<config_t>>>, "PANOC + Newton (for convex problems)"}},
260 {"zerofpr.lbfgs", {get_results_panoc_like<alpaqa::ZeroFPRSolver<alpaqa::LBFGSDirection<config_t>>>, "ZeroFPR + LBFGS solver"}},
261 {"zerofpr.struclbfgs", {get_results_panoc_like<alpaqa::ZeroFPRSolver<alpaqa::StructuredLBFGSDirection<config_t>>>, "ZeroFPR + Structured LBFGS solver"}},
262 {"zerofpr.anderson", {get_results_panoc_like<alpaqa::ZeroFPRSolver<alpaqa::AndersonDirection<config_t>>>, "ZeroFPR + Anderson acceleration solver"}},
263 {"zerofpr.convex-newton", {get_results_panoc_like<alpaqa::PANOCSolver<alpaqa::ConvexNewtonDirection<config_t>>>, "ZeroFPR + Newton (for convex problems)"}},
265 {"fista", {get_results_fista_like<alpaqa::FISTASolver<config_t>>, "FISTA solver"}},
266 {"ipopt", {alpaqa::params::get_members<void>, "Ipopt solver"}},
267 {"qpalm", {alpaqa::params::get_members<void>, "QPALM solver"}},
268 #if ALPAQA_WITH_LBFGSB
269 {"lbfgsb", {get_results_lbfgsb<alpaqa::lbfgsb::LBFGSBSolver>, "L-BFGS-B solver"}},
270 #else
271 {"lbfgsb", {alpaqa::params::get_members<void>, "L-BFGS-B solver (unavailable)"}},
272 #endif
273 // clang-format on
274};
275
276Result get_results(std::string_view method, const MemberGetter &s) {
277 if (s.key == "method") {
278 auto members = std::views::transform(methods, [](const auto &e) {
279 return Result::Member{
280 .name = e.first,
281 .doc = e.second.doc,
282 .suffix = std::nullopt,
283 };
284 });
285 return {
286 .leaf = true,
287 .prefix = "method",
288 .members = {members.begin(), members.end()},
289 };
290 }
291
292 if (method.empty())
293 method = "panoc";
294 try {
295 const auto &m = methods.at(method);
296 auto result = m.func(s);
297 if (result.members.empty())
299 else if (result.prefix.empty())
300 add_root_opts(result.members);
301 return result;
302 } catch (std::out_of_range &) {
304 }
305}
306
307void print_completion(std::string_view method, std::string_view params) {
308 auto [key, value] = guanaqo::split(params, "=");
309 bool has_value = key.end() != params.end();
310 MemberGetter s{
311 .full_key = key,
312 .key = key,
313 .value = has_value ? std::make_optional(value) : std::nullopt,
314 };
315 auto result = get_results(method, s);
316 if (!result.members.empty()) {
317 std::cout << "_prefix:" << result.prefix;
318 if (!result.prefix.empty() && result.prefix.back() != '.')
319 std::cout << (result.leaf ? '=' : '.');
320 std::cout << "\n_suffix:";
321 if (result.leaf)
322 std::cout << ' ';
323 std::cout << '\n';
324 size_t max_len =
325 std::accumulate(result.members.begin(), result.members.end(),
326 size_t{0}, [](size_t acc, const auto &m) {
327 return std::max(acc, m.name.size());
328 });
329 for (const auto &member : result.members) {
330 auto name = member.name;
331 std::string padding(max_len - std::min(max_len, name.size()), ' ');
332 std::cout << name;
333 if (member.suffix)
334 std::cout << *member.suffix;
335 std::cout << ':' << name;
336 if (member.doc) {
337 auto doc = member.doc->empty() ? "(undocumented)" : *member.doc;
338 std::cout << padding << " -- " << doc << " ";
339 }
340 std::cout << '\n';
341 }
342 }
343}
#define USING_ALPAQA_CONFIG(Conf)
Definition config.hpp:77
Result get_members(const MemberGetter &s)
Catch-all.
std::string_view key
The subkey to resolve next.
std::string_view full_key
Full key string, used for diagnostics.
Result get_members< bool >(const MemberGetter &s)
True/false.
std::optional< std::string_view > value
The value of the parameter to store.
guanaqo::DynamicLoadFlags dl_flags
Specialize this type to define the attribute name to attribute setters dictionaries for a struct type...
Definition structs.hpp:21
Specialize this type to define the enumerator name to value dictionaries for an enum type T.
Definition structs.hpp:87
EigenConfigd DefaultConfig
Definition config.hpp:31
Result get_results_lbfgsb(const MemberGetter &s)
Result get_results(std::string_view method, const MemberGetter &s)
Result get_results_fista_like(const MemberGetter &s)
Result(const MemberGetter &) func_t
std::map< std::string_view, Method > dict_t
Result get_results_panoc_like(const MemberGetter &s)
std::string_view doc
Result get_members(const MemberGetter &s)
Catch-all.
const dict_t methods
func_t * func
void print_completion(std::string_view method, std::string_view params)
void add_root_opts(std::vector< Result::Member > &v)
#define PARAMS_MEMBER(name,...)
Helper macro to easily initialize a attribute_table_t.
Definition structs.hpp:41
#define PARAMS_TABLE(type_,...)
Helper macro to easily specialize attribute_table.
Definition structs.hpp:24