ATLAS Offline Software
Loading...
Searching...
No Matches
parse_json.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
6
7#define BOOST_BIND_GLOBAL_PLACEHOLDERS // Needed to silence Boost pragma message
8#include <boost/property_tree/ptree.hpp>
9#include <boost/property_tree/json_parser.hpp>
10#include <cassert>
11#include <string>
12#include <cmath> // for NAN
13#include <set>
14
15#include<iostream>
16
17namespace {
18 using namespace boost::property_tree;
19 using namespace lwtDev;
20 LayerConfig get_layer(const ptree::value_type& pt);
21 Input get_input(const ptree::value_type& pt);
22 InputNodeConfig get_input_node(const ptree::value_type& pt);
23 NodeConfig get_node(const ptree::value_type& pt);
24 OutputNodeConfig get_output_node(const ptree::value_type& v);
25 NodeConfig::Type get_node_type(const std::string&);
27 Activation get_activation_function(const std::string&);
28 Architecture get_architecture(const std::string&);
29 void set_defaults(LayerConfig& lc);
30 void add_dense_info(LayerConfig& lc, const ptree::value_type& pt);
31 void add_maxout_info(LayerConfig& lc, const ptree::value_type& pt);
32 void add_component_info(LayerConfig& lc, const ptree::value_type& pt);
33 void add_bidirectional_info(LayerConfig& lc, const ptree::value_type& pt);
34 void add_embedding_info(LayerConfig& lc, const ptree::value_type& pt);
35
36 std::map<std::string, double> get_defaults(const ptree& ptree);
37}
38
39
40namespace lwtDev {
41
42 JSONConfig parse_json(std::istream& json)
43 {
44 boost::property_tree::ptree pt;
45 boost::property_tree::read_json(json, pt);
46
47 JSONConfig cfg;
48 for (const auto& v: pt.get_child("inputs")) {
49 cfg.inputs.push_back(get_input(v));
50 }
51 for (const auto& v: pt.get_child("layers")) {
52 cfg.layers.push_back(get_layer(v));
53 }
54 for (const auto& v: pt.get_child("outputs"))
55 {
56 assert(v.first.empty()); // array elements have no names
57 cfg.outputs.push_back(v.second.data());
58 }
59 cfg.defaults = get_defaults(pt);
60 const std::string mname = "miscellaneous";
61 if (pt.count(mname)) {
62 for (const auto& misc: pt.get_child(mname)) {
63 cfg.miscellaneous.emplace(
64 misc.first, misc.second.get_value<std::string>());
65 }
66 }
67 return cfg;
68 }
69
70
72 boost::property_tree::ptree pt;
73 boost::property_tree::read_json(json, pt);
74
75 GraphConfig cfg;
76 for (const auto& v: pt.get_child("inputs")) {
77 cfg.inputs.push_back(get_input_node(v));
78 }
79 for (const auto& v: pt.get_child("input_sequences")) {
80 cfg.input_sequences.push_back(get_input_node(v));
81 }
82 for (const auto& v: pt.get_child("nodes")) {
83 cfg.nodes.push_back(get_node(v));
84 }
85 for (const auto& v: pt.get_child("layers")) {
86 cfg.layers.push_back(get_layer(v));
87 }
88 for (const auto& v: pt.get_child("outputs")) {
89 cfg.outputs.emplace(v.first, get_output_node(v));
90 }
91 return cfg;
92 }
93
94}
95
96namespace {
97
98 lwtDev::Input get_input(const ptree::value_type& v) {
99 std::string name = v.second.get<std::string>("name");
100 auto offset = v.second.get<double>("offset");
101 auto scale = v.second.get<double>("scale");
102 return {std::move(name), offset, scale};
103 }
104
105 lwtDev::InputNodeConfig get_input_node(const ptree::value_type& v) {
107 cfg.name = v.second.get<std::string>("name");
108 for (const auto& var: v.second.get_child("variables")) {
109 cfg.variables.push_back(get_input(var));
110 if (var.second.count("default")) {
111 std::string name = var.second.get<std::string>("name");
112 cfg.defaults.emplace(name, var.second.get<double>("default"));
113 }
114 }
115 return cfg;
116 }
117
118 const std::set<NodeConfig::Type> layerless_nodes {
119 NodeConfig::Type::CONCATENATE, NodeConfig::Type::SUM };
120 NodeConfig get_node(const ptree::value_type& v) {
122
123 for (const auto& source: v.second.get_child("sources")) {
124 int source_number = source.second.get_value<int>();
125 if (source_number < 0) {
126 throw std::logic_error("node source number must be positive");
127 }
128 cfg.sources.push_back(source_number);
129 }
130
131 cfg.type = get_node_type(v.second.get<std::string>("type"));
132 typedef NodeConfig::Type Type;
133 if (cfg.type == Type::INPUT || cfg.type == Type::INPUT_SEQUENCE) {
134 cfg.index = v.second.get<int>("size");
135 } else if (cfg.type == Type::FEED_FORWARD || cfg.type == Type::SEQUENCE ||
136 cfg.type == Type::TIME_DISTRIBUTED) {
137 cfg.index = v.second.get<int>("layer_index");
138 } else if (layerless_nodes.count(cfg.type)){
139 cfg.index = -1;
140 } else {
141 throw std::logic_error("unknown node type");
142 }
143 return cfg;
144 }
145
146 OutputNodeConfig get_output_node(const ptree::value_type& v) {
148 for (const auto& lab: v.second.get_child("labels")) {
149 cfg.labels.push_back(lab.second.get_value<std::string>());
150 }
151 int idx = v.second.get<int>("node_index");
152 if (idx < 0) throw std::logic_error("output node index is negative");
153 cfg.node_index = idx;
154 return cfg;
155 }
156
157 NodeConfig::Type get_node_type(const std::string& type) {
158 typedef NodeConfig::Type Type;
159 if (type == "feed_forward") return Type::FEED_FORWARD;
160 if (type == "sequence") return Type::SEQUENCE;
161 if (type == "input") return Type::INPUT;
162 if (type == "input_sequence") return Type::INPUT_SEQUENCE;
163 if (type == "concatenate") return Type::CONCATENATE;
164 if (type == "time_distributed") return Type::TIME_DISTRIBUTED;
165 if (type == "sum") return Type::SUM;
166 throw std::logic_error("no node type '" + type + "'");
167 }
168
169 LayerConfig get_layer(const ptree::value_type& v) {
170 using namespace lwtDev;
172 set_defaults(layer);
173 Architecture arch = get_architecture(
174 v.second.get<std::string>("architecture"));
175
176 if (arch == Architecture::DENSE) {
177 add_dense_info(layer, v);
178 } else if (arch == Architecture::NORMALIZATION) {
179 add_dense_info(layer, v); // re-use dense layer
180 } else if (arch == Architecture::MAXOUT) {
181 add_maxout_info(layer, v);
182 } else if (arch == Architecture::LSTM ||
183 arch == Architecture::GRU ||
184 arch == Architecture::HIGHWAY) {
185 add_component_info(layer, v);
186 } else if (arch == Architecture::BIDIRECTIONAL) {
187 add_bidirectional_info(layer, v);
188 } else if (arch == Architecture::EMBEDDING) {
189 add_embedding_info(layer, v);
190 } else {
191 throw std::logic_error("architecture not implemented");
192 }
193 layer.architecture = arch;
194 return layer;
195 }
196
198 // check if this is an "advanced" activation function, in which
199 // case it should store the values slightly differently
201 if (v.size() > 0) {
202 cfg.function = get_activation_function(
203 v.get<std::string>("function"));
204 cfg.alpha = v.get<double>("alpha");
205 } else {
206 cfg.function = get_activation_function(v.data());
207 cfg.alpha = NAN;
208 // special case: kerasfunc2json converter used to pass through
209 // the elu activation function. For cases where this has been
210 // saved in JSON files we assume an alpha parameter of 1.
211 if (cfg.function == Activation::ELU) {
212 cfg.alpha = 1.0;
213 }
214 }
215 return cfg;
216 }
217
218 lwtDev::Activation get_activation_function(const std::string& str) {
219 using namespace lwtDev;
220 if (str == "linear") return Activation::LINEAR;
221 if (str == "sigmoid") return Activation::SIGMOID;
222 if (str == "rectified") return Activation::RECTIFIED;
223 if (str == "softmax") return Activation::SOFTMAX;
224 if (str == "tanh") return Activation::TANH;
225 if (str == "hard_sigmoid") return Activation::HARD_SIGMOID;
226 if (str == "elu") return Activation::ELU;
227 if (str == "leakyrelu") return Activation::LEAKY_RELU;
228 if (str == "swish") return Activation::SWISH;
229 if (str == "abs") return Activation::ABS;
230 throw std::logic_error("activation function " + str + " not recognized");
231 return Activation::LINEAR;
232 }
233
234
235 lwtDev::Architecture get_architecture(const std::string& str) {
236 using namespace lwtDev;
237 if (str == "dense") return Architecture::DENSE;
238 if (str == "normalization") return Architecture::NORMALIZATION;
239 if (str == "highway") return Architecture::HIGHWAY;
240 if (str == "maxout") return Architecture::MAXOUT;
241 if (str == "lstm") return Architecture::LSTM;
242 if (str == "gru") return Architecture::GRU;
243 if (str == "bidirectional") return Architecture::BIDIRECTIONAL;
244 if (str == "embedding") return Architecture::EMBEDDING;
245 throw std::logic_error("architecture " + str + " not recognized");
246 }
247
248 void set_defaults(LayerConfig& layer) {
249 layer.activation.function = Activation::NONE;
250 layer.inner_activation.function = Activation::NONE;
251 layer.architecture = Architecture::NONE;
252 }
253
254 void add_dense_info(LayerConfig& layer, const ptree::value_type& v) {
255 for (const auto& wt: v.second.get_child("weights")) {
256 layer.weights.push_back(wt.second.get_value<double>());
257 }
258 for (const auto& bs: v.second.get_child("bias")) {
259 layer.bias.push_back(bs.second.get_value<double>());
260 }
261 // this last category is currently only used for LSTM
262 if (v.second.count("U") != 0) {
263 for (const auto& wt: v.second.get_child("U") ) {
264 layer.U.push_back(wt.second.get_value<double>());
265 }
266 }
267
268 if (v.second.count("activation") != 0) {
269 layer.activation = get_activation(v.second.get_child("activation"));
270 }
271
272 }
273
274 void add_maxout_info(LayerConfig& layer, const ptree::value_type& v) {
275 using namespace lwtDev;
276 for (const auto& sub: v.second.get_child("sublayers")) {
277 LayerConfig sublayer;
278 set_defaults(sublayer);
279 add_dense_info(sublayer, sub);
280 layer.sublayers.push_back(std::move(sublayer));
281 }
282 }
283
284
285 const std::map<std::string, lwtDev::Component> component_map {
286 {"i", Component::I},
287 {"o", Component::O},
288 {"c", Component::C},
289 {"f", Component::F},
290 {"z", Component::Z},
291 {"r", Component::R},
292 {"h", Component::H},
293 {"t", Component::T},
294 {"carry", Component::CARRY}
295 };
296
297 void add_component_info(LayerConfig& layer, const ptree::value_type& v) {
298 using namespace lwtDev;
299 for (const auto& comp: v.second.get_child("components")) {
301 set_defaults(cfg);
302 add_dense_info(cfg, comp);
303 layer.components[component_map.at(comp.first)] = std::move(cfg);
304 }
305 layer.activation = get_activation(v.second.get_child("activation"));
306 layer.go_backwards = false;
307 if (v.second.count("return_sequence") != 0)
308 layer.return_sequence = v.second.get<bool>("return_sequence");
309 if (v.second.count("go_backwards") != 0)
310 layer.go_backwards = v.second.get<bool>("go_backwards");
311 if (v.second.count("inner_activation") != 0) {
312 layer.inner_activation = get_activation(
313 v.second.get_child("inner_activation"));
314 }
315 }
316
317 void add_bidirectional_info(LayerConfig& layer, const ptree::value_type& v) {
318 using namespace lwtDev;
319 set_defaults(layer);
320 LayerConfig forward_layer;
321 LayerConfig backward_layer;
322 for(const auto& val: v.second){
323 if(val.first == "forward_layer"){
324 add_component_info(forward_layer, val);
325 forward_layer.architecture = get_architecture(val.second.get<std::string>("architecture"));
326 }
327 if(val.first == "backward_layer"){
328 add_component_info(backward_layer, val);
329 backward_layer.architecture = get_architecture(val.second.get<std::string>("architecture"));
330 }
331 }
332 layer.sublayers.push_back(std::move(forward_layer));
333 layer.sublayers.push_back(std::move(backward_layer));
334 layer.return_sequence = v.second.get<bool>("return_sequence");
335 layer.merge_mode = v.second.get<std::string>("merge_mode");
336 layer.architecture = Architecture::BIDIRECTIONAL;
337 }
338
339 void add_embedding_info(LayerConfig& layer, const ptree::value_type& v) {
340 using namespace lwtDev;
341 for (const auto& sub: v.second.get_child("sublayers")) {
342 EmbeddingConfig emb;
343 for (const auto& wt: sub.second.get_child("weights")) {
344 emb.weights.push_back(wt.second.get_value<double>());
345 }
346 emb.index = sub.second.get<int>("index");
347 emb.n_out = sub.second.get<int>("n_out");
348 layer.embedding.push_back(std::move(emb));
349 }
350 }
351
352 std::map<std::string, double> get_defaults(const ptree& pt) {
353 const std::string dname = "defaults";
354 std::map<std::string, double> defaults;
355 // NOTE: at some point we may deprecate this first way of storing
356 // default values.
357 if (pt.count(dname)) {
358 for (const auto& def: pt.get_child(dname)) {
359 defaults.emplace(def.first, def.second.get_value <double>());
360 }
361 } else {
362 const std::string dkey = "default";
363 for (const auto& v: pt.get_child("inputs")) {
364 if (v.second.count(dkey)) {
365 std::string key = v.second.get<std::string>("name");
366 defaults.emplace(key, v.second.get<double>(dkey));
367 }
368 }
369 }
370 return defaults;
371 }
372}
nlohmann::json json
boost::property_tree::ptree ptree
OutputNodeConfig get_output_node(const nlohmann::json &v)
InputNodeConfig get_input_node(const nlohmann::json &v)
@ layer
Definition HitInfo.h:79
GraphConfig parse_json_graph(std::istream &json)
JSONConfig parse_json(std::istream &json)
std::function< double(double)> get_activation(lwtDev::ActivationConfig act)
Definition Stack.cxx:671
std::vector< double > weights
Architecture architecture