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