10std::string strip_optional_quotes(std::string s) {
12 const char first =
s.front();
13 const char last =
s.back();
15 if ((first ==
'"' && last ==
'"') ||
16 (first ==
'\'' && last ==
'\'')) {
17 return s.substr(1,
s.size() - 2);
23bool is_numeric_string(
const std::string& s) {
24 if (
s.empty())
return false;
26 std::strtod(
s.c_str(), &end);
27 return end !=
s.c_str() && *end ==
'\0';
30bool looks_like_nuisance_entry(
const std::shared_ptr<DBNode>& node) {
32 && node->contains(
"block")
33 && node->contains(
"code");
37 if (std::holds_alternative<BlockName>(value))
return "string";
38 if (std::holds_alternative<int>(value))
return "int";
39 if (std::holds_alternative<double>(value))
return "double";
40 if (std::holds_alternative<bool>(value))
return "bool";
41 if (std::holds_alternative<std::shared_ptr<DBNode>>(value))
return "node";
42 if (std::holds_alternative<std::vector<std::shared_ptr<DBNode>>>(value))
return "list";
46void collect_entry_nodes_from_value(
const DBNode::Value& value,
47 std::vector<std::shared_ptr<DBNode>>& out);
49void collect_entry_nodes_from_node(
const std::shared_ptr<DBNode>& node,
50 std::vector<std::shared_ptr<DBNode>>& out)
56 if (looks_like_nuisance_entry(node)) {
61 for (
const auto& key : node->
get_keys()) {
62 const auto child = node->get(key);
63 collect_entry_nodes_from_value(child, out);
67void collect_entry_nodes_from_value(
const DBNode::Value& value,
68 std::vector<std::shared_ptr<DBNode>>& out)
70 if (std::holds_alternative<std::vector<std::shared_ptr<DBNode>>>(value)) {
71 const auto& entries = std::get<std::vector<std::shared_ptr<DBNode>>>(value);
72 for (
const auto& entry : entries) {
73 collect_entry_nodes_from_node(entry, out);
78 if (std::holds_alternative<std::shared_ptr<DBNode>>(value)) {
79 collect_entry_nodes_from_node(std::get<std::shared_ptr<DBNode>>(value), out);
84std::vector<std::shared_ptr<DBNode>> extract_nuisance_entries(
const DBNode::Value& value) {
85 std::vector<std::shared_ptr<DBNode>> out;
86 collect_entry_nodes_from_value(value, out);
93 : paths_provider_(
std::move(paths_provider))
95 if (!paths_provider_) {
96 throw std::invalid_argument(
"NuisanceReader: paths_provider is null");
101 return paths_provider_->default_nuisances_path();
105 return paths_provider_->user_nuisances_path();
113 throw std::runtime_error(
"NuisanceReader: default path is empty");
115 if (!fs::exists(path)) {
116 throw std::runtime_error(
"NuisanceReader: default file not found: " + path.string());
119 merge_file_into_registry(path, registry);
134 if (!fs::exists(path)) {
135 throw std::runtime_error(
"NuisanceReader: user file not found: " + path.string());
138 merge_file_into_registry(path, registry);
146 for (
const auto& [pid, spec] : user_registry) {
147 registry[pid] = spec;
154void NuisanceReader::merge_file_into_registry(
const fs::path& path,
159 throw std::runtime_error(
"NuisanceReader: could not create provider for: " + path.string());
162 auto root = provider->provide_db_as_node();
164 throw std::runtime_error(
"NuisanceReader: provider returned null DBNode for: " + path.string());
167 merge_node_into_registry(*root, registry);
170void NuisanceReader::merge_node_into_registry(
const DBNode& root,
177 const auto nuisances_value = root.
get(
"nuisances");
178 const auto entries = extract_nuisance_entries(nuisances_value);
187 for (
const auto& entry_ptr : entries) {
193 registry[spec.
param_id] = std::move(spec);
200 spec.
param_id = make_param_id(entry);
202 const double min_val = value_to_double(
203 get_required_value(entry, {
"min_val",
"min"}),
207 const double max_val = value_to_double(
208 get_required_value(entry, {
"max_val",
"max"}),
212 if (min_val > max_val) {
213 std::ostringstream oss;
214 oss <<
"NuisanceReader: invalid bounds for parameter (min_val="
215 << min_val <<
" > max_val=" << max_val <<
")";
216 throw std::runtime_error(oss.str());
219 spec.
bounds = {min_val, max_val};
221 const std::string distribution = value_to_string(
222 get_required_value(entry, {
"distribution",
"marginal"}),
226 spec.
marginal = parse_marginal_type(distribution);
232 const std::string block_str = value_to_string(
233 get_required_value(entry, {
"block"}),
237 const std::string code_str = value_to_code_string(
238 get_required_value(entry, {
"code"}),
243 LhaID code(code_str);
249 std::initializer_list<const char*> candidate_keys)
251 for (
const char* key : candidate_keys) {
253 return node.
get(key);
257 std::ostringstream oss;
258 oss <<
"NuisanceReader: missing required field among {";
260 for (
const char* key : candidate_keys) {
261 if (!first) oss <<
", ";
267 throw std::runtime_error(oss.str());
270std::string NuisanceReader::value_to_string(
const DBNode::Value& value,
271 const std::string& field_name)
273 if (std::holds_alternative<BlockName>(value)) {
274 return strip_optional_quotes(std::get<BlockName>(value));
277 if (std::holds_alternative<int>(value)) {
278 return std::to_string(std::get<int>(value));
281 if (std::holds_alternative<double>(value)) {
282 std::ostringstream oss;
283 oss << std::get<double>(value);
287 throw std::runtime_error(
288 "NuisanceReader: field '" + field_name +
"' must be string-like"
292std::string NuisanceReader::value_to_code_string(
const DBNode::Value& value,
293 const std::string& field_name)
295 const std::string
s = value_to_string(value, field_name);
296 return strip_optional_quotes(s);
299double NuisanceReader::value_to_double(
const DBNode::Value& value,
300 const std::string& field_name)
302 if (std::holds_alternative<double>(value)) {
303 return std::get<double>(value);
306 if (std::holds_alternative<int>(value)) {
307 return static_cast<double>(std::get<int>(value));
310 if (std::holds_alternative<BlockName>(value)) {
311 const std::string
s = strip_optional_quotes(std::get<BlockName>(value));
312 if (is_numeric_string(s)) {
317 throw std::runtime_error(
318 "NuisanceReader: field '" + field_name +
"' must be numeric"
322MarginalType NuisanceReader::parse_marginal_type(std::string raw) {
323 raw = normalise(strip_optional_quotes(std::move(raw)));
325 if (raw ==
"gaussian") {
329 if (raw ==
"half_gaussian" || raw ==
"halfgaussian" || raw ==
"split_gaussian") {
333 if (raw ==
"flat" || raw ==
"uniform") {
337 if (raw ==
"likelihood") {
341 throw std::runtime_error(
"NuisanceReader: unknown distribution '" + raw +
"'");
344std::string NuisanceReader::normalise(std::string s) {
345 std::transform(
s.begin(),
s.end(),
s.begin(),
346 [](
unsigned char c) { return static_cast<char>(std::tolower(c)); });
348 std::replace(
s.begin(),
s.end(),
'-',
'_');
349 std::replace(
s.begin(),
s.end(),
' ',
'_');
MarginalType
Supported marginal-distribution families.
@ GAUSSIAN
Symmetric Gaussian marginal.
@ HALF_GAUSSIAN
Asymmetric / half-Gaussian-like marginal (currently mapped to split Gaussian logic).
@ LIKELIHOOD
Discrete likelihood-based marginal built from weighted support points.
@ FLAT
Uniform (flat) marginal on a finite interval.
Concrete reader for nuisance-parameter definition files.
std::unordered_map< ParamId, NuisanceSpec > NuisanceRegistry
Registry of nuisance specifications indexed by parameter id.
std::unordered_set< T > get_keys(const std::map< T, U > &map)
Extracts the key set from a std::map into an unordered_set.
Block identifier with alias support.
static std::shared_ptr< IDBNodeProvider > createDBNodeProvider(fs::path src_path)
Creates an IDBNodeProvider for a given file path.
Value get(Keys &&... keys) const
Retrieves a value from the node using a sequence of keys.
std::variant< BlockName, int, double, bool, std::shared_ptr< DBNode >, std::vector< std::shared_ptr< DBNode > > > Value
Variant type used to store values in the tree.
bool contains(const BlockName &key) const
Checks whether the node contains a direct child with the given key.
fs::path default_path() const
Returns the configured default nuisance-file path.
NuisanceReader(std::shared_ptr< INuisancePathsProvider > paths_provider)
Constructs a nuisance reader with an external path provider.
NuisanceRegistry load() const
Loads the merged nuisance registry.
NuisanceRegistry load_default() const override
Loads the built-in nuisance-parameter registry.
fs::path user_path() const
Returns the configured user nuisance-file path.
NuisanceRegistry load_user() const override
Loads the nuisance registry from the configured user source.
Hash specialization for SymbolId<Tag>.
Represents an identifier of a LHA element, possibly containing several sub-ids.
Specification of one nuisance parameter.
MarginalType marginal
Marginal model used for the nuisance constraint.
std::pair< double, double > bounds
Inclusive lower and upper bounds for the nuisance value.
ParamId param_id
Parameter identifier, usually an LHA block/code pair.
Composite identifier for a single parameter.