Skip to content

Commit 325a213

Browse files
committed
[hist] Implement Scale(double factor)
1 parent cd8e752 commit 325a213

File tree

8 files changed

+184
-0
lines changed

8 files changed

+184
-0
lines changed

hist/histv7/inc/ROOT/RBinWithError.hxx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ struct RBinWithError final {
4848
return *this;
4949
}
5050

51+
RBinWithError &operator*=(double factor)
52+
{
53+
fSum *= factor;
54+
fSum2 *= factor * factor;
55+
return *this;
56+
}
57+
5158
void AtomicInc()
5259
{
5360
Internal::AtomicInc(&fSum);

hist/histv7/inc/ROOT/RHist.hxx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,17 @@ public:
277277
fStats.Fill(args...);
278278
}
279279

280+
/// Scale all histogram bin contents and statistics.
281+
///
282+
/// This method is not available for integral bin content types.
283+
///
284+
/// \param[in] factor the scale factor
285+
void Scale(double factor)
286+
{
287+
fEngine.Scale(factor);
288+
fStats.Scale(factor);
289+
}
290+
280291
/// %ROOT Streamer function to throw when trying to store an object of this class.
281292
void Streamer(TBuffer &) { throw std::runtime_error("unable to store RHist"); }
282293
};

hist/histv7/inc/ROOT/RHistEngine.hxx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,19 @@ public:
405405
}
406406
}
407407

408+
/// Scale all histogram bin contents.
409+
///
410+
/// This method is not available for integral bin content types.
411+
///
412+
/// \param[in] factor the scale factor
413+
void Scale(double factor)
414+
{
415+
static_assert(!std::is_integral_v<BinContentType>, "scaling is not supported for integral bin content types");
416+
for (std::size_t i = 0; i < fBinContents.size(); i++) {
417+
fBinContents[i] *= factor;
418+
}
419+
}
420+
408421
/// %ROOT Streamer function to throw when trying to store an object of this class.
409422
void Streamer(TBuffer &) { throw std::runtime_error("unable to store RHistEngine"); }
410423
};

hist/histv7/inc/ROOT/RHistStats.hxx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ public:
7575
fSumWX3 = 0.0;
7676
fSumWX4 = 0.0;
7777
}
78+
79+
void Scale(double factor)
80+
{
81+
fSumWX *= factor;
82+
fSumWX2 *= factor;
83+
fSumWX3 *= factor;
84+
fSumWX4 *= factor;
85+
}
7886
};
7987

8088
private:
@@ -393,6 +401,18 @@ public:
393401
}
394402
}
395403

404+
/// Scale the histogram statistics.
405+
///
406+
/// \param[in] factor the scale factor
407+
void Scale(double factor)
408+
{
409+
fSumW *= factor;
410+
fSumW2 *= factor * factor;
411+
for (std::size_t i = 0; i < fDimensionStats.size(); i++) {
412+
fDimensionStats[i].Scale(factor);
413+
}
414+
}
415+
396416
/// %ROOT Streamer function to throw when trying to store an object of this class.
397417
void Streamer(TBuffer &) { throw std::runtime_error("unable to store RHistStats"); }
398418
};

hist/histv7/test/hist_engine.cxx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,28 @@ TEST(RHistEngine, FillTupleWeightInvalidNumberOfArguments)
358358
EXPECT_THROW(engine2.Fill(std::make_tuple(1, 2, 3), RWeight(1)), std::invalid_argument);
359359
}
360360

361+
TEST(RHistEngine, Scale)
362+
{
363+
static constexpr std::size_t Bins = 20;
364+
const RRegularAxis axis(Bins, {0, Bins});
365+
RHistEngine<float> engine({axis});
366+
367+
engine.Fill(-100, RWeight(0.25));
368+
for (std::size_t i = 0; i < Bins; i++) {
369+
engine.Fill(i, RWeight(0.1 + i * 0.03));
370+
}
371+
engine.Fill(100, RWeight(0.75));
372+
373+
static constexpr double Factor = 0.8;
374+
engine.Scale(Factor);
375+
376+
EXPECT_FLOAT_EQ(engine.GetBinContent(RBinIndex::Underflow()), Factor * 0.25);
377+
for (auto index : axis.GetNormalRange()) {
378+
EXPECT_FLOAT_EQ(engine.GetBinContent(index), Factor * (0.1 + index.GetIndex() * 0.03));
379+
}
380+
EXPECT_FLOAT_EQ(engine.GetBinContent(RBinIndex::Overflow()), Factor * 0.75);
381+
}
382+
361383
TEST(RHistEngine_RBinWithError, Add)
362384
{
363385
static constexpr std::size_t Bins = 20;
@@ -415,3 +437,24 @@ TEST(RHistEngine_RBinWithError, FillWeight)
415437
EXPECT_FLOAT_EQ(bin.fSum2, weight * weight);
416438
}
417439
}
440+
441+
TEST(RHistEngine_RBinWithError, Scale)
442+
{
443+
static constexpr std::size_t Bins = 20;
444+
const RRegularAxis axis(Bins, {0, Bins});
445+
RHistEngine<RBinWithError> engine({axis});
446+
447+
for (std::size_t i = 0; i < Bins; i++) {
448+
engine.Fill(i, RWeight(0.1 + i * 0.03));
449+
}
450+
451+
static constexpr double Factor = 0.8;
452+
engine.Scale(Factor);
453+
454+
for (auto index : axis.GetNormalRange()) {
455+
auto &bin = engine.GetBinContent(index);
456+
double weight = Factor * (0.1 + index.GetIndex() * 0.03);
457+
EXPECT_FLOAT_EQ(bin.fSum, weight);
458+
EXPECT_FLOAT_EQ(bin.fSum2, weight * weight);
459+
}
460+
}

hist/histv7/test/hist_hist.cxx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,34 @@ TEST(RHist, FillWeight)
125125
EXPECT_FLOAT_EQ(hist.GetBinContent(indices), 0.9);
126126

127127
EXPECT_EQ(hist.GetNEntries(), 2);
128+
EXPECT_FLOAT_EQ(hist.GetStats().GetSumW(), 1.7);
129+
EXPECT_FLOAT_EQ(hist.GetStats().GetSumW2(), 1.45);
128130
// Cross-checked with TH1
129131
EXPECT_FLOAT_EQ(hist.ComputeNEffectiveEntries(), 1.9931034);
130132
EXPECT_FLOAT_EQ(hist.ComputeMean(), 9.0294118);
131133
EXPECT_FLOAT_EQ(hist.ComputeStdDev(), 0.49913420);
132134
}
135+
136+
TEST(RHist, Scale)
137+
{
138+
static constexpr std::size_t Bins = 20;
139+
const RRegularAxis axis(Bins, {0, Bins});
140+
RHist<float> hist({axis});
141+
142+
hist.Fill(8.5, RWeight(0.8));
143+
hist.Fill(9.5, RWeight(0.9));
144+
145+
static constexpr double Factor = 0.8;
146+
hist.Scale(Factor);
147+
148+
EXPECT_FLOAT_EQ(hist.GetBinContent(8), Factor * 0.8);
149+
EXPECT_FLOAT_EQ(hist.GetBinContent(9), Factor * 0.9);
150+
151+
EXPECT_EQ(hist.GetNEntries(), 2);
152+
EXPECT_FLOAT_EQ(hist.GetStats().GetSumW(), Factor * 1.7);
153+
EXPECT_FLOAT_EQ(hist.GetStats().GetSumW2(), Factor * Factor * 1.45);
154+
// Cross-checked with TH1 - unchanged compared to FillWeight because the factor cancels out.
155+
EXPECT_FLOAT_EQ(hist.ComputeNEffectiveEntries(), 1.9931034);
156+
EXPECT_FLOAT_EQ(hist.ComputeMean(), 9.0294118);
157+
EXPECT_FLOAT_EQ(hist.ComputeStdDev(), 0.49913420);
158+
}

hist/histv7/test/hist_stats.cxx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,3 +455,44 @@ TEST(RHistStats, FillTupleWeightInvalidNumberOfArguments)
455455
EXPECT_NO_THROW(stats2.Fill(std::make_tuple(1, 2), RWeight(1)));
456456
EXPECT_THROW(stats2.Fill(std::make_tuple(1, 2, 3), RWeight(1)), std::invalid_argument);
457457
}
458+
459+
TEST(RHistStats, Scale)
460+
{
461+
RHistStats stats(3);
462+
ASSERT_EQ(stats.GetNEntries(), 0);
463+
464+
static constexpr std::size_t Entries = 20;
465+
for (std::size_t i = 0; i < Entries; i++) {
466+
stats.Fill(i, 2 * i, i * i, RWeight(0.1 + 0.03 * i));
467+
}
468+
469+
static constexpr double Factor = 0.8;
470+
stats.Scale(Factor);
471+
472+
// The number of entries should not have changed.
473+
EXPECT_EQ(stats.GetNEntries(), Entries);
474+
EXPECT_DOUBLE_EQ(stats.GetSumW(), Factor * 7.7);
475+
EXPECT_DOUBLE_EQ(stats.GetSumW2(), Factor * Factor * 3.563);
476+
477+
{
478+
const auto &dimensionStats = stats.GetDimensionStats(/*=0*/);
479+
EXPECT_DOUBLE_EQ(dimensionStats.fSumWX, Factor * 93.1);
480+
EXPECT_DOUBLE_EQ(dimensionStats.fSumWX2, Factor * 1330.0);
481+
EXPECT_DOUBLE_EQ(dimensionStats.fSumWX3, Factor * 20489.98);
482+
EXPECT_DOUBLE_EQ(dimensionStats.fSumWX4, Factor * 330265.6);
483+
}
484+
{
485+
const auto &dimensionStats = stats.GetDimensionStats(1);
486+
EXPECT_DOUBLE_EQ(dimensionStats.fSumWX, Factor * 2 * 93.1);
487+
EXPECT_DOUBLE_EQ(dimensionStats.fSumWX2, Factor * 4 * 1330.0);
488+
EXPECT_DOUBLE_EQ(dimensionStats.fSumWX3, Factor * 8 * 20489.98);
489+
EXPECT_DOUBLE_EQ(dimensionStats.fSumWX4, Factor * 16 * 330265.6);
490+
}
491+
{
492+
const auto &dimensionStats = stats.GetDimensionStats(2);
493+
EXPECT_DOUBLE_EQ(dimensionStats.fSumWX, Factor * 1330.0);
494+
EXPECT_DOUBLE_EQ(dimensionStats.fSumWX2, Factor * 330265.6);
495+
EXPECT_DOUBLE_EQ(dimensionStats.fSumWX3, Factor * 93164182.0);
496+
EXPECT_DOUBLE_EQ(dimensionStats.fSumWX4, Factor * 28108731464.8);
497+
}
498+
}

hist/histv7/test/hist_user.cxx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ struct User {
3131
return *this;
3232
}
3333

34+
User &operator*=(double factor)
35+
{
36+
fValue *= factor;
37+
return *this;
38+
}
39+
3440
void AtomicInc() { ROOT::Experimental::Internal::AtomicInc(&fValue); }
3541

3642
void AtomicAdd(double w) { ROOT::Experimental::Internal::AtomicAdd(&fValue, w); }
@@ -153,3 +159,20 @@ TEST(RHistEngineUser, FillAtomicWeight)
153159
std::array<RBinIndex, 1> indices = {9};
154160
EXPECT_EQ(engine.GetBinContent(indices).fValue, 0.9);
155161
}
162+
163+
TEST(RHistEngineUser, Scale)
164+
{
165+
// Scaling uses operator+=(double)
166+
static constexpr std::size_t Bins = 20;
167+
const RRegularAxis axis(Bins, {0, Bins});
168+
RHistEngine<User> engine({axis});
169+
170+
engine.Fill(8.5, RWeight(0.8));
171+
engine.Fill(9.5, RWeight(0.9));
172+
173+
static constexpr double Factor = 0.8;
174+
engine.Scale(Factor);
175+
176+
EXPECT_EQ(engine.GetBinContent(8).fValue, Factor * 0.8);
177+
EXPECT_EQ(engine.GetBinContent(9).fValue, Factor * 0.9);
178+
}

0 commit comments

Comments
 (0)