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