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 const std::vector<RAxisVariant> &GetAxes() const { return fAxes.Get(); }
147 std::size_t GetNDimensions() const { return fAxes.GetNDimensions(); }
148 std::uint64_t GetTotalNBins() const { return fBinContents.size(); }
149
150 /// Get the content of a single bin.
151 ///
152 /// \code
153 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
154 /// std::array<ROOT::Experimental::RBinIndex, 2> indices = {3, 5};
155 /// int content = hist.GetBinContent(indices);
156 /// \endcode
157 ///
158 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
159 /// values. See also the class documentation of RBinIndex.
160 ///
161 /// Throws an exception if the number of indices does not match the axis configuration or the bin is not found.
162 ///
163 /// \param[in] indices the array of indices for each axis
164 /// \return the bin content
165 /// \par See also
166 /// the \ref GetBinContent(const A &... args) const "variadic function template overload" accepting arguments
167 /// directly
168 template <std::size_t N>
169 const BinContentType &GetBinContent(const std::array<RBinIndex, N> &indices) const
170 {
171 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
172 // be confusing for users.
173 if (N != GetNDimensions()) {
174 throw std::invalid_argument("invalid number of indices passed to GetBinContent");
175 }
177 if (!index.fValid) {
178 throw std::invalid_argument("bin not found in GetBinContent");
179 }
180 assert(index.fIndex < fBinContents.size());
181 return fBinContents[index.fIndex];
182 }
183
184 /// Get the content of a single bin.
185 ///
186 /// \code
187 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
188 /// std::vector<ROOT::Experimental::RBinIndex> indices = {3, 5};
189 /// int content = hist.GetBinContent(indices);
190 /// \endcode
191 ///
192 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
193 /// values. See also the class documentation of RBinIndex.
194 ///
195 /// Throws an exception if the number of indices does not match the axis configuration or the bin is not found.
196 ///
197 /// \param[in] indices the vector of indices for each axis
198 /// \return the bin content
199 /// \par See also
200 /// the \ref GetBinContent(const A &... args) const "variadic function template overload" accepting arguments
201 /// directly
202 const BinContentType &GetBinContent(const std::vector<RBinIndex> &indices) const
203 {
204 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
205 // be confusing for users.
206 if (indices.size() != GetNDimensions()) {
207 throw std::invalid_argument("invalid number of indices passed to GetBinContent");
208 }
210 if (!index.fValid) {
211 throw std::invalid_argument("bin not found in GetBinContent");
212 }
213 assert(index.fIndex < fBinContents.size());
214 return fBinContents[index.fIndex];
215 }
216
217 /// Get the content of a single bin.
218 ///
219 /// \code
220 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
221 /// int content = hist.GetBinContent(ROOT::Experimental::RBinIndex(3), ROOT::Experimental::RBinIndex(5));
222 /// // ... or construct the RBinIndex arguments implicitly from integers:
223 /// content = hist.GetBinContent(3, 5);
224 /// \endcode
225 ///
226 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
227 /// values. See also the class documentation of RBinIndex.
228 ///
229 /// Throws an exception if the number of arguments does not match the axis configuration or the bin is not found.
230 ///
231 /// \param[in] args the arguments for each axis
232 /// \return the bin content
233 /// \par See also
234 /// the function overloads accepting \ref GetBinContent(const std::array<RBinIndex, N> &indices) const "`std::array`"
235 /// or \ref GetBinContent(const std::vector<RBinIndex> &indices) const "`std::vector`"
236 template <typename... A>
237 const BinContentType &GetBinContent(const A &...args) const
238 {
239 std::array<RBinIndex, sizeof...(A)> indices{args...};
240 return GetBinContent(indices);
241 }
242
243 /// Get the multidimensional range of all bins.
244 ///
245 /// \return the multidimensional range
247
248 /// Set the content of a single bin.
249 ///
250 /// \code
251 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
252 /// std::array<ROOT::Experimental::RBinIndex, 2> indices = {3, 5};
253 /// int value = /* ... */;
254 /// hist.SetBinContent(indices, value);
255 /// \endcode
256 ///
257 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
258 /// values. See also the class documentation of RBinIndex.
259 ///
260 /// Throws an exception if the number of indices does not match the axis configuration or the bin is not found.
261 ///
262 /// \param[in] indices the array of indices for each axis
263 /// \param[in] value the new value of the bin content
264 /// \par See also
265 /// the \ref SetBinContent(const A &... args) const "variadic function template overload" accepting arguments
266 /// directly
267 template <std::size_t N, typename V>
268 void SetBinContent(const std::array<RBinIndex, N> &indices, const V &value)
269 {
270 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
271 // be confusing for users.
272 if (N != GetNDimensions()) {
273 throw std::invalid_argument("invalid number of indices passed to SetBinContent");
274 }
276 if (!index.fValid) {
277 throw std::invalid_argument("bin not found in SetBinContent");
278 }
279 assert(index.fIndex < fBinContents.size());
280 // To allow conversion, we have to accept value with a template type V to capture any argument. Otherwise it would
281 // select the variadic function template...
282 fBinContents[index.fIndex] = value;
283 }
284
285private:
286 template <typename... A, std::size_t... I>
287 void SetBinContentImpl(const std::tuple<A...> &args, std::index_sequence<I...>)
288 {
289 std::array<RBinIndex, sizeof...(A) - 1> indices{std::get<I>(args)...};
290 SetBinContent(indices, std::get<sizeof...(A) - 1>(args));
291 }
292
293public:
294 /// Set the content of a single bin.
295 ///
296 /// \code
297 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
298 /// int value = /* ... */;
299 /// hist.SetBinContent(ROOT::Experimental::RBinIndex(3), ROOT::Experimental::RBinIndex(5), value);
300 /// // ... or construct the RBinIndex arguments implicitly from integers:
301 /// hist.SetBinContent(3, 5, value);
302 /// \endcode
303 ///
304 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
305 /// values. See also the class documentation of RBinIndex.
306 ///
307 /// Throws an exception if the number of arguments does not match the axis configuration or the bin is not found.
308 ///
309 /// \param[in] args the arguments for each axis and the new value of the bin content
310 /// \par See also
311 /// the \ref SetBinContent(const std::array<RBinIndex, N> &indices, const V &value) const "function overload"
312 /// accepting `std::array`
313 template <typename... A>
314 void SetBinContent(const A &...args)
315 {
316 auto t = std::forward_as_tuple(args...);
317 SetBinContentImpl(t, std::make_index_sequence<sizeof...(A) - 1>());
318 }
319
320 /// Add all bin contents of another histogram.
321 ///
322 /// Throws an exception if the axes configurations are not identical.
323 ///
324 /// \param[in] other another histogram
325 void Add(const RHistEngine &other)
326 {
327 if (fAxes != other.fAxes) {
328 throw std::invalid_argument("axes configurations not identical in Add");
329 }
330 for (std::size_t i = 0; i < fBinContents.size(); i++) {
331 fBinContents[i] += other.fBinContents[i];
332 }
333 }
334
335 /// Add all bin contents of another histogram using atomic instructions.
336 ///
337 /// Throws an exception if the axes configurations are not identical.
338 ///
339 /// \param[in] other another histogram that must not be modified during the operation
341 {
342 if (fAxes != other.fAxes) {
343 throw std::invalid_argument("axes configurations not identical in AddAtomic");
344 }
345 for (std::size_t i = 0; i < fBinContents.size(); i++) {
346 Internal::AtomicAdd(&fBinContents[i], other.fBinContents[i]);
347 }
348 }
349
350 /// Clear all bin contents.
351 void Clear()
352 {
353 for (std::size_t i = 0; i < fBinContents.size(); i++) {
354 fBinContents[i] = {};
355 }
356 }
357
358 /// Clone this histogram engine.
359 ///
360 /// Copying all bin contents can be an expensive operation, depending on the number of bins.
361 ///
362 /// \return the cloned object
364 {
366 for (std::size_t i = 0; i < fBinContents.size(); i++) {
367 h.fBinContents[i] = fBinContents[i];
368 }
369 return h;
370 }
371
372 /// Convert this histogram engine to a different bin content type.
373 ///
374 /// There is no bounds checking to make sure that the converted values can be represented. Note that it is not
375 /// possible to convert to RBinWithError since the information about individual weights has been lost since filling.
376 ///
377 /// Converting all bin contents can be an expensive operation, depending on the number of bins.
378 ///
379 /// \return the converted object
380 template <typename U>
382 {
384 for (std::size_t i = 0; i < fBinContents.size(); i++) {
385 h.fBinContents[i] = static_cast<U>(fBinContents[i]);
386 }
387 return h;
388 }
389
390 /// Whether this histogram engine type supports weighted filling.
391 static constexpr bool SupportsWeightedFilling = !std::is_integral_v<BinContentType>;
392
393 /// Fill an entry into the histogram.
394 ///
395 /// \code
396 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
397 /// auto args = std::make_tuple(8.5, 10.5);
398 /// hist.Fill(args);
399 /// \endcode
400 ///
401 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
402 /// discarded.
403 ///
404 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
405 /// converted for the axis type at run-time.
406 ///
407 /// \param[in] args the arguments for each axis
408 /// \par See also
409 /// the \ref Fill(const A &... args) "variadic function template overload" accepting arguments directly and the
410 /// \ref Fill(const std::tuple<A...> &args, RWeight weight) "overload for weighted filling"
411 template <typename... A>
412 void Fill(const std::tuple<A...> &args)
413 {
414 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
415 // be confusing for users.
416 if (sizeof...(A) != GetNDimensions()) {
417 throw std::invalid_argument("invalid number of arguments to Fill");
418 }
420 if (index.fValid) {
421 assert(index.fIndex < fBinContents.size());
422 fBinContents[index.fIndex]++;
423 }
424 }
425
426 /// Fill an entry into the histogram with a weight.
427 ///
428 /// This overload is not available for integral bin content types (see \ref SupportsWeightedFilling).
429 ///
430 /// \code
431 /// ROOT::Experimental::RHistEngine<float> hist({/* two dimensions */});
432 /// auto args = std::make_tuple(8.5, 10.5);
433 /// hist.Fill(args, ROOT::Experimental::RWeight(0.8));
434 /// \endcode
435 ///
436 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
437 /// discarded.
438 ///
439 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
440 /// converted for the axis type at run-time.
441 ///
442 /// \param[in] args the arguments for each axis
443 /// \param[in] weight the weight for this entry
444 /// \par See also
445 /// the \ref Fill(const A &... args) "variadic function template overload" accepting arguments directly and the
446 /// \ref Fill(const std::tuple<A...> &args) "overload for unweighted filling"
447 template <typename... A>
448 void Fill(const std::tuple<A...> &args, RWeight weight)
449 {
450 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
451
452 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
453 // be confusing for users.
454 if (sizeof...(A) != GetNDimensions()) {
455 throw std::invalid_argument("invalid number of arguments to Fill");
456 }
458 if (index.fValid) {
459 assert(index.fIndex < fBinContents.size());
460 fBinContents[index.fIndex] += weight.fValue;
461 }
462 }
463
464 /// Fill an entry into the histogram with a user-defined weight.
465 ///
466 /// This overload is only available for user-defined bin content types.
467 ///
468 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
469 /// discarded.
470 ///
471 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
472 /// converted for the axis type at run-time.
473 ///
474 /// \param[in] args the arguments for each axis
475 /// \param[in] weight the weight for this entry
476 template <typename... A, typename W>
477 void Fill(const std::tuple<A...> &args, const W &weight)
478 {
479 static_assert(std::is_class_v<BinContentType>,
480 "user-defined weight types are only supported for user-defined bin content types");
481
482 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
483 // be confusing for users.
484 if (sizeof...(A) != GetNDimensions()) {
485 throw std::invalid_argument("invalid number of arguments to Fill");
486 }
488 if (index.fValid) {
489 assert(index.fIndex < fBinContents.size());
490 fBinContents[index.fIndex] += weight;
491 }
492 }
493
494 /// Fill an entry into the histogram.
495 ///
496 /// \code
497 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
498 /// hist.Fill(8.5, 10.5);
499 /// \endcode
500 ///
501 /// For weighted filling, pass an RWeight as the last argument:
502 /// \code
503 /// ROOT::Experimental::RHistEngine<float> hist({/* two dimensions */});
504 /// hist.Fill(8.5, 10.5, ROOT::Experimental::RWeight(0.8));
505 /// \endcode
506 /// This is not available for integral bin content types (see \ref SupportsWeightedFilling).
507 ///
508 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
509 /// discarded.
510 ///
511 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
512 /// converted for the axis type at run-time.
513 ///
514 /// \param[in] args the arguments for each axis
515 /// \par See also
516 /// the function overloads accepting `std::tuple` \ref Fill(const std::tuple<A...> &args) "for unweighted filling"
517 /// and \ref Fill(const std::tuple<A...> &args, RWeight) "for weighted filling"
518 template <typename... A>
519 void Fill(const A &...args)
520 {
521 static_assert(sizeof...(A) >= 1, "need at least one argument to Fill");
522 if constexpr (sizeof...(A) >= 1) {
523 auto t = std::forward_as_tuple(args...);
524 if constexpr (std::is_same_v<typename Internal::LastType<A...>::type, RWeight>) {
525 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
526 static constexpr std::size_t N = sizeof...(A) - 1;
527 if (N != fAxes.GetNDimensions()) {
528 throw std::invalid_argument("invalid number of arguments to Fill");
529 }
530 RWeight weight = std::get<N>(t);
532 if (index.fValid) {
533 assert(index.fIndex < fBinContents.size());
534 fBinContents[index.fIndex] += weight.fValue;
535 }
536 } else {
537 Fill(t);
538 }
539 }
540 }
541
542 /// Fill an entry into the histogram using atomic instructions.
543 ///
544 /// \param[in] args the arguments for each axis
545 /// \see Fill(const std::tuple<A...> &args)
546 template <typename... A>
547 void FillAtomic(const std::tuple<A...> &args)
548 {
549 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
550 // be confusing for users.
551 if (sizeof...(A) != GetNDimensions()) {
552 throw std::invalid_argument("invalid number of arguments to Fill");
553 }
555 if (index.fValid) {
556 assert(index.fIndex < fBinContents.size());
558 }
559 }
560
561 /// Fill an entry into the histogram with a weight using atomic instructions.
562 ///
563 /// This overload is not available for integral bin content types (see \ref SupportsWeightedFilling).
564 ///
565 /// \param[in] args the arguments for each axis
566 /// \param[in] weight the weight for this entry
567 /// \see Fill(const std::tuple<A...> &args, RWeight weight)
568 template <typename... A>
569 void FillAtomic(const std::tuple<A...> &args, RWeight weight)
570 {
571 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
572
573 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
574 // be confusing for users.
575 if (sizeof...(A) != GetNDimensions()) {
576 throw std::invalid_argument("invalid number of arguments to Fill");
577 }
579 if (index.fValid) {
580 assert(index.fIndex < fBinContents.size());
582 }
583 }
584
585 /// Fill an entry into the histogram with a user-defined weight using atomic instructions.
586 ///
587 /// This overload is only available for user-defined bin content types.
588 ///
589 /// \param[in] args the arguments for each axis
590 /// \param[in] weight the weight for this entry
591 /// \see Fill(const std::tuple<A...> &args, const W &weight)
592 template <typename... A, typename W>
593 void FillAtomic(const std::tuple<A...> &args, const W &weight)
594 {
595 static_assert(std::is_class_v<BinContentType>,
596 "user-defined weight types are only supported for user-defined bin content types");
597
598 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
599 // be confusing for users.
600 if (sizeof...(A) != GetNDimensions()) {
601 throw std::invalid_argument("invalid number of arguments to Fill");
602 }
604 if (index.fValid) {
605 assert(index.fIndex < fBinContents.size());
606 Internal::AtomicAdd(&fBinContents[index.fIndex], weight);
607 }
608 }
609
610 /// Fill an entry into the histogram using atomic instructions.
611 ///
612 /// \param[in] args the arguments for each axis
613 /// \see Fill(const A &...args)
614 template <typename... A>
615 void FillAtomic(const A &...args)
616 {
617 static_assert(sizeof...(A) >= 1, "need at least one argument to Fill");
618 if constexpr (sizeof...(A) >= 1) {
619 auto t = std::forward_as_tuple(args...);
620 if constexpr (std::is_same_v<typename Internal::LastType<A...>::type, RWeight>) {
621 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
622 static constexpr std::size_t N = sizeof...(A) - 1;
623 if (N != fAxes.GetNDimensions()) {
624 throw std::invalid_argument("invalid number of arguments to Fill");
625 }
626 RWeight weight = std::get<N>(t);
628 if (index.fValid) {
629 assert(index.fIndex < fBinContents.size());
631 }
632 } else {
633 FillAtomic(t);
634 }
635 }
636 }
637
638 /// Scale all histogram bin contents.
639 ///
640 /// This method is not available for integral bin content types.
641 ///
642 /// \param[in] factor the scale factor
643 void Scale(double factor)
644 {
645 static_assert(!std::is_integral_v<BinContentType>, "scaling is not supported for integral bin content types");
646 for (std::size_t i = 0; i < fBinContents.size(); i++) {
647 fBinContents[i] *= factor;
648 }
649 }
650
651private:
652 RHistEngine SliceImpl(const std::vector<RSliceSpec> &sliceSpecs, bool &dropped) const
653 {
654 if (sliceSpecs.size() != GetNDimensions()) {
655 throw std::invalid_argument("invalid number of specifications passed to Slice");
656 }
657
658 // Slice the axes.
659 std::vector<RAxisVariant> axes;
660 for (std::size_t i = 0; i < sliceSpecs.size(); i++) {
661 // A sum operation makes the dimension disappear.
662 if (sliceSpecs[i].GetOperationSum() == nullptr) {
663 axes.push_back(fAxes.Get()[i].Slice(sliceSpecs[i]));
664 }
665 }
666 if (axes.empty()) {
667 throw std::invalid_argument("summing across all dimensions is not supported");
668 }
669
670 RHistEngine sliced(std::move(axes));
671
672 // Create the helper objects to map the bin contents to the sliced histogram.
674 assert(mapper.GetMappedDimensionality() == sliced.GetNDimensions());
675 std::vector<RBinIndex> mappedIndices(mapper.GetMappedDimensionality());
676
678 auto origRangeIt = origRange.begin();
679
680 for (std::size_t i = 0; i < fBinContents.size(); i++) {
681 const auto &origIndices = *origRangeIt;
682#ifndef NDEBUG
683 // Verify that the original indices correspond to the iteration variable.
685 assert(origIndex.fValid);
686 assert(origIndex.fIndex == i);
687#endif
688
690 if (success) {
691 RLinearizedIndex mappedIndex = sliced.fAxes.ComputeGlobalIndex(mappedIndices);
692 assert(mappedIndex.fValid);
693 sliced.fBinContents[mappedIndex.fIndex] += fBinContents[i];
694 } else {
695 dropped = true;
696 }
697 ++origRangeIt;
698 }
699
700 return sliced;
701 }
702
703public:
704 /// Slice this histogram with an RSliceSpec per dimension.
705 ///
706 /// With a range, only the specified bins are retained. All other bin contents are transferred to the underflow and
707 /// overflow bins:
708 /// \code
709 /// ROOT::Experimental::RHistEngine<int> hist(/* one dimension */);
710 /// // Fill the histogram with a number of entries...
711 /// auto sliced = hist.Slice({hist.GetAxes()[0].GetNormalRange(1, 5)});
712 /// // The returned histogram will have 4 normal bins, an underflow and an overflow bin.
713 /// \endcode
714 ///
715 /// Slicing can also perform operations per dimension, see RSliceSpec. RSliceSpec::ROperationRebin allows to rebin
716 /// the histogram axis, grouping a number of normal bins into a new one:
717 /// \code
718 /// ROOT::Experimental::RHistEngine<int> hist(/* one dimension */);
719 /// // Fill the histogram with a number of entries...
720 /// auto rebinned = hist.Slice(ROOT::Experimental::RSliceSpec::ROperationRebin(2));
721 /// // The returned histogram has groups of two normal bins merged.
722 /// \endcode
723 ///
724 /// RSliceSpec::ROperationSum sums the bin contents along that axis, which allows to project to a lower-dimensional
725 /// histogram:
726 /// \code
727 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
728 /// // Fill the histogram with a number of entries...
729 /// auto projected = hist.Slice(ROOT::Experimental::RSliceSpec{}, ROOT::Experimental::RSliceSpec::ROperationSum{});
730 /// // The returned histogram has one dimension, with bin contents summed along the second axis.
731 /// \endcode
732 /// Note that it is not allowed to sum along all histogram axes because the return value would be a scalar.
733 ///
734 /// Ranges and operations can be combined. In that case, the range is applied before the operation.
735 ///
736 /// \param[in] sliceSpecs the slice specifications for each axis
737 /// \return the sliced histogram
738 /// \par See also
739 /// the \ref Slice(const A &... args) const "variadic function template overload" accepting arguments directly
740 RHistEngine Slice(const std::vector<RSliceSpec> &sliceSpecs) const
741 {
742 bool dropped = false;
744 }
745
746 /// Slice this histogram with an RSliceSpec per dimension.
747 ///
748 /// With a range, only the specified bins are retained. All other bin contents are transferred to the underflow and
749 /// overflow bins:
750 /// \code
751 /// ROOT::Experimental::RHistEngine<int> hist(/* one dimension */);
752 /// // Fill the histogram with a number of entries...
753 /// auto sliced = hist.Slice(hist.GetAxes()[0].GetNormalRange(1, 5));
754 /// // The returned histogram will have 4 normal bins, an underflow and an overflow bin.
755 /// \endcode
756 ///
757 /// Slicing can also perform operations per dimension, see RSliceSpec. RSliceSpec::ROperationRebin allows to rebin
758 /// the histogram axis, grouping a number of normal bins into a new one:
759 /// \code
760 /// ROOT::Experimental::RHistEngine<int> hist(/* one dimension */);
761 /// // Fill the histogram with a number of entries...
762 /// auto rebinned = hist.Slice(ROOT::Experimental::RSliceSpec::ROperationRebin(2));
763 /// // The returned histogram has groups of two normal bins merged.
764 /// \endcode
765 ///
766 /// RSliceSpec::ROperationSum sums the bin contents along that axis, which allows to project to a lower-dimensional
767 /// histogram:
768 /// \code
769 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
770 /// // Fill the histogram with a number of entries...
771 /// auto projected = hist.Slice(ROOT::Experimental::RSliceSpec{}, ROOT::Experimental::RSliceSpec::ROperationSum{});
772 /// // The returned histogram has one dimension, with bin contents summed along the second axis.
773 /// \endcode
774 /// Note that it is not allowed to sum along all histogram axes because the return value would be a scalar.
775 ///
776 /// Ranges and operations can be combined. In that case, the range is applied before the operation.
777 ///
778 /// \param[in] args the arguments for each axis
779 /// \return the sliced histogram
780 /// \par See also
781 /// the \ref Slice(const std::vector<RSliceSpec> &sliceSpecs) const "function overload" accepting `std::vector`
782 template <typename... A>
783 RHistEngine Slice(const A &...args) const
784 {
785 std::vector<RSliceSpec> sliceSpecs{args...};
786 return Slice(sliceSpecs);
787 }
788
789 /// %ROOT Streamer function to throw when trying to store an object of this class.
790 void Streamer(TBuffer &) { throw std::runtime_error("unable to store RHistEngine"); }
791};
792
793} // namespace Experimental
794} // namespace ROOT
795
796#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