alpaqa guanaqo
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// end
27
28#include <cstdio>
29#include <iostream>
30#include <numeric>
31#include <optional>
32#include <stdexcept>
33#include <string_view>
34using namespace std::string_view_literals;
35
38namespace alpaqa::params {
39
40template <class T>
41bool is_leaf() {
43}
44
45/// Catch-all
46template <class T>
48 auto pfx = std::string_view{s.full_key.begin(), s.key.begin()};
49 return {.leaf = true, .prefix = pfx, .members = {}}; // no suggestions
50}
51
52/// Struct types
53template <class T>
54 requires requires { attribute_table<T, MemberGetter>::table; }
57 auto [key, remainder] = guanaqo::split(s.key, ".");
58 auto it = m.find(key);
59 if (it == m.end()) {
60 auto pfx = std::string_view{s.full_key.begin(), s.key.begin()};
61 if (key.end() != s.key.end() || s.value)
62 return {.leaf = false, .prefix = pfx, .members = {}};
63 auto members = std::views::transform(m, [](const auto &e) {
64 return Result::Member{
65 .name = e.first,
66 .doc = e.second.doc,
67 .suffix = e.second.leaf ? '=' : '.',
68 };
69 });
70 return {
71 .leaf = false,
72 .prefix = pfx,
73 .members = {members.begin(), members.end()},
74 };
75 }
76 auto recurse = s;
77 recurse.key = remainder;
78 return it->second.get(recurse);
79}
80
81/// Enum types
82template <class T>
83 requires requires { enum_table<T, MemberGetter>::table; }
86 auto pfx = std::string_view{s.full_key.begin(), s.key.begin()};
87 auto members = std::views::transform(m, [](const auto &e) {
88 return Result::Member{
89 .name = e.first,
90 .doc = e.second.doc,
91 .suffix = std::nullopt,
92 };
93 });
94 return {
95 .leaf = true,
96 .prefix = pfx,
97 .members = {members.begin(), members.end()},
98 };
99}
100
101/// True/false
102template <>
104 auto pfx = std::string_view{s.full_key.begin(), s.key.begin()};
105 return {
106 .leaf = true,
107 .prefix = pfx,
108 .members = {{.name = "true"}, {.name = "false"}},
109 };
110}
111
112struct Value {};
113struct Struct {};
114
115struct RootOpts {
116 [[no_unique_address]] Value method, out, sol, x0, mul_g0, mul_x0, num_exp;
119 guanaqo::DynamicLoadFlags dl_flags;
120};
123
125 RootOpts, //
126 PARAMS_MEMBER(method, "Solver to use"), //
127 PARAMS_MEMBER(out, "File to write output to"), //
128 PARAMS_MEMBER(sol, "Folder to write the solutions and statistics to"), //
129 PARAMS_MEMBER(x0, "Initial guess for the solution"), //
130 PARAMS_MEMBER(mul_g0,
131 "Initial guess for the general constraint multipliers"), //
132 PARAMS_MEMBER(mul_x0,
133 "Initial guess for the bound constraint multipliers"), //
134 PARAMS_MEMBER(num_exp, "Number of times to repeat the experiment"), //
135 PARAMS_MEMBER(extra_stats, "Log more per-iteration solver statistics"), //
136 PARAMS_MEMBER(show_funcs, "Print the provided problem functions"), //
137 PARAMS_MEMBER(problem, "Options to pass to the problem"), //
138 PARAMS_MEMBER(dl_flags, "Flags to pass to dlopen"), //
140
142
143} // namespace alpaqa::params
144
148
149void add_root_opts(std::vector<Result::Member> &v) {
150 MemberGetter s{};
152 for (auto &e : r.members)
153 v.push_back(e);
154}
155
156template <class S>
158 auto [key, remainder] = guanaqo::split(s.key, ".");
159 auto recurse = s;
160 recurse.key = remainder;
161
162 if (key == "alm")
164 if (key == "solver")
165 return get_members<typename S::Params>(recurse);
166 if (key == "dir")
168 if (key == "accel")
170 if (key.end() != s.key.end() || s.value) // no remainder && no '.'
171 return {.leaf = false, .prefix = "", .members = {}};
172 return {
173 .leaf = false,
174 .prefix = "",
175 .members = {{.name = "alm",
176 .doc = "Options for the augmented Lagrangian method",
177 .suffix = '.'},
178 {.name = "solver",
179 .doc = "Options for the inner solver",
180 .suffix = '.'},
181 {.name = "dir",
182 .doc = "Options for the direction provider",
183 .suffix = '.'},
184 {.name = "accel",
185 .doc = "Options for the accelerator",
186 .suffix = '.'}},
187 };
188}
189
190template <class S>
192 auto [key, remainder] = guanaqo::split(s.key, ".");
193 auto recurse = s;
194 recurse.key = remainder;
195
196 if (key == "alm")
198 if (key == "solver")
199 return get_members<typename S::Params>(recurse);
200 if (key.end() != s.key.end() || s.value)
201 return {.leaf = false, .prefix = "", .members = {}};
202 return {
203 .leaf = false,
204 .prefix = "",
205 .members = {{.name = "alm",
206 .doc = "Options for the augmented Lagrangian method",
207 .suffix = '.'},
208 {.name = "solver",
209 .doc = "Options for the inner solver",
210 .suffix = '.'}},
211 };
212}
213
214using func_t = Result(const MemberGetter &);
215struct Method {
216 // std::function<func_t> func;
218 std::string_view doc;
219};
220using dict_t = std::map<std::string_view, Method>;
221
223 // clang-format off
224 {"panoc", {get_results_panoc_like<alpaqa::PANOCSolver<alpaqa::LBFGSDirection<config_t>>>, "PANOC + LBFGS solver (default)"}},
225 {"panoc.lbfgs", {get_results_panoc_like<alpaqa::PANOCSolver<alpaqa::LBFGSDirection<config_t>>>, "PANOC + LBFGS solver"}},
226 {"panoc.struclbfgs", {get_results_panoc_like<alpaqa::PANOCSolver<alpaqa::StructuredLBFGSDirection<config_t>>>, "PANOC + Structured LBFGS solver"}},
227 {"panoc.anderson", {get_results_panoc_like<alpaqa::PANOCSolver<alpaqa::AndersonDirection<config_t>>>, "PANOC + Anderson acceleration solver"}},
228 {"panoc.convex-newton", {get_results_panoc_like<alpaqa::PANOCSolver<alpaqa::ConvexNewtonDirection<config_t>>>, "PANOC + Newton (for convex problems)"}},
230 {"zerofpr.lbfgs", {get_results_panoc_like<alpaqa::ZeroFPRSolver<alpaqa::LBFGSDirection<config_t>>>, "ZeroFPR + LBFGS solver"}},
231 {"zerofpr.struclbfgs", {get_results_panoc_like<alpaqa::ZeroFPRSolver<alpaqa::StructuredLBFGSDirection<config_t>>>, "ZeroFPR + Structured LBFGS solver"}},
232 {"zerofpr.anderson", {get_results_panoc_like<alpaqa::ZeroFPRSolver<alpaqa::AndersonDirection<config_t>>>, "ZeroFPR + Anderson acceleration solver"}},
233 {"zerofpr.convex-newton", {get_results_panoc_like<alpaqa::PANOCSolver<alpaqa::ConvexNewtonDirection<config_t>>>, "ZeroFPR + Newton (for convex problems)"}},
235 {"fista", {get_results_fista_like<alpaqa::FISTASolver<config_t>>, "FISTA solver"}},
236 {"ipopt", {alpaqa::params::get_members<void>, "Ipopt solver"}},
237 {"qpalm", {alpaqa::params::get_members<void>, "QPALM solver"}},
238 // clang-format on
239};
240
241Result get_results(std::string_view method, const MemberGetter &s) {
242 if (s.key == "method") {
243 auto members = std::views::transform(methods, [](const auto &e) {
244 return Result::Member{
245 .name = e.first,
246 .doc = e.second.doc,
247 .suffix = std::nullopt,
248 };
249 });
250 return {
251 .leaf = true,
252 .prefix = "method",
253 .members = {members.begin(), members.end()},
254 };
255 }
256
257 if (method.empty())
258 method = "panoc";
259 try {
260 const auto &m = methods.at(method);
261 auto result = m.func(s);
262 if (result.members.empty())
264 else if (result.prefix.empty())
265 add_root_opts(result.members);
266 return result;
267 } catch (std::out_of_range &) {
269 }
270}
271
272void print_completion(std::string_view method, std::string_view params) {
273 auto [key, value] = guanaqo::split(params, "=");
274 bool has_value = key.end() != params.end();
275 MemberGetter s{
276 .full_key = key,
277 .key = key,
278 .value = has_value ? std::make_optional(value) : std::nullopt,
279 };
280 auto result = get_results(method, s);
281 if (!result.members.empty()) {
282 std::cout << "_prefix:" << result.prefix;
283 if (!result.prefix.empty() && result.prefix.back() != '.')
284 std::cout << (result.leaf ? '=' : '.');
285 std::cout << "\n_suffix:";
286 if (result.leaf)
287 std::cout << ' ';
288 std::cout << '\n';
289 size_t max_len =
290 std::accumulate(result.members.begin(), result.members.end(),
291 size_t{0}, [](size_t acc, const auto &m) {
292 return std::max(acc, m.name.size());
293 });
294 for (const auto &member : result.members) {
295 auto name = member.name;
296 std::string padding(max_len - std::min(max_len, name.size()), ' ');
297 std::cout << name;
298 if (member.suffix)
299 std::cout << *member.suffix;
300 std::cout << ':' << name;
301 if (member.doc) {
302 auto doc = member.doc->empty() ? "(undocumented)" : *member.doc;
303 std::cout << padding << " -- " << doc << " ";
304 }
305 std::cout << '\n';
306 }
307 }
308}
#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:90
EigenConfigd DefaultConfig
Definition config.hpp:31
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