EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
dfe_histogram.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file dfe_histogram.hpp
1 // SPDX-License-Identifier: MIT
2 // Copyright 2018 Moritz Kiehn
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 // SOFTWARE.
21 
26 
27 #pragma once
28 
29 #include <algorithm>
30 #include <array>
31 #include <functional>
32 #include <initializer_list>
33 #include <numeric>
34 #include <stdexcept>
35 #include <type_traits>
36 #include <utility>
37 #include <vector>
38 
39 namespace dfe {
40 namespace histogram_impl {
41 namespace {
42 
48 template<typename T, std::size_t NDimensions>
49 class NArray {
50 public:
51  using Index = std::array<std::size_t, NDimensions>;
52 
54  NArray(Index size, const T& value = T());
55  NArray(const NArray&) = default;
56  NArray(NArray&&) = default;
57  NArray& operator=(const NArray&) = default;
58  NArray& operator=(NArray&&) = default;
59  ~NArray() = default;
60 
62  constexpr const Index& size() const { return m_size; }
64  constexpr const T& operator[](Index idx) const { return m_data[linear(idx)]; }
66  constexpr T& operator[](Index idx) { return m_data[linear(idx)]; }
68  const T& at(Index idx) const;
70  T& at(Index idx);
71 
72 private:
73  constexpr std::size_t linear(Index idx) const;
74  constexpr bool within_bounds(Index idx) const;
75 
77  std::vector<T> m_data;
78 };
79 
80 } // namespace
81 } // namespace histogram_impl
82 
84 template<typename T>
85 class UniformAxis {
86 public:
87  using Value = T;
88 
92  UniformAxis(T lower, T upper, std::size_t nbins);
93 
95  constexpr std::size_t nbins() const { return m_nbins; }
97  std::size_t index(T value) const;
98 
99 private:
100  std::size_t m_nbins;
103 };
104 
108 template<typename T>
110 public:
111  using Value = T;
112 
116  OverflowAxis(T lower, T upper, std::size_t nbins);
117 
119  constexpr std::size_t nbins() const { return 2 + m_ndatabins; }
121  constexpr std::size_t index(T value) const;
122 
123 private:
124  std::size_t m_ndatabins;
127 };
128 
130 template<typename T>
132 public:
133  using Value = T;
134 
136  explicit VariableAxis(std::vector<T>&& edges);
138  VariableAxis(std::initializer_list<T> edges);
139 
141  constexpr std::size_t nbins() const { return m_edges.size() - 1; }
143  std::size_t index(T value) const;
144 
145 private:
146  std::vector<T> m_edges;
147 };
148 
153 template<typename T, typename... Axes>
154 class Histogram {
155 public:
156  using Data = dfe::histogram_impl::NArray<T, sizeof...(Axes)>;
157  using Index = typename Data::Index;
158 
159  Histogram(Axes&&... axes);
160 
162  constexpr const Index& size() const { return m_data.size(); }
164  const T& value(Index idx) const { return m_data.at(idx); }
169  void fill(typename Axes::Value... values, T weight = static_cast<T>(1)) {
170  // TODO 2018-11-28 how to typedef parameter pack Axes::Value...?
171  m_data[index(std::index_sequence_for<Axes...>(), values...)] += weight;
172  }
173 
174 private:
175  template<std::size_t... Is>
176  constexpr Index index(
177  std::index_sequence<Is...>, typename Axes::Value... values) const {
178  return Index{std::get<Is>(m_axes).index(values)...};
179  }
180 
182  std::tuple<Axes...> m_axes;
183 };
184 
185 // predefined histogram types
186 
188 using Histogram2 =
190 
191 // implementation NArray
192 
193 template<typename T, std::size_t NDimensions>
194 inline histogram_impl::NArray<T, NDimensions>::NArray(
195  Index size, const T& value)
196  : m_size(size)
197  , m_data(
198  std::accumulate(
199  size.begin(), size.end(), static_cast<std::size_t>(1),
200  std::multiplies<std::size_t>()),
201  value) {}
202 
203 // construct linear column-major index from n-dimensional index
204 template<typename T, std::size_t NDimensions>
205 constexpr std::size_t
206 histogram_impl::NArray<T, NDimensions>::linear(Index idx) const {
207  std::size_t result = 0;
208  std::size_t step = 1;
209  for (std::size_t i = 0; i < NDimensions; ++i) {
210  result += step * idx[i];
211  step *= m_size[i];
212  }
213  return result;
214 }
215 
216 template<typename T, std::size_t NDimensions>
217 constexpr bool
218 histogram_impl::NArray<T, NDimensions>::within_bounds(Index idx) const {
219  for (std::size_t i = 0; i < NDimensions; ++i) {
220  if (m_size[i] <= idx[i]) {
221  return false;
222  }
223  }
224  return true;
225 }
226 
227 template<typename T, std::size_t NDimensions>
228 inline const T&
230  if (!within_bounds(idx)) {
231  throw std::out_of_range("NArray index is out of valid range");
232  }
233  return m_data[linear(idx)];
234 }
235 
236 template<typename T, std::size_t NDimensions>
237 inline T&
239  if (!within_bounds(idx)) {
240  throw std::out_of_range("NArray index is out of valid range");
241  }
242  return m_data[linear(idx)];
243 }
244 
245 // implementation UniformAxis
246 
247 template<typename T>
248 inline UniformAxis<T>::UniformAxis(T lower, T upper, std::size_t nbins)
249  : m_nbins(nbins), m_lower(lower), m_upper(upper) {}
250 
251 template<typename T>
252 inline std::size_t
254  if (value < this->m_lower) {
255  throw std::out_of_range("Value is smaller than lower axis limit");
256  }
257  if (m_upper <= value) {
258  throw std::out_of_range("Value is equal or larger than upper axis limit");
259  }
260  // cast truncates to integer part; should work since index is always > 0.
261  return static_cast<std::size_t>(
262  m_nbins * (value - m_lower) / (m_upper - m_lower));
263 }
264 
265 // implementation OverflowAxis
266 
267 template<typename T>
269  Value lower, Value upper, std::size_t nbins)
270  : m_ndatabins(nbins), m_lower(lower), m_upper(upper) {}
271 
272 template<typename T>
273 constexpr std::size_t
275  if (value < m_lower) {
276  return 0;
277  }
278  if (m_upper <= value) {
279  return m_ndatabins + 1;
280  }
281  // cast truncates to integer part; should work since index is always > 0.
282  return 1
283  + static_cast<std::size_t>(
284  m_ndatabins * (value - m_lower) / (m_upper - m_lower));
285 }
286 
287 // implementation VariableAxis
288 
289 template<typename T>
290 inline VariableAxis<T>::VariableAxis(std::vector<Value>&& edges)
291  : m_edges(std::move(edges)) {
292  if (m_edges.size() < 2) {
293  throw std::invalid_argument("Less than two bin edges");
294  }
295  // edges must be sorted and unique
296  if (!std::is_sorted(
297  m_edges.begin(), m_edges.end(), std::less_equal<Value>())) {
298  throw std::invalid_argument("Bin edges are not sorted or have duplicates");
299  }
300 }
301 
302 template<typename T>
303 inline VariableAxis<T>::VariableAxis(std::initializer_list<Value> edges)
304  : VariableAxis(std::vector<Value>(edges)) {}
305 
306 template<typename T>
307 inline std::size_t
309  // find upper edge of the corresponding bin
310  auto it = std::upper_bound(m_edges.begin(), m_edges.end(), value);
311  if (it == m_edges.begin()) {
312  throw std::out_of_range("Value is smaller than lower axis limit");
313  }
314  if (it == m_edges.end()) {
315  throw std::out_of_range("Value is equal or larger than upper axis limit");
316  }
317  return std::distance(m_edges.begin(), it) - 1;
318 }
319 
320 // implementation Histogram
321 
322 template<typename T, typename... Axes>
323 inline Histogram<T, Axes...>::Histogram(Axes&&... axes)
324  // access nbins *before* moving the axes, otherwise the axes are invalid.
325  : m_data(Index{axes.nbins()...}, static_cast<T>(0))
326  , m_axes(std::move(axes)...) {}
327 
328 } // namespace dfe