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