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 "RAxisVariant.hxx"
10#include "RBinIndex.hxx"
12#include "RHistUtils.hxx"
13#include "RLinearizedIndex.hxx"
14#include "RRegularAxis.hxx"
16#include "RSliceSpec.hxx"
17#include "RWeight.hxx"
18
19#include <array>
20#include <cassert>
21#include <cstddef>
22#include <cstdint>
23#include <initializer_list>
24#include <stdexcept>
25#include <tuple>
26#include <type_traits>
27#include <utility>
28#include <vector>
29
30class TBuffer;
31
32namespace ROOT {
33namespace Experimental {
34
35// forward declaration for friend declaration
36template <typename T>
37class RHist;
38
39/**
40A histogram data structure to bin data along multiple dimensions.
41
42Every call to \ref Fill(const A &... args) "Fill" bins the data according to the axis configuration and increments the
43bin content:
44\code
45ROOT::Experimental::RHistEngine<int> hist(10, {5, 15});
46hist.Fill(8.5);
47// hist.GetBinContent(ROOT::Experimental::RBinIndex(3)) will return 1
48\endcode
49
50The class is templated on the bin content type. For counting, as in the example above, it may be an integral type such
51as `int` or `long`. Narrower types such as `unsigned char` or `short` are supported, but may overflow due to their
52limited range and must be used with care. For weighted filling, the bin content type must not be an integral type, but
53a floating-point type such as `float` or `double`, or the special type RBinWithError. Note that `float` has a limited
54significand precision of 24 bits.
55
56An object can have arbitrary dimensionality determined at run-time. The axis configuration is passed as a vector of
57RAxisVariant:
58\code
59std::vector<ROOT::Experimental::RAxisVariant> axes;
60axes.push_back(ROOT::Experimental::RRegularAxis(10, {5, 15}));
61axes.push_back(ROOT::Experimental::RVariableBinAxis({1, 10, 100, 1000}));
62ROOT::Experimental::RHistEngine<int> hist(axes);
63// hist.GetNDimensions() will return 2
64\endcode
65
66\warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes.
67Feedback is welcome!
68*/
69template <typename BinContentType>
71 // For conversion, all other template instantiations must be a friend.
72 template <typename U>
73 friend class RHistEngine;
74
75 // For slicing, RHist needs to call SliceImpl.
76 friend class RHist<BinContentType>;
77
78 /// The axis configuration for this histogram. Relevant methods are forwarded from the public interface.
80 /// The bin contents for this histogram
81 std::vector<BinContentType> fBinContents;
82
83public:
84 /// Construct a histogram engine.
85 ///
86 /// \param[in] axes the axis objects, must have size > 0
87 explicit RHistEngine(std::vector<RAxisVariant> axes) : fAxes(std::move(axes))
88 {
90 }
91
92 /// Construct a histogram engine.
93 ///
94 /// Note that there is no perfect forwarding of the axis objects. If that is needed, use the
95 /// \ref RHistEngine(std::vector<RAxisVariant> axes) "overload accepting a std::vector".
96 ///
97 /// \param[in] axes the axis objects, must have size > 0
98 explicit RHistEngine(std::initializer_list<RAxisVariant> axes) : RHistEngine(std::vector(axes)) {}
99
100 /// Construct a histogram engine.
101 ///
102 /// Note that there is no perfect forwarding of the axis objects. If that is needed, use the
103 /// \ref RHistEngine(std::vector<RAxisVariant> axes) "overload accepting a std::vector".
104 ///
105 /// \param[in] axis1 the first axis object
106 /// \param[in] axes the remaining axis objects
107 template <typename... Axes>
108 explicit RHistEngine(const RAxisVariant &axis1, const Axes &...axes)
109 : RHistEngine(std::vector<RAxisVariant>{axis1, axes...})
110 {
111 }
112
113 /// Construct a one-dimensional histogram engine with a regular axis.
114 ///
115 /// \param[in] nNormalBins the number of normal bins, must be > 0
116 /// \param[in] interval the axis interval (lower end inclusive, upper end exclusive)
117 /// \par See also
118 /// the \ref RRegularAxis::RRegularAxis(std::uint64_t nNormalBins, std::pair<double, double> interval, bool
119 /// enableFlowBins) "constructor of RRegularAxis"
120 RHistEngine(std::uint64_t nNormalBins, std::pair<double, double> interval)
122 {
123 }
124
125 /// The copy constructor is deleted.
126 ///
127 /// Copying all bin contents can be an expensive operation, depending on the number of bins. If required, users can
128 /// explicitly call Clone().
129 RHistEngine(const RHistEngine &) = delete;
130 /// Efficiently move construct a histogram engine.
131 ///
132 /// After this operation, the moved-from object is invalid.
134
135 /// The copy assignment operator is deleted.
136 ///
137 /// Copying all bin contents can be an expensive operation, depending on the number of bins. If required, users can
138 /// explicitly call Clone().
140 /// Efficiently move a histogram engine.
141 ///
142 /// After this operation, the moved-from object is invalid.
144
145 ~RHistEngine() = default;
146
147 /// \name Accessors
148 /// \{
149
150 const std::vector<RAxisVariant> &GetAxes() const { return fAxes.Get(); }
151 std::size_t GetNDimensions() const { return fAxes.GetNDimensions(); }
152 std::uint64_t GetTotalNBins() const { return fBinContents.size(); }
153
154 /// Get the content of a single bin.
155 ///
156 /// \code
157 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
158 /// std::array<ROOT::Experimental::RBinIndex, 2> indices = {3, 5};
159 /// int content = hist.GetBinContent(indices);
160 /// \endcode
161 ///
162 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
163 /// values. See also the class documentation of RBinIndex.
164 ///
165 /// Throws an exception if the number of indices does not match the axis configuration or the bin is not found.
166 ///
167 /// \param[in] indices the array of indices for each axis
168 /// \return the bin content
169 /// \par See also
170 /// the \ref GetBinContent(const A &... args) const "variadic function template overload" accepting arguments
171 /// directly
172 template <std::size_t N>
173 const BinContentType &GetBinContent(const std::array<RBinIndex, N> &indices) const
174 {
175 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
176 // be confusing for users.
177 if (N != GetNDimensions()) {
178 throw std::invalid_argument("invalid number of indices passed to GetBinContent");
179 }
181 if (!index.fValid) {
182 throw std::invalid_argument("bin not found in GetBinContent");
183 }
184 assert(index.fIndex < fBinContents.size());
185 return fBinContents[index.fIndex];
186 }
187
188 /// Get the content of a single bin.
189 ///
190 /// \code
191 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
192 /// std::vector<ROOT::Experimental::RBinIndex> indices = {3, 5};
193 /// int content = hist.GetBinContent(indices);
194 /// \endcode
195 ///
196 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
197 /// values. See also the class documentation of RBinIndex.
198 ///
199 /// Throws an exception if the number of indices does not match the axis configuration or the bin is not found.
200 ///
201 /// \param[in] indices the vector of indices for each axis
202 /// \return the bin content
203 /// \par See also
204 /// the \ref GetBinContent(const A &... args) const "variadic function template overload" accepting arguments
205 /// directly
206 const BinContentType &GetBinContent(const std::vector<RBinIndex> &indices) const
207 {
208 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
209 // be confusing for users.
210 if (indices.size() != GetNDimensions()) {
211 throw std::invalid_argument("invalid number of indices passed to GetBinContent");
212 }
214 if (!index.fValid) {
215 throw std::invalid_argument("bin not found in GetBinContent");
216 }
217 assert(index.fIndex < fBinContents.size());
218 return fBinContents[index.fIndex];
219 }
220
221 /// Get the content of a single bin.
222 ///
223 /// \code
224 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
225 /// int content = hist.GetBinContent(ROOT::Experimental::RBinIndex(3), ROOT::Experimental::RBinIndex(5));
226 /// // ... or construct the RBinIndex arguments implicitly from integers:
227 /// content = hist.GetBinContent(3, 5);
228 /// \endcode
229 ///
230 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
231 /// values. See also the class documentation of RBinIndex.
232 ///
233 /// Throws an exception if the number of arguments does not match the axis configuration or the bin is not found.
234 ///
235 /// \param[in] args the arguments for each axis
236 /// \return the bin content
237 /// \par See also
238 /// the function overloads accepting \ref GetBinContent(const std::array<RBinIndex, N> &indices) const "`std::array`"
239 /// or \ref GetBinContent(const std::vector<RBinIndex> &indices) const "`std::vector`"
240 template <typename... A>
241 const BinContentType &GetBinContent(const A &...args) const
242 {
243 std::array<RBinIndex, sizeof...(A)> indices{args...};
244 return GetBinContent(indices);
245 }
246
247 /// Get the multidimensional range of all bins.
248 ///
249 /// \return the multidimensional range
251
252 /// Set the content of a single bin.
253 ///
254 /// \code
255 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
256 /// std::array<ROOT::Experimental::RBinIndex, 2> indices = {3, 5};
257 /// int value = /* ... */;
258 /// hist.SetBinContent(indices, value);
259 /// \endcode
260 ///
261 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
262 /// values. See also the class documentation of RBinIndex.
263 ///
264 /// Throws an exception if the number of indices does not match the axis configuration or the bin is not found.
265 ///
266 /// \param[in] indices the array of indices for each axis
267 /// \param[in] value the new value of the bin content
268 /// \par See also
269 /// the \ref SetBinContent(const A &... args) "variadic function template overload" accepting arguments directly
270 template <std::size_t N, typename V>
271 void SetBinContent(const std::array<RBinIndex, N> &indices, const V &value)
272 {
273 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
274 // be confusing for users.
275 if (N != GetNDimensions()) {
276 throw std::invalid_argument("invalid number of indices passed to SetBinContent");
277 }
279 if (!index.fValid) {
280 throw std::invalid_argument("bin not found in SetBinContent");
281 }
282 assert(index.fIndex < fBinContents.size());
283 // To allow conversion, we have to accept value with a template type V to capture any argument. Otherwise it would
284 // select the variadic function template...
285 fBinContents[index.fIndex] = value;
286 }
287
288 /// \}
289 // End the group to ensure that all contained member functions are public.
290
291private:
292 template <typename... A, std::size_t... I>
293 void SetBinContentImpl(const std::tuple<A...> &args, std::index_sequence<I...>)
294 {
295 std::array<RBinIndex, sizeof...(A) - 1> indices{std::get<I>(args)...};
296 SetBinContent(indices, std::get<sizeof...(A) - 1>(args));
297 }
298
299public:
300 /// \name Accessors
301 /// \{
302
303 /// Set the content of a single bin.
304 ///
305 /// \code
306 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
307 /// int value = /* ... */;
308 /// hist.SetBinContent(ROOT::Experimental::RBinIndex(3), ROOT::Experimental::RBinIndex(5), value);
309 /// // ... or construct the RBinIndex arguments implicitly from integers:
310 /// hist.SetBinContent(3, 5, value);
311 /// \endcode
312 ///
313 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
314 /// values. See also the class documentation of RBinIndex.
315 ///
316 /// Throws an exception if the number of arguments does not match the axis configuration or the bin is not found.
317 ///
318 /// \param[in] args the arguments for each axis and the new value of the bin content
319 /// \par See also
320 /// the \ref SetBinContent(const std::array<RBinIndex, N> &indices, const V &value) "function overload" accepting
321 /// `std::array`
322 template <typename... A>
323 void SetBinContent(const A &...args)
324 {
325 auto t = std::forward_as_tuple(args...);
326 SetBinContentImpl(t, std::make_index_sequence<sizeof...(A) - 1>());
327 }
328
329 /// \}
330
331 /// Whether this histogram engine type supports weighted filling.
332 static constexpr bool SupportsWeightedFilling = !std::is_integral_v<BinContentType>;
333
334 // SupportsWeightedFilling is not included because it is static, which would mess up the subgrouping below "Public
335 // Member Functions".
336 /// \name Filling
337 /// \{
338
339 /// Fill an entry into the histogram.
340 ///
341 /// \code
342 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
343 /// auto args = std::make_tuple(8.5, 10.5);
344 /// hist.Fill(args);
345 /// \endcode
346 ///
347 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
348 /// discarded.
349 ///
350 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
351 /// converted for the axis type at run-time.
352 ///
353 /// \param[in] args the arguments for each axis
354 /// \par See also
355 /// the \ref Fill(const A &... args) "variadic function template overload" accepting arguments directly and the
356 /// \ref Fill(const std::tuple<A...> &args, RWeight weight) "overload for weighted filling"
357 template <typename... A>
358 void Fill(const std::tuple<A...> &args)
359 {
360 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
361 // be confusing for users.
362 if (sizeof...(A) != GetNDimensions()) {
363 throw std::invalid_argument("invalid number of arguments to Fill");
364 }
366 if (index.fValid) {
367 assert(index.fIndex < fBinContents.size());
368 fBinContents[index.fIndex]++;
369 }
370 }
371
372 /// Fill an entry into the histogram with a weight.
373 ///
374 /// This overload is not available for integral bin content types (see \ref SupportsWeightedFilling).
375 ///
376 /// \code
377 /// ROOT::Experimental::RHistEngine<float> hist({/* two dimensions */});
378 /// auto args = std::make_tuple(8.5, 10.5);
379 /// hist.Fill(args, ROOT::Experimental::RWeight(0.8));
380 /// \endcode
381 ///
382 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
383 /// discarded.
384 ///
385 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
386 /// converted for the axis type at run-time.
387 ///
388 /// \param[in] args the arguments for each axis
389 /// \param[in] weight the weight for this entry
390 /// \par See also
391 /// the \ref Fill(const A &... args) "variadic function template overload" accepting arguments directly and the
392 /// \ref Fill(const std::tuple<A...> &args) "overload for unweighted filling"
393 template <typename... A>
394 void Fill(const std::tuple<A...> &args, RWeight weight)
395 {
396 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
397
398 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
399 // be confusing for users.
400 if (sizeof...(A) != GetNDimensions()) {
401 throw std::invalid_argument("invalid number of arguments to Fill");
402 }
404 if (index.fValid) {
405 assert(index.fIndex < fBinContents.size());
406 fBinContents[index.fIndex] += weight.fValue;
407 }
408 }
409
410 /// Fill an entry into the histogram with a user-defined weight.
411 ///
412 /// This overload is only available for user-defined bin content types.
413 ///
414 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
415 /// discarded.
416 ///
417 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
418 /// converted for the axis type at run-time.
419 ///
420 /// \param[in] args the arguments for each axis
421 /// \param[in] weight the weight for this entry
422 template <typename... A, typename W>
423 void Fill(const std::tuple<A...> &args, const W &weight)
424 {
425 static_assert(std::is_class_v<BinContentType>,
426 "user-defined weight types are only supported for user-defined bin content types");
427
428 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
429 // be confusing for users.
430 if (sizeof...(A) != GetNDimensions()) {
431 throw std::invalid_argument("invalid number of arguments to Fill");
432 }
434 if (index.fValid) {
435 assert(index.fIndex < fBinContents.size());
436 fBinContents[index.fIndex] += weight;
437 }
438 }
439
440 /// Fill an entry into the histogram.
441 ///
442 /// \code
443 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
444 /// hist.Fill(8.5, 10.5);
445 /// \endcode
446 ///
447 /// For weighted filling, pass an RWeight as the last argument:
448 /// \code
449 /// ROOT::Experimental::RHistEngine<float> hist({/* two dimensions */});
450 /// hist.Fill(8.5, 10.5, ROOT::Experimental::RWeight(0.8));
451 /// \endcode
452 /// This is not available for integral bin content types (see \ref SupportsWeightedFilling).
453 ///
454 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
455 /// discarded.
456 ///
457 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
458 /// converted for the axis type at run-time.
459 ///
460 /// \param[in] args the arguments for each axis
461 /// \par See also
462 /// the function overloads accepting `std::tuple` \ref Fill(const std::tuple<A...> &args) "for unweighted filling"
463 /// and \ref Fill(const std::tuple<A...> &args, RWeight) "for weighted filling"
464 template <typename... A>
465 void Fill(const A &...args)
466 {
467 static_assert(sizeof...(A) >= 1, "need at least one argument to Fill");
468 if constexpr (sizeof...(A) >= 1) {
469 auto t = std::forward_as_tuple(args...);
470 if constexpr (std::is_same_v<typename Internal::LastType<A...>::type, RWeight>) {
471 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
472 static constexpr std::size_t N = sizeof...(A) - 1;
473 if (N != fAxes.GetNDimensions()) {
474 throw std::invalid_argument("invalid number of arguments to Fill");
475 }
476 RWeight weight = std::get<N>(t);
478 if (index.fValid) {
479 assert(index.fIndex < fBinContents.size());
480 fBinContents[index.fIndex] += weight.fValue;
481 }
482 } else {
483 Fill(t);
484 }
485 }
486 }
487
488 /// Fill an entry into the histogram using atomic instructions.
489 ///
490 /// \param[in] args the arguments for each axis
491 /// \see Fill(const std::tuple<A...> &args)
492 template <typename... A>
493 void FillAtomic(const std::tuple<A...> &args)
494 {
495 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
496 // be confusing for users.
497 if (sizeof...(A) != GetNDimensions()) {
498 throw std::invalid_argument("invalid number of arguments to Fill");
499 }
501 if (index.fValid) {
502 assert(index.fIndex < fBinContents.size());
504 }
505 }
506
507 /// Fill an entry into the histogram with a weight using atomic instructions.
508 ///
509 /// This overload is not available for integral bin content types (see \ref SupportsWeightedFilling).
510 ///
511 /// \param[in] args the arguments for each axis
512 /// \param[in] weight the weight for this entry
513 /// \see Fill(const std::tuple<A...> &args, RWeight weight)
514 template <typename... A>
515 void FillAtomic(const std::tuple<A...> &args, RWeight weight)
516 {
517 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
518
519 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
520 // be confusing for users.
521 if (sizeof...(A) != GetNDimensions()) {
522 throw std::invalid_argument("invalid number of arguments to Fill");
523 }
525 if (index.fValid) {
526 assert(index.fIndex < fBinContents.size());
528 }
529 }
530
531 /// Fill an entry into the histogram with a user-defined weight using atomic instructions.
532 ///
533 /// This overload is only available for user-defined bin content types.
534 ///
535 /// \param[in] args the arguments for each axis
536 /// \param[in] weight the weight for this entry
537 /// \see Fill(const std::tuple<A...> &args, const W &weight)
538 template <typename... A, typename W>
539 void FillAtomic(const std::tuple<A...> &args, const W &weight)
540 {
541 static_assert(std::is_class_v<BinContentType>,
542 "user-defined weight types are only supported for user-defined bin content types");
543
544 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
545 // be confusing for users.
546 if (sizeof...(A) != GetNDimensions()) {
547 throw std::invalid_argument("invalid number of arguments to Fill");
548 }
550 if (index.fValid) {
551 assert(index.fIndex < fBinContents.size());
552 Internal::AtomicAdd(&fBinContents[index.fIndex], weight);
553 }
554 }
555
556 /// Fill an entry into the histogram using atomic instructions.
557 ///
558 /// \param[in] args the arguments for each axis
559 /// \see Fill(const A &...args)
560 template <typename... A>
561 void FillAtomic(const A &...args)
562 {
563 static_assert(sizeof...(A) >= 1, "need at least one argument to Fill");
564 if constexpr (sizeof...(A) >= 1) {
565 auto t = std::forward_as_tuple(args...);
566 if constexpr (std::is_same_v<typename Internal::LastType<A...>::type, RWeight>) {
567 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
568 static constexpr std::size_t N = sizeof...(A) - 1;
569 if (N != fAxes.GetNDimensions()) {
570 throw std::invalid_argument("invalid number of arguments to Fill");
571 }
572 RWeight weight = std::get<N>(t);
574 if (index.fValid) {
575 assert(index.fIndex < fBinContents.size());
577 }
578 } else {
579 FillAtomic(t);
580 }
581 }
582 }
583
584 /// \}
585 /// \name Operations
586 /// \{
587
588 /// Add all bin contents of another histogram.
589 ///
590 /// Throws an exception if the axes configurations are not identical.
591 ///
592 /// \param[in] other another histogram
593 void Add(const RHistEngine &other)
594 {
595 if (fAxes != other.fAxes) {
596 throw std::invalid_argument("axes configurations not identical in Add");
597 }
598 for (std::size_t i = 0; i < fBinContents.size(); i++) {
599 fBinContents[i] += other.fBinContents[i];
600 }
601 }
602
603 /// Add all bin contents of another histogram using atomic instructions.
604 ///
605 /// Throws an exception if the axes configurations are not identical.
606 ///
607 /// \param[in] other another histogram that must not be modified during the operation
609 {
610 if (fAxes != other.fAxes) {
611 throw std::invalid_argument("axes configurations not identical in AddAtomic");
612 }
613 for (std::size_t i = 0; i < fBinContents.size(); i++) {
614 Internal::AtomicAdd(&fBinContents[i], other.fBinContents[i]);
615 }
616 }
617
618 /// Clear all bin contents.
619 void Clear()
620 {
621 for (std::size_t i = 0; i < fBinContents.size(); i++) {
622 fBinContents[i] = {};
623 }
624 }
625
626 /// Clone this histogram engine.
627 ///
628 /// Copying all bin contents can be an expensive operation, depending on the number of bins.
629 ///
630 /// \return the cloned object
632 {
634 for (std::size_t i = 0; i < fBinContents.size(); i++) {
635 h.fBinContents[i] = fBinContents[i];
636 }
637 return h;
638 }
639
640 /// Convert this histogram engine to a different bin content type.
641 ///
642 /// There is no bounds checking to make sure that the converted values can be represented. Note that it is not
643 /// possible to convert to RBinWithError since the information about individual weights has been lost since filling.
644 ///
645 /// Converting all bin contents can be an expensive operation, depending on the number of bins.
646 ///
647 /// \return the converted object
648 template <typename U>
650 {
652 for (std::size_t i = 0; i < fBinContents.size(); i++) {
653 h.fBinContents[i] = static_cast<U>(fBinContents[i]);
654 }
655 return h;
656 }
657
658 /// Scale all histogram bin contents.
659 ///
660 /// This method is not available for integral bin content types.
661 ///
662 /// \param[in] factor the scale factor
663 void Scale(double factor)
664 {
665 static_assert(!std::is_integral_v<BinContentType>, "scaling is not supported for integral bin content types");
666 for (std::size_t i = 0; i < fBinContents.size(); i++) {
667 fBinContents[i] *= factor;
668 }
669 }
670
671 /// \}
672 // End the group to ensure that all contained member functions are public.
673
674private:
675 RHistEngine SliceImpl(const std::vector<RSliceSpec> &sliceSpecs, bool &dropped) const
676 {
677 if (sliceSpecs.size() != GetNDimensions()) {
678 throw std::invalid_argument("invalid number of specifications passed to Slice");
679 }
680
681 // Slice the axes.
682 std::vector<RAxisVariant> axes;
683 for (std::size_t i = 0; i < sliceSpecs.size(); i++) {
684 // A sum operation makes the dimension disappear.
685 if (sliceSpecs[i].GetOperationSum() == nullptr) {
686 axes.push_back(fAxes.Get()[i].Slice(sliceSpecs[i]));
687 }
688 }
689 if (axes.empty()) {
690 throw std::invalid_argument("summing across all dimensions is not supported");
691 }
692
693 RHistEngine sliced(std::move(axes));
694
695 // Create the helper objects to map the bin contents to the sliced histogram.
697 assert(mapper.GetMappedDimensionality() == sliced.GetNDimensions());
698 std::vector<RBinIndex> mappedIndices(mapper.GetMappedDimensionality());
699
701 auto origRangeIt = origRange.begin();
702
703 for (std::size_t i = 0; i < fBinContents.size(); i++) {
704 const auto &origIndices = *origRangeIt;
705#ifndef NDEBUG
706 // Verify that the original indices correspond to the iteration variable.
708 assert(origIndex.fValid);
709 assert(origIndex.fIndex == i);
710#endif
711
713 if (success) {
714 RLinearizedIndex mappedIndex = sliced.fAxes.ComputeGlobalIndex(mappedIndices);
715 assert(mappedIndex.fValid);
716 sliced.fBinContents[mappedIndex.fIndex] += fBinContents[i];
717 } else {
718 dropped = true;
719 }
720 ++origRangeIt;
721 }
722
723 return sliced;
724 }
725
726public:
727 /// \name Operations
728 /// \{
729
730 /// Slice this histogram with an RSliceSpec per dimension.
731 ///
732 /// With a range, only the specified bins are retained. All other bin contents are transferred to the underflow and
733 /// overflow bins:
734 /// \code
735 /// ROOT::Experimental::RHistEngine<int> hist(/* one dimension */);
736 /// // Fill the histogram with a number of entries...
737 /// auto sliced = hist.Slice({hist.GetAxes()[0].GetNormalRange(1, 5)});
738 /// // The returned histogram will have 4 normal bins, an underflow and an overflow bin.
739 /// \endcode
740 ///
741 /// Slicing can also perform operations per dimension, see RSliceSpec. RSliceSpec::ROperationRebin allows to rebin
742 /// the histogram axis, grouping a number of normal bins into a new one:
743 /// \code
744 /// ROOT::Experimental::RHistEngine<int> hist(/* one dimension */);
745 /// // Fill the histogram with a number of entries...
746 /// auto rebinned = hist.Slice(ROOT::Experimental::RSliceSpec::ROperationRebin(2));
747 /// // The returned histogram has groups of two normal bins merged.
748 /// \endcode
749 ///
750 /// RSliceSpec::ROperationSum sums the bin contents along that axis, which allows to project to a lower-dimensional
751 /// histogram:
752 /// \code
753 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
754 /// // Fill the histogram with a number of entries...
755 /// auto projected = hist.Slice(ROOT::Experimental::RSliceSpec{}, ROOT::Experimental::RSliceSpec::ROperationSum{});
756 /// // The returned histogram has one dimension, with bin contents summed along the second axis.
757 /// \endcode
758 /// Note that it is not allowed to sum along all histogram axes because the return value would be a scalar.
759 ///
760 /// Ranges and operations can be combined. In that case, the range is applied before the operation.
761 ///
762 /// \param[in] sliceSpecs the slice specifications for each axis
763 /// \return the sliced histogram
764 /// \par See also
765 /// the \ref Slice(const A &... args) const "variadic function template overload" accepting arguments directly
766 RHistEngine Slice(const std::vector<RSliceSpec> &sliceSpecs) const
767 {
768 bool dropped = false;
770 }
771
772 /// Slice this histogram with an RSliceSpec per dimension.
773 ///
774 /// With a range, only the specified bins are retained. All other bin contents are transferred to the underflow and
775 /// overflow bins:
776 /// \code
777 /// ROOT::Experimental::RHistEngine<int> hist(/* one dimension */);
778 /// // Fill the histogram with a number of entries...
779 /// auto sliced = hist.Slice(hist.GetAxes()[0].GetNormalRange(1, 5));
780 /// // The returned histogram will have 4 normal bins, an underflow and an overflow bin.
781 /// \endcode
782 ///
783 /// Slicing can also perform operations per dimension, see RSliceSpec. RSliceSpec::ROperationRebin allows to rebin
784 /// the histogram axis, grouping a number of normal bins into a new one:
785 /// \code
786 /// ROOT::Experimental::RHistEngine<int> hist(/* one dimension */);
787 /// // Fill the histogram with a number of entries...
788 /// auto rebinned = hist.Slice(ROOT::Experimental::RSliceSpec::ROperationRebin(2));
789 /// // The returned histogram has groups of two normal bins merged.
790 /// \endcode
791 ///
792 /// RSliceSpec::ROperationSum sums the bin contents along that axis, which allows to project to a lower-dimensional
793 /// histogram:
794 /// \code
795 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
796 /// // Fill the histogram with a number of entries...
797 /// auto projected = hist.Slice(ROOT::Experimental::RSliceSpec{}, ROOT::Experimental::RSliceSpec::ROperationSum{});
798 /// // The returned histogram has one dimension, with bin contents summed along the second axis.
799 /// \endcode
800 /// Note that it is not allowed to sum along all histogram axes because the return value would be a scalar.
801 ///
802 /// Ranges and operations can be combined. In that case, the range is applied before the operation.
803 ///
804 /// \param[in] args the arguments for each axis
805 /// \return the sliced histogram
806 /// \par See also
807 /// the \ref Slice(const std::vector<RSliceSpec> &sliceSpecs) const "function overload" accepting `std::vector`
808 template <typename... A>
809 RHistEngine Slice(const A &...args) const
810 {
811 std::vector<RSliceSpec> sliceSpecs{args...};
812 return Slice(sliceSpecs);
813 }
814
815 /// \}
816
817 /// %ROOT Streamer function to throw when trying to store an object of this class.
818 void Streamer(TBuffer &) { throw std::runtime_error("unable to store RHistEngine"); }
819};
820
821} // namespace Experimental
822} // namespace ROOT
823
824#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:40
std::size_t GetNDimensions() const
Definition RAxes.hxx:55
RLinearizedIndex ComputeGlobalIndexImpl(std::size_t index, const std::tuple< A... > &args) const
Definition RAxes.hxx:77
RLinearizedIndex ComputeGlobalIndex(const std::tuple< A... > &args) const
Compute the global index for all axes.
Definition RAxes.hxx:131
RBinIndexMultiDimRange GetFullMultiDimRange() const
Get the multidimensional range of all bins.
Definition RAxes.hxx:195
std::uint64_t ComputeTotalNBins() const
Compute the total number of bins for all axes.
Definition RAxes.hxx:66
const std::vector< RAxisVariant > & Get() const
Definition RAxes.hxx:56
Mapper of bin indices for slice operations.
A variant of all supported axis types.
A multidimensional range of bin indices.
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.
RHistEngine Slice(const std::vector< RSliceSpec > &sliceSpecs) const
Slice this histogram with an RSliceSpec per dimension.
RHistEngine(const RAxisVariant &axis1, const Axes &...axes)
Construct a histogram engine.
void Fill(const A &...args)
Fill an entry into the histogram.
const std::vector< RAxisVariant > & GetAxes() const
RBinIndexMultiDimRange GetFullMultiDimRange() const
Get the multidimensional range of all bins.
RHistEngine Clone() const
Clone this histogram engine.
RHistEngine & operator=(RHistEngine &&)=default
Efficiently move a histogram engine.
void SetBinContent(const A &...args)
Set the content of a single bin.
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(std::uint64_t nNormalBins, std::pair< double, double > interval)
Construct a one-dimensional histogram engine with a regular axis.
RHistEngine & operator=(const RHistEngine &)=delete
The copy assignment operator is deleted.
const BinContentType & GetBinContent(const std::vector< RBinIndex > &indices) const
Get the content of a single bin.
RHistEngine(RHistEngine &&)=default
Efficiently move construct a histogram engine.
RHistEngine(const RHistEngine &)=delete
The copy constructor is deleted.
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 SliceImpl(const std::vector< RSliceSpec > &sliceSpecs, bool &dropped) const
void SetBinContent(const std::array< RBinIndex, N > &indices, const V &value)
Set the content of a single bin.
void AddAtomic(const RHistEngine &other)
Add all bin contents of another histogram using atomic instructions.
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 Add(const RHistEngine &other)
Add all bin contents of another histogram.
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 Slice(const A &...args) const
Slice this histogram with an RSliceSpec per dimension.
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.
void SetBinContentImpl(const std::tuple< A... > &args, std::index_sequence< I... >)
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(std::initializer_list< RAxisVariant > axes)
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.
RHistEngine< U > Convert() const
Convert this histogram engine to a different bin content type.
A histogram for aggregation of data along multiple dimensions.
Definition RHist.hxx:67
A regular axis with equidistant bins in the interval .
const_iterator begin() const
Buffer base class used for serializing objects.
Definition TBuffer.h:43
#define I(x, y, z)
std::enable_if_t< std::is_arithmetic_v< T > > AtomicInc(T *ptr)
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