ATLAS Offline Software
Loading...
Searching...
No Matches
QuantileFactory.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4#include "VariableFactory.h"
5#include "QuantileFactory.h"
6#include <iostream>
7using json = nlohmann::json;
8
9std::vector<float> QuantileFactory::parseEdges(const json& cfg){
10 std::vector<float> edges;
11 edges.reserve(cfg.size());
12
13 for (const auto& val : cfg) {
14 if (val.is_number()) {
15 edges.push_back(val.get<float>());
16 }
17 else if (val.is_string()) {
18 const std::string s = val.get<std::string>();
19
20 if (s == "inf" || s == "+inf") {
21 edges.push_back(std::numeric_limits<float>::infinity());
22 }
23 else if (s == "-inf") {
24 edges.push_back(-std::numeric_limits<float>::infinity());
25 }
26 else {
27 throw std::runtime_error("Invalid edge value: " + s);
28 }
29 }
30 else {
31 throw std::runtime_error("Edge must be a number or \"inf\"/\"-inf\", got:" + val.dump());
32 }
33 }
34
35 // Verify edges are strictly sorted ascending — catches malformed JSON early.
36 if (!std::is_sorted(edges.begin(), edges.end())) {
37 throw std::runtime_error("Bin edges must be sorted in ascending order");
38 }
39
40 return edges;
41}
42
45 std::vector<int> values = cfg.at("values").get<std::vector<int>>();
46 int default_bin = cfg.at("default").get<int>();
47
48 std::unordered_map<int,int> mapping;
49 for (int i = 0; i < static_cast<int>(values.size()); ++i) {
50 mapping[values[i]] = i;
51 }
52
53 auto func = [var, mapping, default_bin](const SG::AuxElement& el) -> int {
54 int v = static_cast<int>(var(el));
55 auto it = mapping.find(v);
56 if (it != mapping.end()) {
57 return it->second;
58 }
59 return default_bin;
60 };
61
62 return {values.size(), func};
63
64}
65
68 std::vector<float> edges = QuantileFactory::parseEdges(cfg.at("edges"));
69
70 const bool useAbs = cfg.value("abs", false);
71
72 int underflow_bin = cfg.value("underflow_bin", -1);
73 int overflow_bin = cfg.value("overflow_bin", -1);
74
75 auto func = [var, edges, useAbs, underflow_bin, overflow_bin] (const SG::AuxElement& el) -> int {
76 float v = var(el);
77 if (useAbs) v = std::abs(v);
78
79 if (v < edges.front()) {
80 return underflow_bin;
81 }
82 if (v >= edges.back()) {
83 return overflow_bin;
84 }
85
86 auto it = std::lower_bound(edges.begin(), edges.end(), v);
87 return static_cast<int>(std::distance(edges.begin(), it)) - 1;
88 };
89
90 return {edges.size() - 1, func};
91}
92
93// for 2-d tagging
96 std::vector<float> edges = QuantileFactory::parseEdges(cfg.at("edges"));
97
98 std::vector<std::function<int(const SG::AuxElement&)>> sub_nodes;
99 std::vector<size_t> sub_sizes;
100
101 size_t totalSize = 0;
102
103 for (const auto& node : cfg.at("nodes")) {
105 sub_nodes.push_back(func);
106 sub_sizes.push_back(size);
107 totalSize += size;
108 }
109
110 std::string numbering = cfg.value("numbering", "sequential");
111
112 std::vector<int> offsets(sub_nodes.size(), 0);
113 if (numbering == "sequential") {
114 for (size_t i = 1; i < sub_nodes.size(); ++i) {
115 offsets[i] = offsets[i-1] + sub_sizes[i-1];
116 }
117 }
118
119 auto func = [var, edges, sub_nodes, offsets, numbering]
120 (const SG::AuxElement& el) -> int {
121 float v = var(el);
122
123 auto it = std::lower_bound(edges.begin(), edges.end(), v);
124 int region = static_cast<int>(std::distance(edges.begin(), it)) - 1;
125 if (region < 0 || region >= static_cast<int>(edges.size()) - 1) {
126 throw std::runtime_error("Failed to find region for value " + std::to_string(v));
127 }
128
129 int local = sub_nodes[region](el);
130
131 if (numbering == "sequential") {
132 return offsets[region] + local;
133 }
134 return local;
135 };
136
137 return {totalSize, func};
138}
139
140// turn the already computed per-axis bin indices into a flattened index
142 std::vector<std::function<int(const SG::AuxElement&)>> axes;
143 std::vector<size_t> axis_sizes;
144
145 for (const auto& axis : cfg.at("axes")) {
146 auto [size, func] = QuantileFactory::quantileFactory(axis);
147 axes.push_back(func);
148 axis_sizes.push_back(size);
149 }
150
151 std::vector<int> strides(axes.size(), 1);
152 for (int i = static_cast<int>(axes.size()) - 2; i >= 0; --i) {
153 strides[i] = strides[i+1] * axis_sizes[i+1];
154 }
155
156 auto func = [axes, strides](const SG::AuxElement& el) -> int {
157 int index = 0;
158 for (size_t i = 0; i < axes.size(); ++i) {
159 int bin = axes[i](el);
160 if (bin < 0) {return -1;}
161 index += bin * strides[i];
162 }
163 return index;
164 };
165
166 size_t total_size = 1;
167 for (size_t size : axis_sizes) {
168 total_size *= size;
169 }
170
171 return {total_size, func};
172}
173
175
176 std::string type = cfg.at("type");
177
178 if (type == "category") return QuantileFactory::makeCategory(cfg);
179 if (type == "enumerate") return QuantileFactory::makeEnumerate(cfg);
180 if (type == "nodes") return QuantileFactory::makeNodes(cfg);
181 if (type == "dense") return QuantileFactory::makeDense(cfg);
182
183 throw std::runtime_error("Unknown quantile type: " + type);
184
185}
186
nlohmann::json json
static unsigned int totalSize(const MultiDimArray< T, N > &ht)
size_t size() const
Number of registered mappings.
std::pair< size_t, std::function< int(const SG::AuxElement &)> > QuantileFunc
static QuantileFunc makeEnumerate(const nlohmann::json &cfg)
Maps discrete values to category indices.
static QuantileFunc makeNodes(const nlohmann::json &cfg)
Builds a tree-based lookup structure for node-based binning, primarily used for pseudo-continuous 2D ...
static QuantileFunc makeDense(const nlohmann::json &cfg)
Combines multiple binning dimensions into a single flattened global index used for scale factor looku...
static QuantileFunc quantileFactory(const nlohmann::json &cfg)
Dispatch to the appropriate binning scheme based on the JSON config.
static std::vector< float > parseEdges(const nlohmann::json &cfg)
Parse an ordered list of bin edges from the JSON config.
static QuantileFunc makeCategory(const nlohmann::json &cfg)
Converts a continuous variable into a bin index using an ordered list of bin edges.
static FloatFunc floatVariableFactory(const nlohmann::json &cfg)
std::function< float(const SG::AuxElement &)> FloatFunc
static IntFunc intVariableFactory(const nlohmann::json &cfg)
std::function< int(const SG::AuxElement &)> IntFunc
Definition node.h:24
AuxElement(SG::AuxVectorData *container, size_t index)
Base class for elements of a container that can have aux data.
Definition index.py:1