Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RHistEngine.hxx
Go to the documentation of this file.
1/// \file
2/// \warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes.
3/// Feedback is welcome!
4
5#ifndef ROOT_RHistEngine
6#define ROOT_RHistEngine
7
8#include "RAxes.hxx"
9#include "RBinIndex.hxx"
10#include "RHistUtils.hxx"
11#include "RLinearizedIndex.hxx"
12#include "RRegularAxis.hxx"
13#include "RWeight.hxx"
14
15#include <array>
16#include <cassert>
17#include <stdexcept>
18#include <tuple>
19#include <type_traits>
20#include <utility>
21#include <vector>
22
23class TBuffer;
24
25namespace ROOT {
26namespace Experimental {
27
28// forward declarations for friend declaration
29template <typename BinContentType>
30class RHistEngine;
31namespace Internal {
32template <typename T, std::size_t N>
33static void SetBinContent(RHistEngine<T> &hist, const std::array<RBinIndex, N> &indices, const T &value);
34} // namespace Internal
35
36/**
37A histogram data structure to bin data along multiple dimensions.
38
39Every call to \ref Fill(const A &... args) "Fill" bins the data according to the axis configuration and increments the
40bin content:
41\code
42ROOT::Experimental::RHistEngine<int> hist(10, {5, 15});
43hist.Fill(8.5);
44// hist.GetBinContent(ROOT::Experimental::RBinIndex(3)) will return 1
45\endcode
46
47The class is templated on the bin content type. For counting, as in the example above, it may be an integral type such
48as `int` or `long`. Narrower types such as `unsigned char` or `short` are supported, but may overflow due to their
49limited range and must be used with care. For weighted filling, the bin content type must not be an integral type, but
50a floating-point type such as `float` or `double`, or the special type RBinWithError. Note that `float` has a limited
51significand precision of 24 bits.
52
53An object can have arbitrary dimensionality determined at run-time. The axis configuration is passed as a vector of
54RAxisVariant:
55\code
56std::vector<ROOT::Experimental::RAxisVariant> axes;
57axes.push_back(ROOT::Experimental::RRegularAxis(10, 5, 15));
58axes.push_back(ROOT::Experimental::RVariableBinAxis({1, 10, 100, 1000}));
59ROOT::Experimental::RHistEngine<int> hist(axes);
60// hist.GetNDimensions() will return 2
61\endcode
62
63\warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes.
64Feedback is welcome!
65*/
66template <typename BinContentType>
68 template <typename T, std::size_t N>
69 friend void Internal::SetBinContent(RHistEngine<T> &, const std::array<RBinIndex, N> &, const T &);
70
71 /// The axis configuration for this histogram. Relevant methods are forwarded from the public interface.
73 /// The bin contents for this histogram
74 std::vector<BinContentType> fBinContents;
75
76public:
77 /// Construct a histogram engine.
78 ///
79 /// \param[in] axes the axis objects, must have size > 0
80 explicit RHistEngine(std::vector<RAxisVariant> axes) : fAxes(std::move(axes))
81 {
83 }
84
85 /// Construct a one-dimensional histogram engine with a regular axis.
86 ///
87 /// \param[in] nNormalBins the number of normal bins, must be > 0
88 /// \param[in] interval the axis interval (lower end inclusive, upper end exclusive)
89 /// \par See also
90 /// the
91 /// \ref RRegularAxis::RRegularAxis(std::size_t nNormalBins, std::pair<double, double> interval, bool enableFlowBins)
92 /// "constructor of RRegularAxis"
93 RHistEngine(std::size_t nNormalBins, std::pair<double, double> interval)
95 {
96 }
97
98 /// The copy constructor is deleted.
99 ///
100 /// Copying all bin contents can be an expensive operation, depending on the number of bins. If required, users can
101 /// explicitly call Clone().
103 /// Efficiently move construct a histogram engine.
104 ///
105 /// After this operation, the moved-from object is invalid.
107
108 /// The copy assignment operator is deleted.
109 ///
110 /// Copying all bin contents can be an expensive operation, depending on the number of bins. If required, users can
111 /// explicitly call Clone().
113 /// Efficiently move a histogram engine.
114 ///
115 /// After this operation, the moved-from object is invalid.
117
118 ~RHistEngine() = default;
119
120 const std::vector<RAxisVariant> &GetAxes() const { return fAxes.Get(); }
121 std::size_t GetNDimensions() const { return fAxes.GetNDimensions(); }
122 std::size_t GetTotalNBins() const { return fBinContents.size(); }
123
124 /// Get the content of a single bin.
125 ///
126 /// \code
127 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
128 /// std::array<ROOT::Experimental::RBinIndex, 2> indices = {3, 5};
129 /// int content = hist.GetBinContent(indices);
130 /// \endcode
131 ///
132 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
133 /// values. See also the class documentation of RBinIndex.
134 ///
135 /// Throws an exception if the number of indices does not match the axis configuration or the bin is not found.
136 ///
137 /// \param[in] indices the array of indices for each axis
138 /// \return the bin content
139 /// \par See also
140 /// the \ref GetBinContent(const A &... args) const "variadic function template overload" accepting arguments
141 /// directly
142 template <std::size_t N>
143 const BinContentType &GetBinContent(const std::array<RBinIndex, N> &indices) const
144 {
145 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
146 // be confusing for users.
147 if (N != GetNDimensions()) {
148 throw std::invalid_argument("invalid number of indices passed to GetBinContent");
149 }
151 if (!index.fValid) {
152 throw std::invalid_argument("bin not found in GetBinContent");
153 }
154 assert(index.fIndex < fBinContents.size());
155 return fBinContents[index.fIndex];
156 }
157
158 /// Get the content of a single bin.
159 ///
160 /// \code
161 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
162 /// int content = hist.GetBinContent(ROOT::Experimental::RBinIndex(3), ROOT::Experimental::RBinIndex(5));
163 /// // ... or construct the RBinIndex arguments implicitly from integers:
164 /// content = hist.GetBinContent(3, 5);
165 /// \endcode
166 ///
167 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
168 /// values. See also the class documentation of RBinIndex.
169 ///
170 /// Throws an exception if the number of arguments does not match the axis configuration or the bin is not found.
171 ///
172 /// \param[in] args the arguments for each axis
173 /// \return the bin content
174 /// \par See also
175 /// the \ref GetBinContent(const std::array<RBinIndex, N> &indices) const "function overload" accepting
176 /// `std::array`
177 template <typename... A>
178 const BinContentType &GetBinContent(const A &...args) const
179 {
180 std::array<RBinIndex, sizeof...(A)> indices{args...};
181 return GetBinContent(indices);
182 }
183
184 /// Add all bin contents of another histogram.
185 ///
186 /// Throws an exception if the axes configurations are not identical.
187 ///
188 /// \param[in] other another histogram
190 {
191 if (fAxes != other.fAxes) {
192 throw std::invalid_argument("axes configurations not identical in Add");
193 }
194 for (std::size_t i = 0; i < fBinContents.size(); i++) {
195 fBinContents[i] += other.fBinContents[i];
196 }
197 }
198
199 /// Add all bin contents of another histogram using atomic instructions.
200 ///
201 /// Throws an exception if the axes configurations are not identical.
202 ///
203 /// \param[in] other another histogram that must not be modified during the operation
205 {
206 if (fAxes != other.fAxes) {
207 throw std::invalid_argument("axes configurations not identical in AddAtomic");
208 }
209 for (std::size_t i = 0; i < fBinContents.size(); i++) {
210 Internal::AtomicAdd(&fBinContents[i], other.fBinContents[i]);
211 }
212 }
213
214 /// Clear all bin contents.
215 void Clear()
216 {
217 for (std::size_t i = 0; i < fBinContents.size(); i++) {
218 fBinContents[i] = {};
219 }
220 }
221
222 /// Clone this histogram engine.
223 ///
224 /// Copying all bin contents can be an expensive operation, depending on the number of bins.
225 ///
226 /// \return the cloned object
228 {
230 for (std::size_t i = 0; i < fBinContents.size(); i++) {
231 h.fBinContents[i] = fBinContents[i];
232 }
233 return h;
234 }
235
236 /// Whether this histogram engine type supports weighted filling.
237 static constexpr bool SupportsWeightedFilling = !std::is_integral_v<BinContentType>;
238
239 /// Fill an entry into the histogram.
240 ///
241 /// \code
242 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
243 /// auto args = std::make_tuple(8.5, 10.5);
244 /// hist.Fill(args);
245 /// \endcode
246 ///
247 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
248 /// discarded.
249 ///
250 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
251 /// converted for the axis type at run-time.
252 ///
253 /// \param[in] args the arguments for each axis
254 /// \par See also
255 /// the \ref Fill(const A &... args) "variadic function template overload" accepting arguments directly and the
256 /// \ref Fill(const std::tuple<A...> &args, RWeight weight) "overload for weighted filling"
257 template <typename... A>
258 void Fill(const std::tuple<A...> &args)
259 {
260 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
261 // be confusing for users.
262 if (sizeof...(A) != GetNDimensions()) {
263 throw std::invalid_argument("invalid number of arguments to Fill");
264 }
266 if (index.fValid) {
267 assert(index.fIndex < fBinContents.size());
268 fBinContents[index.fIndex]++;
269 }
270 }
271
272 /// Fill an entry into the histogram with a weight.
273 ///
274 /// This overload is not available for integral bin content types (see \ref SupportsWeightedFilling).
275 ///
276 /// \code
277 /// ROOT::Experimental::RHistEngine<float> hist({/* two dimensions */});
278 /// auto args = std::make_tuple(8.5, 10.5);
279 /// hist.Fill(args, ROOT::Experimental::RWeight(0.8));
280 /// \endcode
281 ///
282 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
283 /// discarded.
284 ///
285 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
286 /// converted for the axis type at run-time.
287 ///
288 /// \param[in] args the arguments for each axis
289 /// \param[in] weight the weight for this entry
290 /// \par See also
291 /// the \ref Fill(const A &... args) "variadic function template overload" accepting arguments directly and the
292 /// \ref Fill(const std::tuple<A...> &args) "overload for unweighted filling"
293 template <typename... A>
294 void Fill(const std::tuple<A...> &args, RWeight weight)
295 {
296 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
297
298 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
299 // be confusing for users.
300 if (sizeof...(A) != GetNDimensions()) {
301 throw std::invalid_argument("invalid number of arguments to Fill");
302 }
304 if (index.fValid) {
305 assert(index.fIndex < fBinContents.size());
306 fBinContents[index.fIndex] += weight.fValue;
307 }
308 }
309
310 /// Fill an entry into the histogram.
311 ///
312 /// \code
313 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
314 /// hist.Fill(8.5, 10.5);
315 /// \endcode
316 ///
317 /// For weighted filling, pass an RWeight as the last argument:
318 /// \code
319 /// ROOT::Experimental::RHistEngine<float> hist({/* two dimensions */});
320 /// hist.Fill(8.5, 10.5, ROOT::Experimental::RWeight(0.8));
321 /// \endcode
322 /// This is not available for integral bin content types (see \ref SupportsWeightedFilling).
323 ///
324 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
325 /// discarded.
326 ///
327 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
328 /// converted for the axis type at run-time.
329 ///
330 /// \param[in] args the arguments for each axis
331 /// \par See also
332 /// the function overloads accepting `std::tuple` \ref Fill(const std::tuple<A...> &args) "for unweighted filling"
333 /// and \ref Fill(const std::tuple<A...> &args, RWeight) "for weighted filling"
334 template <typename... A>
335 void Fill(const A &...args)
336 {
337 auto t = std::forward_as_tuple(args...);
338 if constexpr (std::is_same_v<typename Internal::LastType<A...>::type, RWeight>) {
339 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
340 static constexpr std::size_t N = sizeof...(A) - 1;
341 if (N != fAxes.GetNDimensions()) {
342 throw std::invalid_argument("invalid number of arguments to Fill");
343 }
344 RWeight weight = std::get<N>(t);
346 if (index.fValid) {
347 assert(index.fIndex < fBinContents.size());
348 fBinContents[index.fIndex] += weight.fValue;
349 }
350 } else {
351 Fill(t);
352 }
353 }
354
355 /// Fill an entry into the histogram using atomic instructions.
356 ///
357 /// \param[in] args the arguments for each axis
358 /// \see Fill(const std::tuple<A...> &args)
359 template <typename... A>
360 void FillAtomic(const std::tuple<A...> &args)
361 {
362 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
363 // be confusing for users.
364 if (sizeof...(A) != GetNDimensions()) {
365 throw std::invalid_argument("invalid number of arguments to Fill");
366 }
368 if (index.fValid) {
369 assert(index.fIndex < fBinContents.size());
371 }
372 }
373
374 /// Fill an entry into the histogram with a weight using atomic instructions.
375 ///
376 /// This overload is not available for integral bin content types (see \ref SupportsWeightedFilling).
377 ///
378 /// \param[in] args the arguments for each axis
379 /// \param[in] weight the weight for this entry
380 /// \see Fill(const std::tuple<A...> &args, RWeight weight)
381 template <typename... A>
382 void FillAtomic(const std::tuple<A...> &args, RWeight weight)
383 {
384 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
385
386 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
387 // be confusing for users.
388 if (sizeof...(A) != GetNDimensions()) {
389 throw std::invalid_argument("invalid number of arguments to Fill");
390 }
392 if (index.fValid) {
393 assert(index.fIndex < fBinContents.size());
395 }
396 }
397
398 /// Fill an entry into the histogram using atomic instructions.
399 ///
400 /// \param[in] args the arguments for each axis
401 /// \see Fill(const A &...args)
402 template <typename... A>
403 void FillAtomic(const A &...args)
404 {
405 auto t = std::forward_as_tuple(args...);
406 if constexpr (std::is_same_v<typename Internal::LastType<A...>::type, RWeight>) {
407 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
408 static constexpr std::size_t N = sizeof...(A) - 1;
409 if (N != fAxes.GetNDimensions()) {
410 throw std::invalid_argument("invalid number of arguments to Fill");
411 }
412 RWeight weight = std::get<N>(t);
414 if (index.fValid) {
415 assert(index.fIndex < fBinContents.size());
417 }
418 } else {
419 FillAtomic(t);
420 }
421 }
422
423 /// Scale all histogram bin contents.
424 ///
425 /// This method is not available for integral bin content types.
426 ///
427 /// \param[in] factor the scale factor
428 void Scale(double factor)
429 {
430 static_assert(!std::is_integral_v<BinContentType>, "scaling is not supported for integral bin content types");
431 for (std::size_t i = 0; i < fBinContents.size(); i++) {
432 fBinContents[i] *= factor;
433 }
434 }
435
436 /// %ROOT Streamer function to throw when trying to store an object of this class.
437 void Streamer(TBuffer &) { throw std::runtime_error("unable to store RHistEngine"); }
438};
439
440namespace Internal {
441/// %Internal function to set the content of a single bin.
442template <typename T, std::size_t N>
443static void SetBinContent(RHistEngine<T> &hist, const std::array<RBinIndex, N> &indices, const T &value)
444{
445 RLinearizedIndex index = hist.fAxes.ComputeGlobalIndex(indices);
446 if (!index.fValid) {
447 throw std::invalid_argument("bin not found in SetBinContent");
448 }
449 assert(index.fIndex < hist.fBinContents.size());
450 hist.fBinContents[index.fIndex] = value;
451}
452} // namespace Internal
453
454} // namespace Experimental
455} // namespace ROOT
456
457#endif
#define h(i)
Definition RSha256.hxx:106
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define N
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
Bin configurations for all dimensions of a histogram.
Definition RAxes.hxx:41
std::size_t GetNDimensions() const
Definition RAxes.hxx:56
RLinearizedIndex ComputeGlobalIndexImpl(std::size_t index, const std::tuple< A... > &args) const
Definition RAxes.hxx:86
RLinearizedIndex ComputeGlobalIndex(const std::tuple< A... > &args) const
Compute the global index for all axes.
Definition RAxes.hxx:140
std::size_t ComputeTotalNBins() const
Compute the total number of bins for all axes.
Definition RAxes.hxx:67
const std::vector< RAxisVariant > & Get() const
Definition RAxes.hxx:57
A bin index with special values for underflow and overflow bins.
Definition RBinIndex.hxx:22
A histogram data structure to bin data along multiple dimensions.
void Fill(const A &...args)
Fill an entry into the histogram.
const std::vector< RAxisVariant > & GetAxes() const
void AddAtomic(const RHistEngine< BinContentType > &other)
Add all bin contents of another histogram using atomic instructions.
RHistEngine< BinContentType > Clone() const
Clone this histogram engine.
RHistEngine< BinContentType > & operator=(const RHistEngine< BinContentType > &)=delete
The copy assignment operator is deleted.
void Scale(double factor)
Scale all histogram bin contents.
std::size_t GetTotalNBins() const
void Fill(const std::tuple< A... > &args)
Fill an entry into the histogram.
void FillAtomic(const std::tuple< A... > &args)
Fill an entry into the histogram using atomic instructions.
RHistEngine< BinContentType > & operator=(RHistEngine< BinContentType > &&)=default
Efficiently move a histogram engine.
const BinContentType & GetBinContent(const std::array< RBinIndex, N > &indices) const
Get the content of a single bin.
const BinContentType & GetBinContent(const A &...args) const
Get the content of a single bin.
RHistEngine(const RHistEngine< BinContentType > &)=delete
The copy constructor is deleted.
void Add(const RHistEngine< BinContentType > &other)
Add all bin contents of another histogram.
std::size_t GetNDimensions() const
void FillAtomic(const std::tuple< A... > &args, RWeight weight)
Fill an entry into the histogram with a weight using atomic instructions.
static constexpr bool SupportsWeightedFilling
Whether this histogram engine type supports weighted filling.
void Clear()
Clear all bin contents.
RHistEngine(std::vector< RAxisVariant > axes)
Construct a histogram engine.
void FillAtomic(const A &...args)
Fill an entry into the histogram using atomic instructions.
Internal::RAxes fAxes
The axis configuration for this histogram. Relevant methods are forwarded from the public interface.
RHistEngine(std::size_t nNormalBins, std::pair< double, double > interval)
Construct a one-dimensional histogram engine with a regular axis.
void Fill(const std::tuple< A... > &args, RWeight weight)
Fill an entry into the histogram with a weight.
RHistEngine(RHistEngine< BinContentType > &&)=default
Efficiently move construct a histogram engine.
void Streamer(TBuffer &)
ROOT Streamer function to throw when trying to store an object of this class.
std::vector< BinContentType > fBinContents
The bin contents for this histogram.
A regular axis with equidistant bins in the interval .
Buffer base class used for serializing objects.
Definition TBuffer.h:43
std::enable_if_t< std::is_arithmetic_v< T > > AtomicInc(T *ptr)
static void SetBinContent(RHistEngine< T > &hist, const std::array< RBinIndex, N > &indices, const T &value)
Internal function to set the content of a single bin.
std::enable_if_t< std::is_integral_v< T > > AtomicAdd(T *ptr, T val)
A linearized index that can be invalid.
A weight for filling histograms.
Definition RWeight.hxx:17