alpaqa guanaqo
Nonconvex constrained optimization
Loading...
Searching...
No Matches
json.cpp
Go to the documentation of this file.
2
4#include <guanaqo/demangled-typename.hpp>
5#include <guanaqo/duration-parse.hpp>
6#include <guanaqo/io/csv.hpp>
7#include <guanaqo/possible-alias.hpp>
8#include <algorithm>
9#include <cmath>
10#include <concepts>
11#include <fstream>
12#include <limits>
13#include <variant>
14
27#include <alpaqa/outer/alm.hpp>
28#include <guanaqo/dl-flags.hpp>
29#if ALPAQA_WITH_OCP
31#endif
33namespace alpaqa::params {
34
35using guanaqo::demangled_typename;
36
37template <class T>
38inline constexpr bool is_duration = false;
39template <class Rep, class Period>
41
42template <class Duration>
44void set_param_default(Duration &t, const json &j) {
45 if (!j.is_string())
47 "Invalid value " + to_string(j) + " for type '" +
48 demangled_typename(typeid(Duration)) + "' (expected a string)");
49 std::string value = j; // keep outside of try block
50 try {
51 guanaqo::parse_duration(t = {}, value);
52 } catch (guanaqo::invalid_duration_value &e) {
54 "Invalid value '" + value + "' for type '" +
55 demangled_typename(typeid(Duration)) + "': error at '" +
56 std::string(std::string_view(value.data(), e.result.ptr)));
57 } catch (guanaqo::invalid_duration_units &e) {
59 "Invalid units '" + std::string(e.units) + "' for type '" +
60 demangled_typename(typeid(Duration)) + "' in '" + value + "'");
61 }
62}
63
64template <class Duration>
66void get_param_default(const Duration &t, json &s) {
67 namespace chr = std::chrono;
68 auto dur = t;
69 std::string result;
70 if (dur.count() == 0) {
71 result = "0";
72 } else {
73 if (auto d = duration_cast<chr::hours>(dur); d.count() != 0) {
74 result += std::to_string(d.count()) + "h";
75 dur -= duration_cast<Duration>(d);
76 }
77 if (auto d = duration_cast<chr::minutes>(dur); d.count() != 0) {
78 result += std::to_string(d.count()) + "min";
79 dur -= duration_cast<Duration>(d);
80 }
81 if (auto d = duration_cast<chr::seconds>(dur); d.count() != 0) {
82 result += std::to_string(d.count()) + "s";
83 dur -= duration_cast<Duration>(d);
84 }
85 if (auto d = duration_cast<chr::milliseconds>(dur); d.count() != 0) {
86 result += std::to_string(d.count()) + "ms";
87 dur -= duration_cast<Duration>(d);
88 }
89 if (auto d = duration_cast<chr::microseconds>(dur); d.count() != 0) {
90 result += std::to_string(d.count()) + "µs";
91 dur -= duration_cast<Duration>(d);
92 }
93 if (auto d = duration_cast<chr::nanoseconds>(dur); d.count() != 0) {
94 result += std::to_string(d.count()) + "ns";
95 dur -= duration_cast<Duration>(d);
96 }
97 }
98 s = std::move(result);
99}
100
101template <>
102void ALPAQA_EXPORT set_param(alpaqa::vec<config_t> &v, const json &j) {
103 if (!j.is_array())
104 throw invalid_json_param("Invalid value " + to_string(j) +
105 " for type '" + demangled_typename(typeid(v)) +
106 "' (expected an array, but got " +
107 j.type_name() + ')');
108 v.resize(static_cast<length_t<config_t>>(j.size()));
109 auto convert = [](const json &j) -> real_t<config_t> {
110 try {
111 return j;
112 } catch (json::exception &e) {
113 throw invalid_json_param("Invalid vector element " + to_string(j) +
114 " (expected a number, but got " +
115 j.type_name() + "): " + e.what());
116 }
117 };
118 std::ranges::transform(j, v.begin(), convert);
119}
120
121template <>
122void ALPAQA_EXPORT set_param(vec_from_file<config_t> &v, const json &j) {
123 using namespace guanaqo::io;
124 if (j.is_string()) {
125 std::string fpath{j};
126 std::ifstream f(fpath);
127 if (!f)
128 throw invalid_json_param("Unable to open file '" + fpath +
129 "' for type '" +
130 demangled_typename(typeid(v)));
131 try {
132 auto r = csv_read_row_std_vector<real_t<config_t>>(f);
133 auto r_size = static_cast<length_t<config_t>>(r.size());
134 if (v.expected_size >= 0 && r_size != v.expected_size)
135 throw invalid_json_param(
136 "Incorrect size in '" + fpath + "' (expected " +
137 std::to_string(v.expected_size) + ", but got " +
138 std::to_string(r.size()) + ")");
139 v.value.emplace(cmvec<config_t>{r.data(), r_size});
140 } catch (csv_read_error &e) {
141 throw invalid_json_param("Unable to read from file '" + fpath +
142 "': alpaqa::csv::read_error: " + e.what());
144 } else if (j.is_array()) {
145 alpaqa::params::set_param(v.value.emplace(), j);
146 if (v.expected_size >= 0 && v.value->size() != v.expected_size)
148 "Incorrect size in " + to_string(j) + "' (expected " +
149 std::to_string(v.expected_size) + ", but got " +
150 std::to_string(v.value->size()) + ')');
151 } else {
152 throw invalid_json_param("Invalid value " + to_string(j) +
153 " for type '" + demangled_typename(typeid(v)) +
154 "' (expected string or array, but got " +
155 j.type_name() + ')');
156 }
158
159template <>
160void ALPAQA_EXPORT set_param(std::monostate &, const nlohmann::json &) {
161 throw invalid_json_param("Cannot set value of std::monostate");
162}
164template <>
165void ALPAQA_EXPORT set_param(bool &t, const nlohmann::json &j) {
166 if (!j.is_boolean())
167 throw invalid_json_param("Invalid value " + to_string(j) +
168 " for type '" + demangled_typename(typeid(t)) +
169 "' (expected boolean, but got " +
170 j.type_name() + ')');
171 t = static_cast<bool>(j);
172}
173
174template <>
175void ALPAQA_EXPORT set_param(std::string &t, const nlohmann::json &j) {
176 if (!j.is_string())
177 throw invalid_json_param("Invalid value " + to_string(j) +
178 " for type '" + demangled_typename(typeid(t)) +
179 "' (expected string, but got " +
180 j.type_name() + ')');
181 t = static_cast<std::string>(j);
182}
183
184template <std::integral T>
185 requires(!std::same_as<T, bool>)
186void set_param_default(T &t, const nlohmann::json &j) {
187 if (std::unsigned_integral<T> && !j.is_number_unsigned())
188 throw invalid_json_param("Invalid value " + to_string(j) +
189 " for type '" + demangled_typename(typeid(T)) +
190 "' (expected unsigned integer, but got " +
191 j.type_name() + ')');
192 if (!j.is_number_integer())
193 throw invalid_json_param("Invalid value " + to_string(j) +
194 " for type '" + demangled_typename(typeid(T)) +
195 "' (expected integer, but got " +
196 j.type_name() + ')');
197 t = static_cast<T>(j);
198}
199
200template <std::floating_point T>
201void set_param_default(T &t, const nlohmann::json &j) {
202 if (j.is_string()) {
203 if (j == "nan") {
204 t = std::numeric_limits<T>::quiet_NaN();
205 } else if (j == "inf" || j == "+inf") {
206 t = std::numeric_limits<T>::infinity();
207 } else if (j == "-inf") {
208 t = -std::numeric_limits<T>::infinity();
209 } else {
210 throw invalid_json_param("Invalid value " + to_string(j) +
211 " for type '" +
212 demangled_typename(typeid(T)) +
213 "' (expected number or any of "
214 "\"nan\", \"inf\", \"+inf\", \"-inf\")");
215 }
216 } else if (j.is_number()) {
217 t = static_cast<T>(j);
218 } else {
219 throw invalid_json_param("Invalid value " + to_string(j) +
220 " for type '" + demangled_typename(typeid(T)) +
221 "' (expected number, but got " +
222 j.type_name() + ')');
223 }
224}
225
226template <class T>
227 requires(std::integral<T> || std::same_as<T, bool> ||
228 std::same_as<T, std::string>)
229void get_param_default(const T &t, nlohmann::json &j) {
230 j = t;
231}
232
233template <std::floating_point T>
234void get_param_default(const T &t, nlohmann::json &j) {
235 if (std::isnan(t))
236 j = "nan";
237 else if (t == +std::numeric_limits<T>::infinity())
238 j = "inf";
239 else if (t == -std::numeric_limits<T>::infinity())
240 j = "-inf";
241 else
242 j = t;
243}
244
246
247// Because of the new name mangling for concepts (https://reviews.llvm.org/D147655)
248// we need this to be an unconstrained function template. The actual constraints
249// are in set_param_default, which is not exported. Fully specialized
250// instantiations of set_param are still allowed.
251template <class T>
252void set_param(T &t, const json &j) {
253 set_param_default(t, j);
254}
255template <class T>
256void get_param(const T &t, json &j) {
257 get_param_default(t, j);
258}
259
260template <class... Ts>
261void set_param(guanaqo::detail::dummy<Ts...> &, const json &) {}
262template <class... Ts>
263void get_param(const guanaqo::detail::dummy<Ts...> &, json &) {}
264
265#define ALPAQA_GET_PARAM_INST(...) \
266 template void ALPAQA_EXPORT get_param( \
267 const guanaqo::possible_alias_t<__VA_ARGS__> &, json &)
268#define ALPAQA_GETSET_PARAM_INST(...) \
269 template void ALPAQA_EXPORT set_param( \
270 guanaqo::possible_alias_t<__VA_ARGS__> &, const json &); \
271 template void ALPAQA_EXPORT get_param( \
272 const guanaqo::possible_alias_t<__VA_ARGS__> &, json &)
273
275
277
280ALPAQA_GETSET_PARAM_INST(long double, double, float);
281
290
291// Here, we would like to instantiate alpaqa::params::set_param for all standard
292// integer types, but the issue is that they might not be distinct types:
293// For example, on some platforms, int32_t might be a weak alias to int, whereas
294// on other platforms, it could be a distinct type.
295// To resolve this issue, we use some metaprogramming to ensure distinct
296// instantiations with unique dummy types.
297#define ALPAQA_GETSET_PARAM_INST_INT(...) \
298 ALPAQA_GETSET_PARAM_INST(__VA_ARGS__, int8_t, uint8_t, int16_t, uint16_t, \
299 int32_t, int64_t, uint32_t, uint64_t)
300
304ALPAQA_GETSET_PARAM_INST_INT(long long, long, int, short);
305ALPAQA_GETSET_PARAM_INST_INT(ptrdiff_t, long long, long, int, short);
307ALPAQA_GETSET_PARAM_INST_INT(unsigned int, unsigned short);
308ALPAQA_GETSET_PARAM_INST_INT(unsigned long, unsigned int, unsigned short);
309ALPAQA_GETSET_PARAM_INST_INT(unsigned long long, unsigned long, unsigned int,
310 unsigned short);
311ALPAQA_GETSET_PARAM_INST_INT(size_t, unsigned long long, unsigned long,
312 unsigned int, unsigned short);
313
314ALPAQA_GETSET_PARAM_INST(std::chrono::nanoseconds);
315ALPAQA_GETSET_PARAM_INST(std::chrono::microseconds);
316ALPAQA_GETSET_PARAM_INST(std::chrono::milliseconds);
317ALPAQA_GETSET_PARAM_INST(std::chrono::seconds);
318ALPAQA_GETSET_PARAM_INST(std::chrono::minutes);
319ALPAQA_GETSET_PARAM_INST(std::chrono::hours);
320
323ALPAQA_GETSET_PARAM_INST(guanaqo::DynamicLoadFlags);
342#if ALPAQA_WITH_OCP
344#endif
345
346} // namespace alpaqa::params
Parameters for the Augmented Lagrangian solver.
Definition alm.hpp:21
Parameters for the AndersonAccel class.
Definition anderson.hpp:15
Parameters for the AndersonDirection class.
Definition anderson.hpp:12
Parameters for the ConvexNewtonDirection class.
Parameters for the ConvexNewtonDirection class.
Tuning parameters for the FISTA algorithm.
Definition fista.hpp:25
Parameters for the LBFGSDirection class.
Definition lbfgs.hpp:12
Parameters for the LBFGS class.
Definition lbfgs.hpp:42
Parameters for the estimation of the Lipschitz constant of the gradient of the smooth term of the cos...
Definition lipschitz.hpp:12
Parameters for the NewtonTRDirection class.
Definition newton-tr.hpp:17
Tuning parameters for the PANOC algorithm.
Definition panoc-ocp.hpp:17
Tuning parameters for the PANOC algorithm.
Definition panoc.hpp:25
Tuning parameters for the PANTR algorithm.
Definition pantr.hpp:23
Parameters for SteihaugCG.
Parameters for the StructuredNewtonDirection class.
Parameters for the StructuredNewtonDirection class.
Tuning parameters for the ZeroFPR algorithm.
Definition zerofpr.hpp:24
#define ALPAQA_GETSET_PARAM_INST(...)
Definition json.cpp:268
#define ALPAQA_GETSET_PARAM_INST_INT(...)
Definition json.cpp:297
#define ALPAQA_GET_PARAM_INST(...)
Definition json.cpp:265
void get_param(const T &, json &j)
Get the first argument as a JSON object j.
Definition json.cpp:256
void get_param_default(const T &t, json &j)
Definition json.tpp:149
void set_param_default(T &t, const json &j)
Definition json.tpp:114
constexpr bool is_duration
Definition json.cpp:38
void set_param(T &, const json &j)
Update/overwrite the first argument based on the JSON object j.
Definition json.cpp:252
typename Conf::real_t real_t
Definition config.hpp:86
typename Conf::length_t length_t
Definition config.hpp:103
typename Conf::cmvec cmvec
Definition config.hpp:90
typename Conf::vec vec
Definition config.hpp:88
LBFGSStepSize
Which method to use to select the L-BFGS step size.
Definition lbfgs.hpp:26
Cautious BFGS update.
Definition lbfgs.hpp:18
Parameters for the StructuredLBFGSDirection class.
Custom parameter parsing exception.
Definition json.hpp:28