Skip to content

Commit 63f1469

Browse files
committed
[hist] Implement RBinWithError
1 parent 97b81ce commit 63f1469

File tree

7 files changed

+123
-6
lines changed

7 files changed

+123
-6
lines changed

hist/histv7/doc/CodeArchitecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ During `Fill`, it delegates to `RHistEngine::Fill` but also updates the histogra
5353

5454
## Classes for Weighted Filling
5555

56-
### `RDoubleBinWithError`
56+
### `RBinWithError`
5757

5858
A special bin content type that also accumulates the sum of weights squared.
5959
It can be used as a template argument to `RHistEngine` and `RHist`.

hist/histv7/doc/Terminology.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ A *categorical axis* has a unique label per bin.
1313
*Axes* is the plural of axis and usually means the bin configurations for all dimensions of a histogram.
1414

1515
A *bin content* is the value of a single bin.
16-
The *bin content type* can be an integer type, a floating-point type, the special `RDoubleBinWithError`, or a user-defined type.
16+
The *bin content type* can be an integer type, a floating-point type, the special `RBinWithError`, or a user-defined type.
1717

1818
A *bin error* is the Poisson error of a bin content.
19-
With the special `RDoubleBinWithError`, it is the square root of the sum of weights squared: $\sqrt{\sum w_i^2}$
19+
With the special `RBinWithError`, it is the square root of the sum of weights squared: $\sqrt{\sum w_i^2}$
2020
Otherwise it is the square root of the bin content, which is only correct with unweighted filling.
2121

2222
A *bin index* (plural *indices*) refers to a single bin of a dimension, an array of indices refers to a bin in a histogram.

hist/histv7/headers.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ set(histv7_headers
22
ROOT/RAxes.hxx
33
ROOT/RBinIndex.hxx
44
ROOT/RBinIndexRange.hxx
5+
ROOT/RBinWithError.hxx
56
ROOT/RHistEngine.hxx
67
ROOT/RHistUtils.hxx
78
ROOT/RLinearizedIndex.hxx
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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_RBinWithError
6+
#define ROOT_RBinWithError
7+
8+
namespace ROOT {
9+
namespace Experimental {
10+
11+
/**
12+
A special bin content type to compute the bin error in weighted filling.
13+
14+
\warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes.
15+
Feedback is welcome!
16+
*/
17+
struct RBinWithError final {
18+
double fSum = 0;
19+
double fSum2 = 0;
20+
21+
RBinWithError &operator++()
22+
{
23+
fSum++;
24+
fSum2++;
25+
return *this;
26+
}
27+
28+
RBinWithError operator++(int)
29+
{
30+
RBinWithError old = *this;
31+
operator++();
32+
return old;
33+
}
34+
35+
RBinWithError &operator+=(double w)
36+
{
37+
fSum += w;
38+
fSum2 += w * w;
39+
return *this;
40+
}
41+
42+
RBinWithError &operator+=(const RBinWithError &rhs)
43+
{
44+
fSum += rhs.fSum;
45+
fSum2 += rhs.fSum2;
46+
return *this;
47+
}
48+
};
49+
50+
} // namespace Experimental
51+
} // namespace ROOT
52+
53+
#endif

hist/histv7/inc/ROOT/RHistEngine.hxx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "RAxes.hxx"
99
#include "RBinIndex.hxx"
10+
#include "RBinWithError.hxx"
1011
#include "RHistUtils.hxx"
1112
#include "RLinearizedIndex.hxx"
1213
#include "RRegularAxis.hxx"
@@ -39,7 +40,8 @@ hist.Fill(8.5);
3940
The class is templated on the bin content type. For counting, as in the example above, it may be an integer type such as
4041
`int` or `long`. Narrower types such as `unsigned char` or `short` are supported, but may overflow due to their limited
4142
range and must be used with care. For weighted filling, the bin content type must be a floating-point type such as
42-
`float` or `double`. Note that `float` has a limited significant precision of 24 bits.
43+
`float` or `double`, or the special type RBinWithError. Note that `float` has a limited significant precision of 24
44+
bits.
4345
4446
An object can have arbitrary dimensionality determined at run-time. The axis configuration is passed as a vector of
4547
RAxisVariant:
@@ -205,8 +207,9 @@ public:
205207
return h;
206208
}
207209

208-
/// Whether this histogram engine type supported weighted filling.
209-
static constexpr bool SupportsWeightedFilling = std::is_floating_point_v<BinContentType>;
210+
/// Whether this histogram engine type supports weighted filling.
211+
static constexpr bool SupportsWeightedFilling =
212+
std::is_floating_point_v<BinContentType> || std::is_same_v<BinContentType, RBinWithError>;
210213

211214
/// Fill an entry into the histogram.
212215
///

hist/histv7/test/hist_engine.cxx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,3 +331,61 @@ TEST(RHistEngine, FillTupleWeightInvalidNumberOfArguments)
331331
EXPECT_NO_THROW(engine2.Fill(std::make_tuple(1, 2), RWeight(1)));
332332
EXPECT_THROW(engine2.Fill(std::make_tuple(1, 2, 3), RWeight(1)), std::invalid_argument);
333333
}
334+
335+
TEST(RHistEngine_RBinWithError, Add)
336+
{
337+
static constexpr std::size_t Bins = 20;
338+
const RRegularAxis axis(Bins, 0, Bins);
339+
RHistEngine<RBinWithError> engineA({axis});
340+
RHistEngine<RBinWithError> engineB({axis});
341+
342+
for (std::size_t i = 0; i < Bins; i++) {
343+
engineA.Fill(i, RWeight(0.2 + i * 0.03));
344+
engineB.Fill(i, RWeight(0.1 + i * 0.05));
345+
}
346+
347+
engineA.Add(engineB);
348+
349+
for (auto index : axis.GetNormalRange()) {
350+
auto &bin = engineA.GetBinContent(index);
351+
double weightA = 0.2 + index.GetIndex() * 0.03;
352+
double weightB = 0.1 + index.GetIndex() * 0.05;
353+
EXPECT_FLOAT_EQ(bin.fSum, weightA + weightB);
354+
EXPECT_FLOAT_EQ(bin.fSum2, weightA * weightA + weightB * weightB);
355+
}
356+
}
357+
358+
TEST(RHistEngine_RBinWithError, Fill)
359+
{
360+
static constexpr std::size_t Bins = 20;
361+
const RRegularAxis axis(Bins, 0, Bins);
362+
RHistEngine<RBinWithError> engine({axis});
363+
364+
for (std::size_t i = 0; i < Bins; i++) {
365+
engine.Fill(i);
366+
}
367+
368+
for (auto index : axis.GetNormalRange()) {
369+
auto &bin = engine.GetBinContent(index);
370+
EXPECT_EQ(bin.fSum, 1);
371+
EXPECT_EQ(bin.fSum2, 1);
372+
}
373+
}
374+
375+
TEST(RHistEngine_RBinWithError, FillWeight)
376+
{
377+
static constexpr std::size_t Bins = 20;
378+
const RRegularAxis axis(Bins, 0, Bins);
379+
RHistEngine<RBinWithError> engine({axis});
380+
381+
for (std::size_t i = 0; i < Bins; i++) {
382+
engine.Fill(i, RWeight(0.1 + i * 0.03));
383+
}
384+
385+
for (auto index : axis.GetNormalRange()) {
386+
auto &bin = engine.GetBinContent(index);
387+
double weight = 0.1 + index.GetIndex() * 0.03;
388+
EXPECT_FLOAT_EQ(bin.fSum, weight);
389+
EXPECT_FLOAT_EQ(bin.fSum2, weight * weight);
390+
}
391+
}

hist/histv7/test/hist_test.hxx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <ROOT/RAxes.hxx>
55
#include <ROOT/RBinIndex.hxx>
66
#include <ROOT/RBinIndexRange.hxx>
7+
#include <ROOT/RBinWithError.hxx>
78
#include <ROOT/RHistEngine.hxx>
89
#include <ROOT/RRegularAxis.hxx>
910
#include <ROOT/RVariableBinAxis.hxx>
@@ -14,6 +15,7 @@
1415
using ROOT::Experimental::RAxisVariant;
1516
using ROOT::Experimental::RBinIndex;
1617
using ROOT::Experimental::RBinIndexRange;
18+
using ROOT::Experimental::RBinWithError;
1719
using ROOT::Experimental::RHistEngine;
1820
using ROOT::Experimental::RRegularAxis;
1921
using ROOT::Experimental::RVariableBinAxis;

0 commit comments

Comments
 (0)