Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 10:22:34

0001 /// \file ROOT/RHist.hxx
0002 /// \ingroup HistV7
0003 /// \author Axel Naumann <axel@cern.ch>
0004 /// \date 2015-03-23
0005 /// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
0006 /// is welcome!
0007 
0008 /*************************************************************************
0009  * Copyright (C) 1995-2015, Rene Brun and Fons Rademakers.               *
0010  * All rights reserved.                                                  *
0011  *                                                                       *
0012  * For the licensing terms see $ROOTSYS/LICENSE.                         *
0013  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
0014  *************************************************************************/
0015 
0016 #ifndef ROOT7_RHist
0017 #define ROOT7_RHist
0018 
0019 #include "ROOT/RSpan.hxx"
0020 #include "ROOT/RAxis.hxx"
0021 #include "ROOT/RHistBinIter.hxx"
0022 #include "ROOT/RHistImpl.hxx"
0023 #include "ROOT/RHistData.hxx"
0024 #include "ROOT/RLogger.hxx"
0025 #include <initializer_list>
0026 #include <stdexcept>
0027 
0028 namespace ROOT {
0029 namespace Experimental {
0030 
0031 // fwd declare for fwd declare for friend declaration in RHist...
0032 template <int DIMENSIONS, class PRECISION, template <int D_, class P_> class... STAT>
0033 class RHist;
0034 
0035 // fwd declare for friend declaration in RHist.
0036 template <int DIMENSIONS, class PRECISION, template <int D_, class P_> class... STAT>
0037 class RHist<DIMENSIONS, PRECISION, STAT...>
0038 HistFromImpl(std::unique_ptr<typename RHist<DIMENSIONS, PRECISION, STAT...>::ImplBase_t> pHistImpl);
0039 
0040 /**
0041  \class RHist
0042  Histogram class for histograms with `DIMENSIONS` dimensions, where each
0043  bin count is stored by a value of type `PRECISION`. STAT stores statistical
0044  data of the entries filled into the histogram (bin content, uncertainties etc).
0045 
0046  A histogram counts occurrences of values or n-dimensional combinations thereof.
0047  Contrary to for instance a `RTree`, a histogram combines adjacent values. The
0048  resolution of this combination is defined by the axis binning, see e.g.
0049  http://www.wikiwand.com/en/Histogram
0050  */
0051 
0052 template <int DIMENSIONS, class PRECISION, template <int D_, class P_> class... STAT>
0053 class RHist {
0054 public:
0055    /// The type of the `Detail::RHistImplBase` of this histogram.
0056    using ImplBase_t =
0057       Detail::RHistImplBase<Detail::RHistData<DIMENSIONS, PRECISION, std::vector<PRECISION>, STAT...>>;
0058    /// The coordinates type: a `DIMENSIONS`-dimensional `std::array` of `double`.
0059    using CoordArray_t = typename ImplBase_t::CoordArray_t;
0060    /// The type of weights
0061    using Weight_t = PRECISION;
0062    /// Pointer type to `HistImpl_t::Fill`, for faster access.
0063    using FillFunc_t = typename ImplBase_t::FillFunc_t;
0064    /// Range.
0065    using AxisRange_t = typename ImplBase_t::AxisIterRange_t;
0066 
0067    using const_iterator = Detail::RHistBinIter<ImplBase_t>;
0068 
0069    /// Number of dimensions of the coordinates
0070    static constexpr int GetNDim() noexcept { return DIMENSIONS; }
0071 
0072    RHist() = default;
0073    RHist(RHist &&) = default;
0074    RHist(const RHist &other): fImpl(other.fImpl->Clone()), fFillFunc(other.fFillFunc)
0075    {}
0076 
0077    /// Create a histogram from an `array` of axes (`RAxisConfig`s). Example code:
0078    ///
0079    /// Construct a 1-dimensional histogram that can be filled with `floats`s.
0080    /// The axis has 10 bins between 0. and 1. The two outermost sets of curly
0081    /// braces are to reach the initialization of the `std::array` elements; the
0082    /// inner one is for the initialization of a `RAxisCoordinate`.
0083    ///
0084    ///     RHist<1,float> h1f({{ {10, 0., 1.} }});
0085    ///
0086    /// Construct a 2-dimensional histogram, with the first axis as before, and
0087    /// the second axis having non-uniform ("irregular") binning, where all bin-
0088    /// edges are specified. As this is itself an array it must be enclosed by
0089    /// double curlies.
0090    ///
0091    ///     RHist<2,int> h2i({{ {10, 0., 1.}, {{-1., 0., 1., 10., 100.}} }});
0092    explicit RHist(std::array<RAxisConfig, DIMENSIONS> axes);
0093 
0094    /// Constructor overload taking the histogram title.
0095    RHist(std::string_view histTitle, std::array<RAxisConfig, DIMENSIONS> axes);
0096 
0097    /// Constructor overload that's only available for a 1-dimensional histogram.
0098    template <int ENABLEIF_NDIM = DIMENSIONS, class = typename std::enable_if<ENABLEIF_NDIM == 1>::type>
0099    explicit RHist(const RAxisConfig &xaxis): RHist(std::array<RAxisConfig, 1>{{xaxis}})
0100    {}
0101 
0102    /// Constructor overload that's only available for a 1-dimensional histogram,
0103    /// also passing the histogram title.
0104    template <int ENABLEIF_NDIM = DIMENSIONS, class = typename std::enable_if<ENABLEIF_NDIM == 1>::type>
0105    RHist(std::string_view histTitle, const RAxisConfig &xaxis): RHist(histTitle, std::array<RAxisConfig, 1>{{xaxis}})
0106    {}
0107 
0108    /// Constructor overload that's only available for a 2-dimensional histogram.
0109    template <int ENABLEIF_NDIM = DIMENSIONS, class = typename std::enable_if<ENABLEIF_NDIM == 2>::type>
0110    RHist(const RAxisConfig &xaxis, const RAxisConfig &yaxis): RHist(std::array<RAxisConfig, 2>{{xaxis, yaxis}})
0111    {}
0112 
0113    /// Constructor overload that's only available for a 2-dimensional histogram,
0114    /// also passing the histogram title.
0115    template <int ENABLEIF_NDIM = DIMENSIONS, class = typename std::enable_if<ENABLEIF_NDIM == 2>::type>
0116    RHist(std::string_view histTitle, const RAxisConfig &xaxis, const RAxisConfig &yaxis)
0117       : RHist(histTitle, std::array<RAxisConfig, 2>{{xaxis, yaxis}})
0118    {}
0119 
0120    /// Constructor overload that's only available for a 3-dimensional histogram.
0121    template <int ENABLEIF_NDIM = DIMENSIONS, class = typename std::enable_if<ENABLEIF_NDIM == 3>::type>
0122    RHist(const RAxisConfig &xaxis, const RAxisConfig &yaxis, const RAxisConfig &zaxis)
0123       : RHist(std::array<RAxisConfig, 3>{{xaxis, yaxis, zaxis}})
0124    {}
0125 
0126    /// Constructor overload that's only available for a 3-dimensional histogram,
0127    /// also passing the histogram title.
0128    template <int ENABLEIF_NDIM = DIMENSIONS, class = typename std::enable_if<ENABLEIF_NDIM == 3>::type>
0129    RHist(std::string_view histTitle, const RAxisConfig &xaxis, const RAxisConfig &yaxis, const RAxisConfig &zaxis)
0130       : RHist(histTitle, std::array<RAxisConfig, 3>{{xaxis, yaxis, zaxis}})
0131    {}
0132 
0133    /// Access the ImplBase_t this RHist points to.
0134    ImplBase_t *GetImpl() const noexcept { return fImpl.get(); }
0135 
0136    /// "Steal" the ImplBase_t this RHist points to.
0137    std::unique_ptr<ImplBase_t> TakeImpl() && noexcept { return std::move(fImpl); }
0138 
0139    /// Add `weight` to the bin containing coordinate `x`.
0140    void Fill(const CoordArray_t &x, Weight_t weight = (Weight_t)1) noexcept { (fImpl.get()->*fFillFunc)(x, weight); }
0141 
0142    /// For each coordinate in `xN`, add `weightN[i]` to the bin at coordinate
0143    /// `xN[i]`. The sizes of `xN` and `weightN` must be the same. This is more
0144    /// efficient than many separate calls to `Fill()`.
0145    void FillN(const std::span<const CoordArray_t> xN, const std::span<const Weight_t> weightN) noexcept
0146    {
0147       fImpl->FillN(xN, weightN);
0148    }
0149 
0150    /// For each coordinate in `xN`, add `weightN[i]` to the bin at coordinate
0151    /// `xN[i]`. The sizes of `xN` and `weightN` must be the same. This is more
0152    /// efficient than many separate calls to `Fill()`.
0153    /// Overload for passing initializer lists.
0154    void FillN(std::initializer_list<const CoordArray_t> xN, std::initializer_list<const Weight_t> weightN) noexcept
0155    {
0156       fImpl->FillN(std::span<const CoordArray_t>(xN.begin(), xN.end()), std::span<const Weight_t>(weightN.begin(), weightN.end()));
0157    }
0158 
0159    /// Convenience overload: `FillN()` with weight 1.
0160    void FillN(const std::span<const CoordArray_t> xN) noexcept { fImpl->FillN(xN); }
0161 
0162    /// Convenience overload: `FillN()` with weight 1.
0163    /// Overload for passing initializer lists.
0164    void FillN(std::initializer_list<const CoordArray_t> xN) noexcept {
0165       fImpl->FillN(std::span<const CoordArray_t>(xN.begin(), xN.end()));
0166    }
0167 
0168    /// Get the number of entries this histogram was filled with.
0169    int64_t GetEntries() const noexcept { return fImpl->GetStat().GetEntries(); }
0170 
0171    /// Get the content of the bin at `x`.
0172    Weight_t GetBinContent(const CoordArray_t &x) const { return fImpl->GetBinContent(x); }
0173 
0174    /// Get the uncertainty on the content of the bin at `x`.
0175    double GetBinUncertainty(const CoordArray_t &x) const { return fImpl->GetBinUncertainty(x); }
0176 
0177    const_iterator begin() const { return const_iterator(*fImpl, 1); }
0178 
0179    const_iterator end() const { return const_iterator(*fImpl, fImpl->GetNBinsNoOver() + 1); }
0180 
0181    /// Swap *this and other.
0182    ///
0183    /// Very efficient; swaps the `fImpl` pointers.
0184    void swap(RHist<DIMENSIONS, PRECISION, STAT...> &other) noexcept
0185    {
0186       std::swap(fImpl, other.fImpl);
0187       std::swap(fFillFunc, other.fFillFunc);
0188    }
0189 
0190 private:
0191    /// The actual histogram implementation.
0192    std::unique_ptr<ImplBase_t> fImpl;
0193 
0194    /// Pointer to RHistImpl::Fill() member function.
0195    FillFunc_t fFillFunc = nullptr;    //!
0196 
0197    friend RHist HistFromImpl<>(std::unique_ptr<ImplBase_t>);
0198 };
0199 
0200 /// RHist with no STAT parameter uses RHistStatContent by default.
0201 template <int DIMENSIONS, class PRECISION>
0202 class RHist<DIMENSIONS, PRECISION>: public RHist<DIMENSIONS, PRECISION, RHistStatContent> {
0203    using RHist<DIMENSIONS, PRECISION, RHistStatContent>::RHist;
0204 };
0205 
0206 /// Swap two histograms.
0207 ///
0208 /// Very efficient; swaps the `fImpl` pointers.
0209 template <int DIMENSIONS, class PRECISION, template <int D_, class P_> class... STAT>
0210 void swap(RHist<DIMENSIONS, PRECISION, STAT...> &a, RHist<DIMENSIONS, PRECISION, STAT...> &b) noexcept
0211 {
0212    a.swap(b);
0213 }
0214 
0215 namespace Internal {
0216 /**
0217  Generate RHist::fImpl from RHist constructor arguments.
0218  */
0219 template <int NDIM, int IDIM, class DATA, class... PROCESSEDAXISCONFIG>
0220 struct RHistImplGen {
0221    /// Select the template argument for the next axis type, and "recurse" into
0222    /// RHistImplGen for the next axis.
0223    template <RAxisConfig::EKind KIND>
0224    std::unique_ptr<Detail::RHistImplBase<DATA>>
0225    MakeNextAxis(std::string_view title, const std::array<RAxisConfig, NDIM> &axes,
0226                 PROCESSEDAXISCONFIG... processedAxisArgs)
0227    {
0228       using NextAxis_t = typename AxisConfigToType<KIND>::Axis_t;
0229       NextAxis_t nextAxis = AxisConfigToType<KIND>()(axes[IDIM]);
0230       using HistImpl_t = RHistImplGen<NDIM, IDIM + 1, DATA, PROCESSEDAXISCONFIG..., NextAxis_t>;
0231       return HistImpl_t()(title, axes, processedAxisArgs..., nextAxis);
0232    }
0233 
0234    /// Make a RHistImpl-derived object reflecting the RAxisConfig array.
0235    ///
0236    /// Delegate to the appropriate MakeNextAxis instantiation, depending on the
0237    /// axis type selected in the RAxisConfig.
0238    /// \param title - title of the derived object.
0239    /// \param axes - `RAxisConfig` objects describing the axis of the resulting
0240    ///   RHistImpl.
0241    /// \param processedAxisArgs - the RAxisBase-derived axis objects describing the
0242    ///   axes of the resulting RHistImpl. There are `IDIM` of those; in the end
0243    /// (`IDIM` == `GetNDim()`), all `axes` have been converted to
0244    /// `processedAxisArgs` and the RHistImpl constructor can be invoked, passing
0245    /// the `processedAxisArgs`.
0246    std::unique_ptr<Detail::RHistImplBase<DATA>> operator()(std::string_view title,
0247                                                            const std::array<RAxisConfig, NDIM> &axes,
0248                                                            PROCESSEDAXISCONFIG... processedAxisArgs)
0249    {
0250       switch (axes[IDIM].GetKind()) {
0251       case RAxisConfig::kEquidistant: return MakeNextAxis<RAxisConfig::kEquidistant>(title, axes, processedAxisArgs...);
0252       case RAxisConfig::kGrow: return MakeNextAxis<RAxisConfig::kGrow>(title, axes, processedAxisArgs...);
0253       case RAxisConfig::kIrregular: return MakeNextAxis<RAxisConfig::kIrregular>(title, axes, processedAxisArgs...);
0254       default: R__LOG_ERROR(HistLog()) << "Unhandled axis kind";
0255       }
0256       return nullptr;
0257    }
0258 };
0259 
0260 /// Generate RHist::fImpl from constructor arguments; recursion end.
0261 template <int NDIM, class DATA, class... PROCESSEDAXISCONFIG>
0262 /// Create the histogram, now that all axis types and initializer objects are
0263 /// determined.
0264 struct RHistImplGen<NDIM, NDIM, DATA, PROCESSEDAXISCONFIG...> {
0265    using HistImplBase_t = ROOT::Experimental::Detail::RHistImplBase<DATA>;
0266    std::unique_ptr<HistImplBase_t>
0267    operator()(std::string_view title, const std::array<RAxisConfig, DATA::GetNDim()> &, PROCESSEDAXISCONFIG... axisArgs)
0268    {
0269       using HistImplt_t = Detail::RHistImpl<DATA, PROCESSEDAXISCONFIG...>;
0270       return std::make_unique<HistImplt_t>(title, axisArgs...);
0271    }
0272 };
0273 } // namespace Internal
0274 
0275 template <int DIMENSIONS, class PRECISION, template <int D_, class P_> class... STAT>
0276 RHist<DIMENSIONS, PRECISION, STAT...>::RHist(std::string_view title, std::array<RAxisConfig, DIMENSIONS> axes)
0277    : fImpl{std::move(
0278         Internal::RHistImplGen<RHist::GetNDim(), 0,
0279                                Detail::RHistData<DIMENSIONS, PRECISION, std::vector<PRECISION>, STAT...>>()(
0280            title, axes))}
0281 {
0282    fFillFunc = fImpl->GetFillFunc();
0283 }
0284 
0285 template <int DIMENSIONS, class PRECISION, template <int D_, class P_> class... STAT>
0286 RHist<DIMENSIONS, PRECISION, STAT...>::RHist(std::array<RAxisConfig, DIMENSIONS> axes): RHist("", axes)
0287 {}
0288 
0289 /// Adopt an external, stand-alone RHistImpl. The RHist will take ownership.
0290 template <int DIMENSIONS, class PRECISION, template <int D_, class P_> class... STAT>
0291 RHist<DIMENSIONS, PRECISION, STAT...>
0292 HistFromImpl(std::unique_ptr<typename RHist<DIMENSIONS, PRECISION, STAT...>::ImplBase_t> pHistImpl)
0293 {
0294    RHist<DIMENSIONS, PRECISION, STAT...> ret;
0295    ret.fFillFunc = pHistImpl->GetFillFunc();
0296    std::swap(ret.fImpl, pHistImpl);
0297    return ret;
0298 }
0299 
0300 /// \name RHist Typedefs
0301 ///\{ Convenience typedefs (ROOT6-compatible type names)
0302 
0303 // Keep them as typedefs, to make sure old-style documentation tools can understand them.
0304 using RH1D = RHist<1, double, RHistStatContent, RHistStatUncertainty>;
0305 using RH1F = RHist<1, float, RHistStatContent, RHistStatUncertainty>;
0306 using RH1C = RHist<1, char, RHistStatContent>;
0307 using RH1I = RHist<1, int, RHistStatContent>;
0308 using RH1L = RHist<1, int64_t, RHistStatContent>;
0309 using RH1LL = RHist<1, int64_t, RHistStatContent>;
0310 
0311 using RH2D = RHist<2, double, RHistStatContent, RHistStatUncertainty>;
0312 using RH2F = RHist<2, float, RHistStatContent, RHistStatUncertainty>;
0313 using RH2C = RHist<2, char, RHistStatContent>;
0314 using RH2I = RHist<2, int, RHistStatContent>;
0315 using RH2L = RHist<2, int64_t, RHistStatContent>;
0316 using RH2LL = RHist<2, int64_t, RHistStatContent>;
0317 
0318 using RH3D = RHist<3, double, RHistStatContent, RHistStatUncertainty>;
0319 using RH3F = RHist<3, float, RHistStatContent, RHistStatUncertainty>;
0320 using RH3C = RHist<3, char, RHistStatContent>;
0321 using RH3I = RHist<3, int, RHistStatContent>;
0322 using RH3L = RHist<3, int64_t, RHistStatContent>;
0323 using RH3LL = RHist<3, int64_t, RHistStatContent>;
0324 ///\}
0325 
0326 /// Add two histograms.
0327 ///
0328 /// This operation may currently only be performed if the two histograms have
0329 /// the same axis configuration, use the same precision, and if `from` records
0330 /// at least the same statistics as `to` (recording more stats is fine).
0331 ///
0332 /// Adding histograms with incompatible axis binning will be reported at runtime
0333 /// with an `std::runtime_error`. Insufficient statistics in the source
0334 /// histogram will be detected at compile-time and result in a compiler error.
0335 ///
0336 /// In the future, we may either adopt a more relaxed definition of histogram
0337 /// addition or provide a mechanism to convert from one histogram type to
0338 /// another. We currently favor the latter path.
0339 template <int DIMENSIONS, class PRECISION,
0340           template <int D_, class P_> class... STAT_TO,
0341           template <int D_, class P_> class... STAT_FROM>
0342 void Add(RHist<DIMENSIONS, PRECISION, STAT_TO...> &to, const RHist<DIMENSIONS, PRECISION, STAT_FROM...> &from)
0343 {
0344    // Enforce "same axis configuration" policy.
0345    auto& toImpl = *to.GetImpl();
0346    const auto& fromImpl = *from.GetImpl();
0347    for (int dim = 0; dim < DIMENSIONS; ++dim) {
0348       if (!toImpl.GetAxis(dim).HasSameBinningAs(fromImpl.GetAxis(dim))) {
0349          throw std::runtime_error("Attempted to add RHists with incompatible axis binning");
0350       }
0351    }
0352 
0353    // Now that we know that the two axes have the same binning, we can just add
0354    // the statistics directly.
0355    toImpl.GetStat().Add(fromImpl.GetStat());
0356 }
0357 
0358 } // namespace Experimental
0359 } // namespace ROOT
0360 
0361 #endif