|
|
|||
File indexing completed on 2025-12-14 10:31:15
0001 /// \file 0002 /// \warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes. 0003 /// Feedback is welcome! 0004 0005 #ifndef ROOT_RHistEngine 0006 #define ROOT_RHistEngine 0007 0008 #include "RAxes.hxx" 0009 #include "RBinIndex.hxx" 0010 #include "RHistUtils.hxx" 0011 #include "RLinearizedIndex.hxx" 0012 #include "RRegularAxis.hxx" 0013 #include "RWeight.hxx" 0014 0015 #include <array> 0016 #include <cassert> 0017 #include <stdexcept> 0018 #include <tuple> 0019 #include <type_traits> 0020 #include <utility> 0021 #include <vector> 0022 0023 class TBuffer; 0024 0025 namespace ROOT { 0026 namespace Experimental { 0027 0028 /** 0029 A histogram data structure to bin data along multiple dimensions. 0030 0031 Every call to \ref Fill(const A &... args) "Fill" bins the data according to the axis configuration and increments the 0032 bin content: 0033 \code 0034 ROOT::Experimental::RHistEngine<int> hist(10, {5, 15}); 0035 hist.Fill(8.5); 0036 // hist.GetBinContent(ROOT::Experimental::RBinIndex(3)) will return 1 0037 \endcode 0038 0039 The class is templated on the bin content type. For counting, as in the example above, it may be an integral type such 0040 as `int` or `long`. Narrower types such as `unsigned char` or `short` are supported, but may overflow due to their 0041 limited range and must be used with care. For weighted filling, the bin content type must not be an integral type, but 0042 a floating-point type such as `float` or `double`, or the special type RBinWithError. Note that `float` has a limited 0043 significand precision of 24 bits. 0044 0045 An object can have arbitrary dimensionality determined at run-time. The axis configuration is passed as a vector of 0046 RAxisVariant: 0047 \code 0048 std::vector<ROOT::Experimental::RAxisVariant> axes; 0049 axes.push_back(ROOT::Experimental::RRegularAxis(10, 5, 15)); 0050 axes.push_back(ROOT::Experimental::RVariableBinAxis({1, 10, 100, 1000})); 0051 ROOT::Experimental::RHistEngine<int> hist(axes); 0052 // hist.GetNDimensions() will return 2 0053 \endcode 0054 0055 \warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes. 0056 Feedback is welcome! 0057 */ 0058 template <typename BinContentType> 0059 class RHistEngine final { 0060 /// The axis configuration for this histogram. Relevant methods are forwarded from the public interface. 0061 Internal::RAxes fAxes; 0062 /// The bin contents for this histogram 0063 std::vector<BinContentType> fBinContents; 0064 0065 public: 0066 /// Construct a histogram engine. 0067 /// 0068 /// \param[in] axes the axis objects, must have size > 0 0069 explicit RHistEngine(std::vector<RAxisVariant> axes) : fAxes(std::move(axes)) 0070 { 0071 fBinContents.resize(fAxes.ComputeTotalNBins()); 0072 } 0073 0074 /// Construct a one-dimensional histogram engine with a regular axis. 0075 /// 0076 /// \param[in] nNormalBins the number of normal bins, must be > 0 0077 /// \param[in] interval the axis interval (lower end inclusive, upper end exclusive) 0078 /// \par See also 0079 /// the 0080 /// \ref RRegularAxis::RRegularAxis(std::size_t nNormalBins, std::pair<double, double> interval, bool enableFlowBins) 0081 /// "constructor of RRegularAxis" 0082 RHistEngine(std::size_t nNormalBins, std::pair<double, double> interval) 0083 : RHistEngine({RRegularAxis(nNormalBins, interval)}) 0084 { 0085 } 0086 0087 /// The copy constructor is deleted. 0088 /// 0089 /// Copying all bin contents can be an expensive operation, depending on the number of bins. If required, users can 0090 /// explicitly call Clone(). 0091 RHistEngine(const RHistEngine<BinContentType> &) = delete; 0092 /// Efficiently move construct a histogram engine. 0093 /// 0094 /// After this operation, the moved-from object is invalid. 0095 RHistEngine(RHistEngine<BinContentType> &&) = default; 0096 0097 /// The copy assignment operator is deleted. 0098 /// 0099 /// Copying all bin contents can be an expensive operation, depending on the number of bins. If required, users can 0100 /// explicitly call Clone(). 0101 RHistEngine<BinContentType> &operator=(const RHistEngine<BinContentType> &) = delete; 0102 /// Efficiently move a histogram engine. 0103 /// 0104 /// After this operation, the moved-from object is invalid. 0105 RHistEngine<BinContentType> &operator=(RHistEngine<BinContentType> &&) = default; 0106 0107 ~RHistEngine() = default; 0108 0109 const std::vector<RAxisVariant> &GetAxes() const { return fAxes.Get(); } 0110 std::size_t GetNDimensions() const { return fAxes.GetNDimensions(); } 0111 std::size_t GetTotalNBins() const { return fBinContents.size(); } 0112 0113 /// Get the content of a single bin. 0114 /// 0115 /// \code 0116 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */}); 0117 /// std::array<ROOT::Experimental::RBinIndex, 2> indices = {3, 5}; 0118 /// int content = hist.GetBinContent(indices); 0119 /// \endcode 0120 /// 0121 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special 0122 /// values. See also the class documentation of RBinIndex. 0123 /// 0124 /// Throws an exception if the number of indices does not match the axis configuration or the bin is not found. 0125 /// 0126 /// \param[in] indices the array of indices for each axis 0127 /// \return the bin content 0128 /// \par See also 0129 /// the \ref GetBinContent(const A &... args) const "variadic function template overload" accepting arguments 0130 /// directly 0131 template <std::size_t N> 0132 const BinContentType &GetBinContent(const std::array<RBinIndex, N> &indices) const 0133 { 0134 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might 0135 // be confusing for users. 0136 if (N != GetNDimensions()) { 0137 throw std::invalid_argument("invalid number of indices passed to GetBinContent"); 0138 } 0139 RLinearizedIndex index = fAxes.ComputeGlobalIndex(indices); 0140 if (!index.fValid) { 0141 throw std::invalid_argument("bin not found in GetBinContent"); 0142 } 0143 assert(index.fIndex < fBinContents.size()); 0144 return fBinContents[index.fIndex]; 0145 } 0146 0147 /// Get the content of a single bin. 0148 /// 0149 /// \code 0150 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */}); 0151 /// int content = hist.GetBinContent(ROOT::Experimental::RBinIndex(3), ROOT::Experimental::RBinIndex(5)); 0152 /// // ... or construct the RBinIndex arguments implicitly from integers: 0153 /// content = hist.GetBinContent(3, 5); 0154 /// \endcode 0155 /// 0156 /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special 0157 /// values. See also the class documentation of RBinIndex. 0158 /// 0159 /// Throws an exception if the number of arguments does not match the axis configuration or the bin is not found. 0160 /// 0161 /// \param[in] args the arguments for each axis 0162 /// \return the bin content 0163 /// \par See also 0164 /// the \ref GetBinContent(const std::array<RBinIndex, N> &indices) const "function overload" accepting 0165 /// `std::array` 0166 template <typename... A> 0167 const BinContentType &GetBinContent(const A &...args) const 0168 { 0169 std::array<RBinIndex, sizeof...(A)> indices{args...}; 0170 return GetBinContent(indices); 0171 } 0172 0173 /// Add all bin contents of another histogram. 0174 /// 0175 /// Throws an exception if the axes configurations are not identical. 0176 /// 0177 /// \param[in] other another histogram 0178 void Add(const RHistEngine<BinContentType> &other) 0179 { 0180 if (fAxes != other.fAxes) { 0181 throw std::invalid_argument("axes configurations not identical in Add"); 0182 } 0183 for (std::size_t i = 0; i < fBinContents.size(); i++) { 0184 fBinContents[i] += other.fBinContents[i]; 0185 } 0186 } 0187 0188 /// Clear all bin contents. 0189 void Clear() 0190 { 0191 for (std::size_t i = 0; i < fBinContents.size(); i++) { 0192 fBinContents[i] = {}; 0193 } 0194 } 0195 0196 /// Clone this histogram engine. 0197 /// 0198 /// Copying all bin contents can be an expensive operation, depending on the number of bins. 0199 /// 0200 /// \return the cloned object 0201 RHistEngine<BinContentType> Clone() const 0202 { 0203 RHistEngine<BinContentType> h(fAxes.Get()); 0204 for (std::size_t i = 0; i < fBinContents.size(); i++) { 0205 h.fBinContents[i] = fBinContents[i]; 0206 } 0207 return h; 0208 } 0209 0210 /// Whether this histogram engine type supports weighted filling. 0211 static constexpr bool SupportsWeightedFilling = !std::is_integral_v<BinContentType>; 0212 0213 /// Fill an entry into the histogram. 0214 /// 0215 /// \code 0216 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */}); 0217 /// auto args = std::make_tuple(8.5, 10.5); 0218 /// hist.Fill(args); 0219 /// \endcode 0220 /// 0221 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently 0222 /// discarded. 0223 /// 0224 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be 0225 /// converted for the axis type at run-time. 0226 /// 0227 /// \param[in] args the arguments for each axis 0228 /// \par See also 0229 /// the \ref Fill(const A &... args) "variadic function template overload" accepting arguments directly and the 0230 /// \ref Fill(const std::tuple<A...> &args, RWeight weight) "overload for weighted filling" 0231 template <typename... A> 0232 void Fill(const std::tuple<A...> &args) 0233 { 0234 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might 0235 // be confusing for users. 0236 if (sizeof...(A) != GetNDimensions()) { 0237 throw std::invalid_argument("invalid number of arguments to Fill"); 0238 } 0239 RLinearizedIndex index = fAxes.ComputeGlobalIndexImpl<sizeof...(A)>(args); 0240 if (index.fValid) { 0241 assert(index.fIndex < fBinContents.size()); 0242 fBinContents[index.fIndex]++; 0243 } 0244 } 0245 0246 /// Fill an entry into the histogram with a weight. 0247 /// 0248 /// This overload is not available for integral bin content types (see \ref SupportsWeightedFilling). 0249 /// 0250 /// \code 0251 /// ROOT::Experimental::RHistEngine<float> hist({/* two dimensions */}); 0252 /// auto args = std::make_tuple(8.5, 10.5); 0253 /// hist.Fill(args, ROOT::Experimental::RWeight(0.8)); 0254 /// \endcode 0255 /// 0256 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently 0257 /// discarded. 0258 /// 0259 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be 0260 /// converted for the axis type at run-time. 0261 /// 0262 /// \param[in] args the arguments for each axis 0263 /// \param[in] weight the weight for this entry 0264 /// \par See also 0265 /// the \ref Fill(const A &... args) "variadic function template overload" accepting arguments directly and the 0266 /// \ref Fill(const std::tuple<A...> &args) "overload for unweighted filling" 0267 template <typename... A> 0268 void Fill(const std::tuple<A...> &args, RWeight weight) 0269 { 0270 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types"); 0271 0272 // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might 0273 // be confusing for users. 0274 if (sizeof...(A) != GetNDimensions()) { 0275 throw std::invalid_argument("invalid number of arguments to Fill"); 0276 } 0277 RLinearizedIndex index = fAxes.ComputeGlobalIndexImpl<sizeof...(A)>(args); 0278 if (index.fValid) { 0279 assert(index.fIndex < fBinContents.size()); 0280 fBinContents[index.fIndex] += weight.fValue; 0281 } 0282 } 0283 0284 /// Fill an entry into the histogram. 0285 /// 0286 /// \code 0287 /// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */}); 0288 /// hist.Fill(8.5, 10.5); 0289 /// \endcode 0290 /// 0291 /// For weighted filling, pass an RWeight as the last argument: 0292 /// \code 0293 /// ROOT::Experimental::RHistEngine<float> hist({/* two dimensions */}); 0294 /// hist.Fill(8.5, 10.5, ROOT::Experimental::RWeight(0.8)); 0295 /// \endcode 0296 /// This is not available for integral bin content types (see \ref SupportsWeightedFilling). 0297 /// 0298 /// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently 0299 /// discarded. 0300 /// 0301 /// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be 0302 /// converted for the axis type at run-time. 0303 /// 0304 /// \param[in] args the arguments for each axis 0305 /// \par See also 0306 /// the function overloads accepting `std::tuple` \ref Fill(const std::tuple<A...> &args) "for unweighted filling" 0307 /// and \ref Fill(const std::tuple<A...> &args, RWeight) "for weighted filling" 0308 template <typename... A> 0309 void Fill(const A &...args) 0310 { 0311 auto t = std::forward_as_tuple(args...); 0312 if constexpr (std::is_same_v<typename Internal::LastType<A...>::type, RWeight>) { 0313 static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types"); 0314 static constexpr std::size_t N = sizeof...(A) - 1; 0315 if (N != fAxes.GetNDimensions()) { 0316 throw std::invalid_argument("invalid number of arguments to Fill"); 0317 } 0318 RWeight weight = std::get<N>(t); 0319 RLinearizedIndex index = fAxes.ComputeGlobalIndexImpl<N>(t); 0320 if (index.fValid) { 0321 assert(index.fIndex < fBinContents.size()); 0322 fBinContents[index.fIndex] += weight.fValue; 0323 } 0324 } else { 0325 Fill(t); 0326 } 0327 } 0328 0329 /// %ROOT Streamer function to throw when trying to store an object of this class. 0330 void Streamer(TBuffer &) { throw std::runtime_error("unable to store RHistEngine"); } 0331 }; 0332 0333 } // namespace Experimental 0334 } // namespace ROOT 0335 0336 #endif
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|