alpaqa 1.0.0a14
Nonconvex constrained optimization
Loading...
Searching...
No Matches
json.cpp
Go to the documentation of this file.
2
8#include <algorithm>
9#include <cmath>
10#include <concepts>
11#include <fstream>
12#include <limits>
13
25#include <alpaqa/outer/alm.hpp>
26#if ALPAQA_WITH_OCP
28#endif
29
30namespace alpaqa::params {
31
32template <class T>
33inline constexpr bool is_duration = false;
34template <class Rep, class Period>
35inline constexpr bool is_duration<std::chrono::duration<Rep, Period>> = true;
36
37template <class Duration>
39void set_param(Duration &t, const json &j) {
40 if (!j.is_string())
42 "Invalid value " + to_string(j) + " for type '" +
43 demangled_typename(typeid(Duration)) + "' (expected a string)");
44 std::string value = j; // keep outside of try block
45 try {
46 util::parse_duration(t = {}, value);
47 } catch (util::invalid_duration_value &e) {
49 "Invalid value '" + value + "' for type '" +
50 demangled_typename(typeid(Duration)) + "': error at '" +
51 std::string(std::string_view(value.data(), e.result.ptr)));
52 } catch (util::invalid_duration_units &e) {
54 "Invalid units '" + std::string(e.units) + "' for type '" +
55 demangled_typename(typeid(Duration)) + "' in '" + value + "'");
56 }
57}
58
59template <class Duration>
61void get_param(const Duration &t, json &s) {
62 namespace chr = std::chrono;
63 auto dur = t;
64 std::string result;
65 if (dur.count() == 0) {
66 result = "0";
67 } else {
68 if (auto d = duration_cast<chr::hours>(dur); d.count() != 0) {
69 result += std::to_string(d.count()) + "h";
71 }
72 if (auto d = duration_cast<chr::minutes>(dur); d.count() != 0) {
73 result += std::to_string(d.count()) + "min";
75 }
76 if (auto d = duration_cast<chr::seconds>(dur); d.count() != 0) {
77 result += std::to_string(d.count()) + "s";
79 }
80 if (auto d = duration_cast<chr::milliseconds>(dur); d.count() != 0) {
81 result += std::to_string(d.count()) + "ms";
83 }
84 if (auto d = duration_cast<chr::microseconds>(dur); d.count() != 0) {
85 result += std::to_string(d.count()) + "µs";
87 }
88 if (auto d = duration_cast<chr::nanoseconds>(dur); d.count() != 0) {
89 result += std::to_string(d.count()) + "ns";
91 }
92 }
93 s = std::move(result);
94}
95
96template <>
98 if (!j.is_array())
99 throw invalid_json_param("Invalid value " + to_string(j) +
100 " for type '" + demangled_typename(typeid(v)) +
101 "' (expected an array, but got " +
102 j.type_name() + ')');
103 v.resize(static_cast<length_t<config_t>>(j.size()));
104 auto convert = [](const json &j) -> real_t<config_t> {
105 try {
106 return j;
107 } catch (json::exception &e) {
108 throw invalid_json_param("Invalid vector element " + to_string(j) +
109 " (expected a number, but got " +
110 j.type_name() + ')');
111 }
112 };
113 std::ranges::transform(j, v.begin(), convert);
114}
115
116template <>
118 if (j.is_string()) {
119 std::string fpath{j};
120 std::ifstream f(fpath);
121 if (!f)
122 throw invalid_json_param("Unable to open file '" + fpath +
123 "' for type '" +
124 demangled_typename(typeid(v)));
125 try {
126 auto r = alpaqa::csv::read_row_std_vector<real_t<config_t>>(f);
127 auto r_size = static_cast<length_t<config_t>>(r.size());
128 if (v.expected_size >= 0 && r_size != v.expected_size)
129 throw invalid_json_param(
130 "Incorrect size in '" + fpath + "' (expected " +
131 std::to_string(v.expected_size) + ", but got " +
132 std::to_string(r.size()) + ")");
133 v.value.emplace(cmvec<config_t>{r.data(), r_size});
134 } catch (alpaqa::csv::read_error &e) {
135 throw invalid_json_param("Unable to read from file '" + fpath +
136 "': alpaqa::csv::read_error: " + e.what());
137 }
138 } else if (j.is_array()) {
139 alpaqa::params::set_param(v.value.emplace(), j);
140 if (v.expected_size >= 0 && v.value->size() != v.expected_size)
141 throw invalid_json_param(
142 "Incorrect size in " + to_string(j) + "' (expected " +
143 std::to_string(v.expected_size) + ", but got " +
144 std::to_string(v.value->size()) + ')');
145 } else {
146 throw invalid_json_param("Invalid value " + to_string(j) +
147 " for type '" + demangled_typename(typeid(v)) +
148 "' (expected string or array, but got " +
149 j.type_name() + ')');
150 }
151}
152
153template <>
154void ALPAQA_EXPORT set_param(bool &t, const nlohmann::json &j) {
155 if (!j.is_boolean())
156 throw invalid_json_param("Invalid value " + to_string(j) +
157 " for type '" + demangled_typename(typeid(t)) +
158 "' (expected boolean, but got " +
159 j.type_name() + ')');
160 t = j;
161}
162
163template <>
164void ALPAQA_EXPORT set_param(std::string &t, const nlohmann::json &j) {
165 if (!j.is_string())
166 throw invalid_json_param("Invalid value " + to_string(j) +
167 " for type '" + demangled_typename(typeid(t)) +
168 "' (expected string, but got " +
169 j.type_name() + ')');
170 t = j;
171}
172
173template <std::integral T>
174 requires(!std::same_as<T, bool>)
175void set_param(T &t, const nlohmann::json &j) {
176 if (std::unsigned_integral<T> && !j.is_number_unsigned())
177 throw invalid_json_param("Invalid value " + to_string(j) +
178 " for type '" + demangled_typename(typeid(T)) +
179 "' (expected unsigned integer, but got " +
180 j.type_name() + ')');
181 if (!j.is_number_integer())
182 throw invalid_json_param("Invalid value " + to_string(j) +
183 " for type '" + demangled_typename(typeid(T)) +
184 "' (expected integer, but got " +
185 j.type_name() + ')');
186 t = j;
187}
188
189template <std::floating_point T>
190void set_param(T &t, const nlohmann::json &j) {
191 if (j.is_string()) {
192 if (j == "nan") {
193 t = std::numeric_limits<T>::quiet_NaN();
194 } else if (j == "inf" || j == "+inf") {
195 t = std::numeric_limits<T>::infinity();
196 } else if (j == "-inf") {
197 t = -std::numeric_limits<T>::infinity();
198 } else {
199 throw invalid_json_param("Invalid value " + to_string(j) +
200 " for type '" +
201 demangled_typename(typeid(T)) +
202 "' (expected number or any of "
203 "\"nan\", \"inf\", \"+inf\", \"-inf\")");
204 }
205 } else if (j.is_number()) {
206 t = j;
207 } else {
208 throw invalid_json_param("Invalid value " + to_string(j) +
209 " for type '" + demangled_typename(typeid(T)) +
210 "' (expected number, but got " +
211 j.type_name() + ')');
212 }
213}
214
215template <class T>
216 requires(std::integral<T> || std::same_as<T, bool> ||
217 std::same_as<T, std::string>)
218void get_param(const T &t, nlohmann::json &j) {
219 j = t;
220}
221
222template <std::floating_point T>
223void get_param(const T &t, nlohmann::json &j) {
224 if (std::isnan(t))
225 j = "nan";
226 else if (t == +std::numeric_limits<T>::infinity())
227 j = "inf";
228 else if (t == -std::numeric_limits<T>::infinity())
229 j = "-inf";
230 else
231 j = t;
232}
233
234template <>
235void ALPAQA_EXPORT set_param(LBFGSStepSize &t, const json &j) {
236 if (j == "BasedOnExternalStepSize")
238 else if (j == "BasedOnCurvature")
240 else
241 throw invalid_json_param("Invalid value " + to_string(j) +
242 " for type 'LBFGSStepSize'");
243}
244
245template <>
246void ALPAQA_EXPORT get_param(const LBFGSStepSize &t, json &s) {
247 s = enum_name(t);
248}
249
250template <>
251void ALPAQA_EXPORT set_param(PANOCStopCrit &t, const json &j) {
252 if (j == "ApproxKKT")
254 else if (j == "ApproxKKT2")
256 else if (j == "ProjGradNorm")
258 else if (j == "ProjGradNorm2")
260 else if (j == "ProjGradUnitNorm")
262 else if (j == "ProjGradUnitNorm2")
264 else if (j == "FPRNorm")
266 else if (j == "FPRNorm2")
268 else if (j == "Ipopt")
270 else if (j == "LBFGSBpp")
272 else
273 throw invalid_json_param("Invalid value " + to_string(j) +
274 " for type 'PANOCStopCrit'");
275}
276
277template <>
278void ALPAQA_EXPORT get_param(const PANOCStopCrit &t, json &s) {
279 s = enum_name(t);
280}
281
283
284template <class... Ts>
285void set_param(util::detail::dummy<Ts...> &, const json &) {}
286template <class... Ts>
287void get_param(const util::detail::dummy<Ts...> &, json &) {}
288
289#define ALPAQA_GET_PARAM_INST(...) \
290 template void ALPAQA_EXPORT get_param( \
291 const util::possible_alias_t<__VA_ARGS__> &, json &)
292#define ALPAQA_GETSET_PARAM_INST(...) \
293 template void ALPAQA_EXPORT set_param( \
294 util::possible_alias_t<__VA_ARGS__> &, const json &); \
295 template void ALPAQA_EXPORT get_param( \
296 const util::possible_alias_t<__VA_ARGS__> &, json &)
297
299
301
304ALPAQA_GETSET_PARAM_INST(long double, double, float);
305
314
315// Here, we would like to instantiate alpaqa::params::set_param for all standard
316// integer types, but the issue is that they might not be distinct types:
317// For example, on some platforms, int32_t might be a weak alias to int, whereas
318// on other platforms, it could be a distinct type.
319// To resolve this issue, we use some metaprogramming to ensure distinct
320// instantiations with unique dummy types.
321#define ALPAQA_GETSET_PARAM_INST_INT(...) \
322 ALPAQA_GETSET_PARAM_INST(__VA_ARGS__, int8_t, uint8_t, int16_t, uint16_t, \
323 int32_t, int64_t, uint32_t, uint64_t)
324
328ALPAQA_GETSET_PARAM_INST_INT(long long, long, int, short);
329ALPAQA_GETSET_PARAM_INST_INT(ptrdiff_t, long long, long, int, short);
331ALPAQA_GETSET_PARAM_INST_INT(unsigned int, unsigned short);
332ALPAQA_GETSET_PARAM_INST_INT(unsigned long, unsigned int, unsigned short);
333ALPAQA_GETSET_PARAM_INST_INT(unsigned long long, unsigned long, unsigned int,
334 unsigned short);
335ALPAQA_GETSET_PARAM_INST_INT(size_t, unsigned long long, unsigned long,
336 unsigned int, unsigned short);
337
338ALPAQA_GETSET_PARAM_INST(std::chrono::nanoseconds);
339ALPAQA_GETSET_PARAM_INST(std::chrono::microseconds);
340ALPAQA_GETSET_PARAM_INST(std::chrono::milliseconds);
341ALPAQA_GETSET_PARAM_INST(std::chrono::seconds);
342ALPAQA_GETSET_PARAM_INST(std::chrono::minutes);
343ALPAQA_GETSET_PARAM_INST(std::chrono::hours);
344
361#if ALPAQA_WITH_OCP
363#endif
364
365} // namespace alpaqa::params
std::string demangled_typename(const std::type_info &t)
Get the pretty name of the given type as a string.
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
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:292
#define ALPAQA_GETSET_PARAM_INST_INT(...)
Definition json.cpp:321
#define ALPAQA_GET_PARAM_INST(...)
Definition json.cpp:289
void get_param(const T &t, json &s)
Get the first argument as a JSON object j.
Definition json.tpp:85
constexpr bool is_duration
Definition json.cpp:33
void set_param(T &t, const json &j)
Update/overwrite the first argument based on the JSON object j.
Definition json.tpp:24
Unused unique type tag for template specializations that were rejected because some types were not di...
void parse_duration(std::chrono::duration< Rep, Period > &t, std::string_view s)
Adds the sum of the durations in the string s to the duration t.
@ LBFGSBpp
The stopping criterion used by LBFGS++, see https://lbfgspp.statr.me/doc/classLBFGSpp_1_1LBFGSBParam....
@ ProjGradUnitNorm
∞-norm of the projected gradient with unit step size:
@ ProjGradNorm
∞-norm of the projected gradient with step size γ:
@ Ipopt
The stopping criterion used by Ipopt, see https://link.springer.com/article/10.1007/s10107-004-0559-y...
@ FPRNorm2
2-norm of fixed point residual:
@ ProjGradNorm2
2-norm of the projected gradient with step size γ:
@ ApproxKKT
Find an ε-approximate KKT point in the ∞-norm:
@ FPRNorm
∞-norm of fixed point residual:
@ ApproxKKT2
Find an ε-approximate KKT point in the 2-norm:
@ ProjGradUnitNorm2
2-norm of the projected gradient with unit step size:
typename Conf::real_t real_t
Definition config.hpp:65
typename Conf::length_t length_t
Definition config.hpp:76
typename Conf::cmvec cmvec
Definition config.hpp:68
constexpr const auto inf
Definition config.hpp:85
typename Conf::vec vec
Definition config.hpp:66
constexpr const char * enum_name(LBFGSStepSize s)
Definition lbfgs.hpp:229
LBFGSStepSize
Which method to use to select the L-BFGS step size.
Definition lbfgs.hpp:26
@ BasedOnCurvature
Initial inverse Hessian approximation is set to .
@ BasedOnExternalStepSize
Initial inverse Hessian approximation is set to , where is the forward-backward splitting step size.
Cautious BFGS update.
Definition lbfgs.hpp:18
Parameters for the StructuredLBFGSDirection class.
Custom parameter parsing exception.
Definition json.hpp:33