alpaqa develop
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#include <variant>
14
27#include <alpaqa/outer/alm.hpp>
29#if ALPAQA_WITH_OCP
31#endif
32
33namespace alpaqa::params {
34
35template <class T>
36inline constexpr bool is_duration = false;
37template <class Rep, class Period>
38inline constexpr bool is_duration<std::chrono::duration<Rep, Period>> = true;
39
40template <class Duration>
42void set_param_default(Duration &t, const json &j) {
43 if (!j.is_string())
45 "Invalid value " + to_string(j) + " for type '" +
46 demangled_typename(typeid(Duration)) + "' (expected a string)");
47 std::string value = j; // keep outside of try block
48 try {
49 util::parse_duration(t = {}, value);
50 } catch (util::invalid_duration_value &e) {
52 "Invalid value '" + value + "' for type '" +
53 demangled_typename(typeid(Duration)) + "': error at '" +
54 std::string(std::string_view(value.data(), e.result.ptr)));
55 } catch (util::invalid_duration_units &e) {
57 "Invalid units '" + std::string(e.units) + "' for type '" +
58 demangled_typename(typeid(Duration)) + "' in '" + value + "'");
59 }
60}
61
62template <class Duration>
64void get_param_default(const Duration &t, json &s) {
65 namespace chr = std::chrono;
66 auto dur = t;
67 std::string result;
68 if (dur.count() == 0) {
69 result = "0";
70 } else {
71 if (auto d = duration_cast<chr::hours>(dur); d.count() != 0) {
72 result += std::to_string(d.count()) + "h";
74 }
75 if (auto d = duration_cast<chr::minutes>(dur); d.count() != 0) {
76 result += std::to_string(d.count()) + "min";
78 }
79 if (auto d = duration_cast<chr::seconds>(dur); d.count() != 0) {
80 result += std::to_string(d.count()) + "s";
82 }
83 if (auto d = duration_cast<chr::milliseconds>(dur); d.count() != 0) {
84 result += std::to_string(d.count()) + "ms";
86 }
87 if (auto d = duration_cast<chr::microseconds>(dur); d.count() != 0) {
88 result += std::to_string(d.count()) + "µs";
90 }
91 if (auto d = duration_cast<chr::nanoseconds>(dur); d.count() != 0) {
92 result += std::to_string(d.count()) + "ns";
94 }
95 }
96 s = std::move(result);
97}
98
99template <>
101 if (!j.is_array())
102 throw invalid_json_param("Invalid value " + to_string(j) +
103 " for type '" + demangled_typename(typeid(v)) +
104 "' (expected an array, but got " +
105 j.type_name() + ')');
106 v.resize(static_cast<length_t<config_t>>(j.size()));
107 auto convert = [](const json &j) -> real_t<config_t> {
108 try {
109 return j;
110 } catch (json::exception &e) {
111 throw invalid_json_param("Invalid vector element " + to_string(j) +
112 " (expected a number, but got " +
113 j.type_name() + "): " + e.what());
114 }
115 };
116 std::ranges::transform(j, v.begin(), convert);
117}
118
119template <>
121 if (j.is_string()) {
122 std::string fpath{j};
123 std::ifstream f(fpath);
124 if (!f)
125 throw invalid_json_param("Unable to open file '" + fpath +
126 "' for type '" +
127 demangled_typename(typeid(v)));
128 try {
129 auto r = alpaqa::csv::read_row_std_vector<real_t<config_t>>(f);
130 auto r_size = static_cast<length_t<config_t>>(r.size());
131 if (v.expected_size >= 0 && r_size != v.expected_size)
132 throw invalid_json_param(
133 "Incorrect size in '" + fpath + "' (expected " +
134 std::to_string(v.expected_size) + ", but got " +
135 std::to_string(r.size()) + ")");
136 v.value.emplace(cmvec<config_t>{r.data(), r_size});
137 } catch (alpaqa::csv::read_error &e) {
138 throw invalid_json_param("Unable to read from file '" + fpath +
139 "': alpaqa::csv::read_error: " + e.what());
140 }
141 } else if (j.is_array()) {
142 alpaqa::params::set_param(v.value.emplace(), j);
143 if (v.expected_size >= 0 && v.value->size() != v.expected_size)
144 throw invalid_json_param(
145 "Incorrect size in " + to_string(j) + "' (expected " +
146 std::to_string(v.expected_size) + ", but got " +
147 std::to_string(v.value->size()) + ')');
148 } else {
149 throw invalid_json_param("Invalid value " + to_string(j) +
150 " for type '" + demangled_typename(typeid(v)) +
151 "' (expected string or array, but got " +
152 j.type_name() + ')');
153 }
154}
155
156template <>
157void ALPAQA_EXPORT set_param(std::monostate &, const nlohmann::json &) {
158 throw invalid_json_param("Cannot set value of std::monostate");
159}
160
161template <>
162void ALPAQA_EXPORT set_param(bool &t, const nlohmann::json &j) {
163 if (!j.is_boolean())
164 throw invalid_json_param("Invalid value " + to_string(j) +
165 " for type '" + demangled_typename(typeid(t)) +
166 "' (expected boolean, but got " +
167 j.type_name() + ')');
168 t = static_cast<bool>(j);
169}
170
171template <>
172void ALPAQA_EXPORT set_param(std::string &t, const nlohmann::json &j) {
173 if (!j.is_string())
174 throw invalid_json_param("Invalid value " + to_string(j) +
175 " for type '" + demangled_typename(typeid(t)) +
176 "' (expected string, but got " +
177 j.type_name() + ')');
178 t = static_cast<std::string>(j);
179}
180
181template <std::integral T>
182 requires(!std::same_as<T, bool>)
183void set_param_default(T &t, const nlohmann::json &j) {
184 if (std::unsigned_integral<T> && !j.is_number_unsigned())
185 throw invalid_json_param("Invalid value " + to_string(j) +
186 " for type '" + demangled_typename(typeid(T)) +
187 "' (expected unsigned integer, but got " +
188 j.type_name() + ')');
189 if (!j.is_number_integer())
190 throw invalid_json_param("Invalid value " + to_string(j) +
191 " for type '" + demangled_typename(typeid(T)) +
192 "' (expected integer, but got " +
193 j.type_name() + ')');
194 t = static_cast<T>(j);
195}
196
197template <std::floating_point T>
198void set_param_default(T &t, const nlohmann::json &j) {
199 if (j.is_string()) {
200 if (j == "nan") {
201 t = std::numeric_limits<T>::quiet_NaN();
202 } else if (j == "inf" || j == "+inf") {
203 t = std::numeric_limits<T>::infinity();
204 } else if (j == "-inf") {
205 t = -std::numeric_limits<T>::infinity();
206 } else {
207 throw invalid_json_param("Invalid value " + to_string(j) +
208 " for type '" +
209 demangled_typename(typeid(T)) +
210 "' (expected number or any of "
211 "\"nan\", \"inf\", \"+inf\", \"-inf\")");
212 }
213 } else if (j.is_number()) {
214 t = static_cast<T>(j);
215 } else {
216 throw invalid_json_param("Invalid value " + to_string(j) +
217 " for type '" + demangled_typename(typeid(T)) +
218 "' (expected number, but got " +
219 j.type_name() + ')');
220 }
221}
222
223template <class T>
224 requires(std::integral<T> || std::same_as<T, bool> ||
225 std::same_as<T, std::string>)
226void get_param_default(const T &t, nlohmann::json &j) {
227 j = t;
228}
229
230template <std::floating_point T>
231void get_param_default(const T &t, nlohmann::json &j) {
232 if (std::isnan(t))
233 j = "nan";
234 else if (t == +std::numeric_limits<T>::infinity())
235 j = "inf";
236 else if (t == -std::numeric_limits<T>::infinity())
237 j = "-inf";
238 else
239 j = t;
240}
241
243
244// Because of the new name mangling for concepts (https://reviews.llvm.org/D147655)
245// we need this to be an unconstrained function template. The actual constraints
246// are in set_param_default, which is not exported. Fully specialized
247// instantiations of set_param are still allowed.
248template <class T>
249void set_param(T &t, const json &j) {
251}
252template <class T>
253void get_param(const T &t, json &j) {
255}
256
257template <class... Ts>
258void set_param(util::detail::dummy<Ts...> &, const json &) {}
259template <class... Ts>
260void get_param(const util::detail::dummy<Ts...> &, json &) {}
261
262#define ALPAQA_GET_PARAM_INST(...) \
263 template void ALPAQA_EXPORT get_param( \
264 const util::possible_alias_t<__VA_ARGS__> &, json &)
265#define ALPAQA_GETSET_PARAM_INST(...) \
266 template void ALPAQA_EXPORT set_param( \
267 util::possible_alias_t<__VA_ARGS__> &, const json &); \
268 template void ALPAQA_EXPORT get_param( \
269 const util::possible_alias_t<__VA_ARGS__> &, json &)
270
272
274
277ALPAQA_GETSET_PARAM_INST(long double, double, float);
278
287
288// Here, we would like to instantiate alpaqa::params::set_param for all standard
289// integer types, but the issue is that they might not be distinct types:
290// For example, on some platforms, int32_t might be a weak alias to int, whereas
291// on other platforms, it could be a distinct type.
292// To resolve this issue, we use some metaprogramming to ensure distinct
293// instantiations with unique dummy types.
294#define ALPAQA_GETSET_PARAM_INST_INT(...) \
295 ALPAQA_GETSET_PARAM_INST(__VA_ARGS__, int8_t, uint8_t, int16_t, uint16_t, \
296 int32_t, int64_t, uint32_t, uint64_t)
297
301ALPAQA_GETSET_PARAM_INST_INT(long long, long, int, short);
302ALPAQA_GETSET_PARAM_INST_INT(ptrdiff_t, long long, long, int, short);
304ALPAQA_GETSET_PARAM_INST_INT(unsigned int, unsigned short);
305ALPAQA_GETSET_PARAM_INST_INT(unsigned long, unsigned int, unsigned short);
306ALPAQA_GETSET_PARAM_INST_INT(unsigned long long, unsigned long, unsigned int,
307 unsigned short);
308ALPAQA_GETSET_PARAM_INST_INT(size_t, unsigned long long, unsigned long,
309 unsigned int, unsigned short);
310
311ALPAQA_GETSET_PARAM_INST(std::chrono::nanoseconds);
312ALPAQA_GETSET_PARAM_INST(std::chrono::microseconds);
313ALPAQA_GETSET_PARAM_INST(std::chrono::milliseconds);
314ALPAQA_GETSET_PARAM_INST(std::chrono::seconds);
315ALPAQA_GETSET_PARAM_INST(std::chrono::minutes);
316ALPAQA_GETSET_PARAM_INST(std::chrono::hours);
317
339#if ALPAQA_WITH_OCP
341#endif
342
343} // 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
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:265
#define ALPAQA_GETSET_PARAM_INST_INT(...)
Definition json.cpp:294
#define ALPAQA_GET_PARAM_INST(...)
Definition json.cpp:262
void get_param(const T &, json &j)
Get the first argument as a JSON object j.
Definition json.cpp:253
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:36
void set_param(T &, const json &j)
Update/overwrite the first argument based on the JSON object j.
Definition json.cpp:249
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.
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
constexpr const auto inf
Definition config.hpp:112
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
Flags to be passed to dlopen.
Definition dl-flags.hpp:8
Parameters for the StructuredLBFGSDirection class.
Custom parameter parsing exception.
Definition json.hpp:28