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 <cstddef>
18#include <cstdint>
19#include <stdexcept>
20#include <tuple>
21#include <type_traits>
22#include <utility>
23#include <vector>
24
25class TBuffer;
26
27namespace ROOT {
28namespace Experimental {
29
30// forward declarations for friend declaration
31template <typename BinContentType>
32class RHistEngine;
33namespace Internal {
34template <typename T, std::size_t N>
35static void SetBinContent(RHistEngine<T> &hist, const std::array<RBinIndex, N> &indices, const T &value);
36} // namespace Internal
37
38/**
39A histogram data structure to bin data along multiple dimensions.
40
41Every call to \ref Fill(const A &... args) "Fill" bins the data according to the axis configuration and increments the
42bin content:
43\code
44ROOT::Experimental::RHistEngine<int> hist(10, {5, 15});
45hist.Fill(8.5);
46// hist.GetBinContent(ROOT::Experimental::RBinIndex(3)) will return 1
47\endcode
48
49The class is templated on the bin content type. For counting, as in the example above, it may be an integral type such
50as `int` or `long`. Narrower types such as `unsigned char` or `short` are supported, but may overflow due to their
51limited range and must be used with care. For weighted filling, the bin content type must not be an integral type, but
52a floating-point type such as `float` or `double`, or the special type RBinWithError. Note that `float` has a limited
53significand precision of 24 bits.
54
55An object can have arbitrary dimensionality determined at run-time. The axis configuration is passed as a vector of
56RAxisVariant:
57\code
58std::vector<ROOT::Experimental::RAxisVariant> axes;
59axes.push_back(ROOT::Experimental::RRegularAxis(10, 5, 15));
60axes.push_back(ROOT::Experimental::RVariableBinAxis({1, 10, 100, 1000}));
61ROOT::Experimental::RHistEngine<int> hist(axes);
62// hist.GetNDimensions() will return 2
63\endcode
64
65\warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes.
66Feedback is welcome!
67*/
68template <typename BinContentType>
70 template <typename T, std::size_t N>
71 friend void Internal::SetBinContent(RHistEngine<T> &, const std::array<RBinIndex, N> &, const T &);
72
73 /// The axis configuration for this histogram. Relevant methods are forwarded from the public interface.
75 /// The bin contents for this histogram
76 std::vector<BinContentType> fBinContents;
77
78public:
79 /// Construct a histogram engine.
80 ///
81 /// \param[in] axes the axis objects, must have size > 0
82 explicit RHistEngine(std::vector<RAxisVariant> axes) : fAxes(std::move(axes))
83 {
85 }
86
87 /// Construct a one-dimensional histogram engine with a regular axis.
88 ///
89 /// \param[in] nNormalBins the number of normal bins, must be > 0
90 /// \param[in] interval the axis interval (lower end inclusive, upper end exclusive)
91 /// \par See also
92 /// the \ref RRegularAxis::RRegularAxis(std::uint64_t nNormalBins, std::pair<double, double> interval, bool
93 /// enableFlowBins) "constructor of RRegularAxis"
94 RHistEngine(std::uint64_t nNormalBins, std::pair<double, double> interval)
96 {
97 }
98
99 /// The copy constructor is deleted.
100 ///
101 /// Copying all bin contents can be an expensive operation, depending on the number of bins. If required, users can
102 /// explicitly call Clone().
104 /// Efficiently move construct a histogram engine.
105 ///
106 /// After this operation, the moved-from object is invalid.
108
109 /// The copy assignment operator is deleted.
110 ///
111 /// Copying all bin contents can be an expensive operation, depending on the number of bins. If required, users can
112 /// explicitly call Clone().
114 /// Efficiently move a histogram engine.
115 ///
116 /// After this operation, the moved-from object is invalid.
118
119 ~RHistEngine() = default;
120
121 const std::vector<RAxisVariant> &GetAxes() const { return fAxes.Get(); }
122 std::size_t GetNDimensions() const { return fAxes.GetNDimensions(); }
123 std::uint64_t GetTotalNBins() const { return fBinContents.size(); }
124
125 /// Get the content of a single bin.
126 ///
127 /// \code
128 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
129 /// std::array<ROOT::Experimental::RBinIndex, 2> indices = {3, 5};
130 /// int content = hist.GetBinContent(indices);
131 /// \endcode
132 ///
133 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
134 /// values. See also the class documentation of RBinIndex.
135 ///
136 /// Throws an exception if the number of indices does not match the axis configuration or the bin is not found.
137 ///
138 /// \param[in] indices the array of indices for each axis
139 /// \return the bin content
140 /// \par See also
141 /// the \ref GetBinContent(const A &... args) const "variadic function template overload" accepting arguments
142 /// directly
143 template <std::size_t N>
144 const BinContentType &GetBinContent(const std::array<RBinIndex, N> &indices) const
145 {
146 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
147 // be confusing for users.
148 if (N != GetNDimensions()) {
149 throw std::invalid_argument("invalid number of indices passed to GetBinContent");
150 }
152 if (!index.fValid) {
153 throw std::invalid_argument("bin not found in GetBinContent");
154 }
155 assert(index.fIndex < fBinContents.size());
156 return fBinContents[index.fIndex];
157 }
158
159 /// Get the content of a single bin.
160 ///
161 /// \code
162 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
163 /// int content = hist.GetBinContent(ROOT::Experimental::RBinIndex(3), ROOT::Experimental::RBinIndex(5));
164 /// // ... or construct the RBinIndex arguments implicitly from integers:
165 /// content = hist.GetBinContent(3, 5);
166 /// \endcode
167 ///
168 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
169 /// values. See also the class documentation of RBinIndex.
170 ///
171 /// Throws an exception if the number of arguments does not match the axis configuration or the bin is not found.
172 ///
173 /// \param[in] args the arguments for each axis
174 /// \return the bin content
175 /// \par See also
176 /// the \ref GetBinContent(const std::array<RBinIndex, N> &indices) const "function overload" accepting
177 /// `std::array`
178 template <typename... A>
179 const BinContentType &GetBinContent(const A &...args) const
180 {
181 std::array<RBinIndex, sizeof...(A)> indices{args...};
182 return GetBinContent(indices);
183 }
184
185 /// Add all bin contents of another histogram.
186 ///
187 /// Throws an exception if the axes configurations are not identical.
188 ///
189 /// \param[in] other another histogram
191 {
192 if (fAxes != other.fAxes) {
193 throw std::invalid_argument("axes configurations not identical in Add");
194 }
195 for (std::size_t i = 0; i < fBinContents.size(); i++) {
196 fBinContents[i] += other.fBinContents[i];
197 }
198 }
199
200 /// Add all bin contents of another histogram using atomic instructions.
201 ///
202 /// Throws an exception if the axes configurations are not identical.
203 ///
204 /// \param[in] other another histogram that must not be modified during the operation
206 {
207 if (fAxes != other.fAxes) {
208 throw std::invalid_argument("axes configurations not identical in AddAtomic");
209 }
210 for (std::size_t i = 0; i < fBinContents.size(); i++) {
211 Internal::AtomicAdd(&fBinContents[i], other.fBinContents[i]);
212 }
213 }
214
215 /// Clear all bin contents.
216 void Clear()
217 {
218 for (std::size_t i = 0; i < fBinContents.size(); i++) {
219 fBinContents[i] = {};
220 }
221 }
222
223 /// Clone this histogram engine.
224 ///
225 /// Copying all bin contents can be an expensive operation, depending on the number of bins.
226 ///
227 /// \return the cloned object
229 {
231 for (std::size_t i = 0; i < fBinContents.size(); i++) {
232 h.fBinContents[i] = fBinContents[i];
233 }
234 return h;
235 }
236
237 /// Whether this histogram engine type supports weighted filling.
238 static constexpr bool SupportsWeightedFilling = !std::is_integral_v<BinContentType>;
239
240 /// Fill an entry into the histogram.
241 ///
242 /// \code
243 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
244 /// auto args = std::make_tuple(8.5, 10.5);
245 /// hist.Fill(args);
246 /// \endcode
247 ///
248 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
249 /// discarded.
250 ///
251 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
252 /// converted for the axis type at run-time.
253 ///
254 /// \param[in] args the arguments for each axis
255 /// \par See also
256 /// the \ref Fill(const A &... args) "variadic function template overload" accepting arguments directly and the
257 /// \ref Fill(const std::tuple<A...> &args, RWeight weight) "overload for weighted filling"
258 template <typename... A>
259 void Fill(const std::tuple<A...> &args)
260 {
261 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
262 // be confusing for users.
263 if (sizeof...(A) != GetNDimensions()) {
264 throw std::invalid_argument("invalid number of arguments to Fill");
265 }
267 if (index.fValid) {
268 assert(index.fIndex < fBinContents.size());
269 fBinContents[index.fIndex]++;
270 }
271 }
272
273 /// Fill an entry into the histogram with a weight.
274 ///
275 /// This overload is not available for integral bin content types (see \ref SupportsWeightedFilling).
276 ///
277 /// \code
278 /// ROOT::Experimental::RHistEngine<float> hist({/* two dimensions */});
279 /// auto args = std::make_tuple(8.5, 10.5);
280 /// hist.Fill(args, ROOT::Experimental::RWeight(0.8));
281 /// \endcode
282 ///
283 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
284 /// discarded.
285 ///
286 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
287 /// converted for the axis type at run-time.
288 ///
289 /// \param[in] args the arguments for each axis
290 /// \param[in] weight the weight for this entry
291 /// \par See also
292 /// the \ref Fill(const A &... args) "variadic function template overload" accepting arguments directly and the
293 /// \ref Fill(const std::tuple<A...> &args) "overload for unweighted filling"
294 template <typename... A>
295 void Fill(const std::tuple<A...> &args, RWeight weight)
296 {
297 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
298
299 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
300 // be confusing for users.
301 if (sizeof...(A) != GetNDimensions()) {
302 throw std::invalid_argument("invalid number of arguments to Fill");
303 }
305 if (index.fValid) {
306 assert(index.fIndex < fBinContents.size());
307 fBinContents[index.fIndex] += weight.fValue;
308 }
309 }
310
311 /// Fill an entry into the histogram with a user-defined weight.
312 ///
313 /// This overload is only available for user-defined bin content types.
314 ///
315 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
316 /// discarded.
317 ///
318 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
319 /// converted for the axis type at run-time.
320 ///
321 /// \param[in] args the arguments for each axis
322 /// \param[in] weight the weight for this entry
323 template <typename... A, typename W>
324 void Fill(const std::tuple<A...> &args, const W &weight)
325 {
326 static_assert(std::is_class_v<BinContentType>,
327 "user-defined weight types are only supported for user-defined bin content types");
328
329 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
330 // be confusing for users.
331 if (sizeof...(A) != GetNDimensions()) {
332 throw std::invalid_argument("invalid number of arguments to Fill");
333 }
335 if (index.fValid) {
336 assert(index.fIndex < fBinContents.size());
337 fBinContents[index.fIndex] += weight;
338 }
339 }
340
341 /// Fill an entry into the histogram.
342 ///
343 /// \code
344 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
345 /// hist.Fill(8.5, 10.5);
346 /// \endcode
347 ///
348 /// For weighted filling, pass an RWeight as the last argument:
349 /// \code
350 /// ROOT::Experimental::RHistEngine<float> hist({/* two dimensions */});
351 /// hist.Fill(8.5, 10.5, ROOT::Experimental::RWeight(0.8));
352 /// \endcode
353 /// This is not available for integral bin content types (see \ref SupportsWeightedFilling).
354 ///
355 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
356 /// discarded.
357 ///
358 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
359 /// converted for the axis type at run-time.
360 ///
361 /// \param[in] args the arguments for each axis
362 /// \par See also
363 /// the function overloads accepting `std::tuple` \ref Fill(const std::tuple<A...> &args) "for unweighted filling"
364 /// and \ref Fill(const std::tuple<A...> &args, RWeight) "for weighted filling"
365 template <typename... A>
366 void Fill(const A &...args)
367 {
368 auto t = std::forward_as_tuple(args...);
369 if constexpr (std::is_same_v<typename Internal::LastType<A...>::type, RWeight>) {
370 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
371 static constexpr std::size_t N = sizeof...(A) - 1;
372 if (N != fAxes.GetNDimensions()) {
373 throw std::invalid_argument("invalid number of arguments to Fill");
374 }
375 RWeight weight = std::get<N>(t);
377 if (index.fValid) {
378 assert(index.fIndex < fBinContents.size());
379 fBinContents[index.fIndex] += weight.fValue;
380 }
381 } else {
382 Fill(t);
383 }
384 }
385
386 /// Fill an entry into the histogram using atomic instructions.
387 ///
388 /// \param[in] args the arguments for each axis
389 /// \see Fill(const std::tuple<A...> &args)
390 template <typename... A>
391 void FillAtomic(const std::tuple<A...> &args)
392 {
393 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
394 // be confusing for users.
395 if (sizeof...(A) != GetNDimensions()) {
396 throw std::invalid_argument("invalid number of arguments to Fill");
397 }
399 if (index.fValid) {
400 assert(index.fIndex < fBinContents.size());
402 }
403 }
404
405 /// Fill an entry into the histogram with a weight using atomic instructions.
406 ///
407 /// This overload is not available for integral bin content types (see \ref SupportsWeightedFilling).
408 ///
409 /// \param[in] args the arguments for each axis
410 /// \param[in] weight the weight for this entry
411 /// \see Fill(const std::tuple<A...> &args, RWeight weight)
412 template <typename... A>
413 void FillAtomic(const std::tuple<A...> &args, RWeight weight)
414 {
415 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
416
417 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
418 // be confusing for users.
419 if (sizeof...(A) != GetNDimensions()) {
420 throw std::invalid_argument("invalid number of arguments to Fill");
421 }
423 if (index.fValid) {
424 assert(index.fIndex < fBinContents.size());
426 }
427 }
428
429 /// Fill an entry into the histogram with a user-defined weight using atomic instructions.
430 ///
431 /// This overload is only available for user-defined bin content types.
432 ///
433 /// \param[in] args the arguments for each axis
434 /// \param[in] weight the weight for this entry
435 /// \see Fill(const std::tuple<A...> &args, const W &weight)
436 template <typename... A, typename W>
437 void FillAtomic(const std::tuple<A...> &args, const W &weight)
438 {
439 static_assert(std::is_class_v<BinContentType>,
440 "user-defined weight types are only supported for user-defined bin content types");
441
442 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
443 // be confusing for users.
444 if (sizeof...(A) != GetNDimensions()) {
445 throw std::invalid_argument("invalid number of arguments to Fill");
446 }
448 if (index.fValid) {
449 assert(index.fIndex < fBinContents.size());
450 Internal::AtomicAdd(&fBinContents[index.fIndex], weight);
451 }
452 }
453
454 /// Fill an entry into the histogram using atomic instructions.
455 ///
456 /// \param[in] args the arguments for each axis
457 /// \see Fill(const A &...args)
458 template <typename... A>
459 void FillAtomic(const A &...args)
460 {
461 auto t = std::forward_as_tuple(args...);
462 if constexpr (std::is_same_v<typename Internal::LastType<A...>::type, RWeight>) {
463 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
464 static constexpr std::size_t N = sizeof...(A) - 1;
465 if (N != fAxes.GetNDimensions()) {
466 throw std::invalid_argument("invalid number of arguments to Fill");
467 }
468 RWeight weight = std::get<N>(t);
470 if (index.fValid) {
471 assert(index.fIndex < fBinContents.size());
473 }
474 } else {
475 FillAtomic(t);
476 }
477 }
478
479 /// Scale all histogram bin contents.
480 ///
481 /// This method is not available for integral bin content types.
482 ///
483 /// \param[in] factor the scale factor
484 void Scale(double factor)
485 {
486 static_assert(!std::is_integral_v<BinContentType>, "scaling is not supported for integral bin content types");
487 for (std::size_t i = 0; i < fBinContents.size(); i++) {
488 fBinContents[i] *= factor;
489 }
490 }
491
492 /// %ROOT Streamer function to throw when trying to store an object of this class.
493 void Streamer(TBuffer &) { throw std::runtime_error("unable to store RHistEngine"); }
494};
495
496namespace Internal {
497/// %Internal function to set the content of a single bin.
498template <typename T, std::size_t N>
499static void SetBinContent(RHistEngine<T> &hist, const std::array<RBinIndex, N> &indices, const T &value)
500{
501 RLinearizedIndex index = hist.fAxes.ComputeGlobalIndex(indices);
502 if (!index.fValid) {
503 throw std::invalid_argument("bin not found in SetBinContent");
504 }
505 assert(index.fIndex < hist.fBinContents.size());
506 hist.fBinContents[index.fIndex] = value;
507}
508} // namespace Internal
509
510} // namespace Experimental
511} // namespace ROOT
512
513#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:42
std::size_t GetNDimensions() const
Definition RAxes.hxx:57
RLinearizedIndex ComputeGlobalIndexImpl(std::size_t index, const std::tuple< A... > &args) const
Definition RAxes.hxx:87
RLinearizedIndex ComputeGlobalIndex(const std::tuple< A... > &args) const
Compute the global index for all axes.
Definition RAxes.hxx:141
std::uint64_t ComputeTotalNBins() const
Compute the total number of bins for all axes.
Definition RAxes.hxx:68
const std::vector< RAxisVariant > & Get() const
Definition RAxes.hxx:58
A bin index with special values for underflow and overflow bins.
Definition RBinIndex.hxx:23
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.
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.
RHistEngine(std::uint64_t nNormalBins, std::pair< double, double > interval)
Construct a one-dimensional histogram engine with a regular axis.
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 Fill(const std::tuple< A... > &args, const W &weight)
Fill an entry into the histogram with a user-defined weight.
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.
std::uint64_t GetTotalNBins() const
void FillAtomic(const std::tuple< A... > &args, const W &weight)
Fill an entry into the histogram with a user-defined weight using atomic instructions.
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.
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