Skip to content

Commit 654d8f2

Browse files
committed
[hist] Implement variadic RProfile::Fill
1 parent 13e2cd3 commit 654d8f2

3 files changed

Lines changed: 145 additions & 10 deletions

File tree

hist/histv7/inc/ROOT/RHistEngine.hxx

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ class RHistEngine final {
7575
// For slicing, RHist needs to call SliceImpl.
7676
friend class RHist<BinContentType>;
7777

78+
friend class RProfile;
79+
7880
/// The axis configuration for this histogram. Relevant methods are forwarded from the public interface.
7981
Internal::RAxes fAxes;
8082
/// The bin contents for this histogram
@@ -407,6 +409,25 @@ public:
407409
}
408410
}
409411

412+
/// \}
413+
// End the group to ensure that all contained member functions are public.
414+
415+
private:
416+
// Also used by RProfile::Fill(const A &... args)
417+
template <std::size_t N, typename... A, typename W>
418+
void FillImpl(const std::tuple<A...> &args, const W &weight)
419+
{
420+
RLinearizedIndex index = fAxes.ComputeGlobalIndexImpl<N>(args);
421+
if (index.fValid) {
422+
assert(index.fIndex < fBinContents.size());
423+
fBinContents[index.fIndex] += weight;
424+
}
425+
}
426+
427+
public:
428+
/// \name Filling
429+
/// \{
430+
410431
/// Fill an entry into the histogram with a user-defined weight.
411432
///
412433
/// This overload is only available for user-defined bin content types.
@@ -430,11 +451,7 @@ public:
430451
if (sizeof...(A) != GetNDimensions()) {
431452
throw std::invalid_argument("invalid number of arguments to Fill");
432453
}
433-
RLinearizedIndex index = fAxes.ComputeGlobalIndexImpl<sizeof...(A)>(args);
434-
if (index.fValid) {
435-
assert(index.fIndex < fBinContents.size());
436-
fBinContents[index.fIndex] += weight;
437-
}
454+
FillImpl<sizeof...(A)>(args, weight);
438455
}
439456

440457
/// Fill an entry into the histogram.

hist/histv7/inc/ROOT/RProfile.hxx

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "RAxisVariant.hxx"
99
#include "RBinIndex.hxx"
1010
#include "RHistEngine.hxx"
11+
#include "RHistUtils.hxx"
1112
#include "RRegularAxis.hxx"
1213
#include "RWeight.hxx"
1314

@@ -17,6 +18,7 @@
1718
#include <initializer_list>
1819
#include <stdexcept>
1920
#include <tuple>
21+
#include <type_traits>
2022
#include <utility>
2123
#include <vector>
2224

@@ -28,11 +30,11 @@ namespace Experimental {
2830
/**
2931
A profile histogram, computing statistical quantities of an additional variable per bin.
3032
31-
Calling \ref Fill(const std::tuple<A...> &args, const V &value) "Fill" requires an additional value:
33+
Calling \ref Fill(const A &... args) "Fill" requires an additional value:
3234
\code
3335
ROOT::Experimental::RProfile profile(10, {5, 15});
34-
profile.Fill(std::make_tuple(8.2), 23.0);
35-
profile.Fill(std::make_tuple(8.7), 25.0);
36+
profile.Fill(8.2, 23.0);
37+
profile.Fill(8.7, 25.0);
3638
// Bin 3 has a mean of 24.0 and a standard deviation of 1.0
3739
\endcode
3840
@@ -257,7 +259,8 @@ public:
257259
/// \param[in] args the arguments for each axis
258260
/// \param[in] v the additional argument
259261
/// \par See also
260-
/// the \ref Fill(const std::tuple<A...> &args, const V &value, RWeight weight) "overload for weighted filling"
262+
/// the \ref Fill(const A &... args) "variadic function template overload" accepting arguments directly and the
263+
/// \ref Fill(const std::tuple<A...> &args, const V &value, RWeight weight) "overload for weighted filling"
261264
template <typename... A, typename V>
262265
void Fill(const std::tuple<A...> &args, const V &value)
263266
{
@@ -283,14 +286,63 @@ public:
283286
/// \param[in] v the additional argument
284287
/// \param[in] weight the weight for this entry
285288
/// \par See also
286-
/// the \ref Fill(const std::tuple<A...> &args, const V &value) "overload for unweighted filling"
289+
/// the \ref Fill(const A &... args) "variadic function template overload" accepting arguments directly and the
290+
/// \ref Fill(const std::tuple<A...> &args, const V &value) "overload for unweighted filling"
287291
template <typename... A, typename V>
288292
void Fill(const std::tuple<A...> &args, const V &value, RWeight weight)
289293
{
290294
RValueWeightWrapper wrapper(value, weight.fValue);
291295
fEngine.Fill(args, wrapper);
292296
}
293297

298+
/// Fill an entry into the profile histogram.
299+
///
300+
/// \code
301+
/// ROOT::Experimental::RProfile profile({/* two dimensions */});
302+
/// profile.Fill(8.5, 10.5, 23.0);
303+
/// \endcode
304+
///
305+
/// For weighted filling, pass an RWeight as the last argument:
306+
/// \code
307+
/// profile.Fill(8.5, 10.5, 23.0, ROOT::Experimental::RWeight(0.8));
308+
/// \endcode
309+
///
310+
/// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
311+
/// discarded.
312+
///
313+
/// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
314+
/// converted for the axis type at run-time.
315+
///
316+
/// \param[in] args the arguments for each axis
317+
/// \par See also
318+
/// the function overloads accepting `std::tuple`
319+
/// \ref Fill(const std::tuple<A...> &args, const V &value) "for unweighted filling" and
320+
/// \ref Fill(const std::tuple<A...> &args, const V &value, RWeight weight) "for weighted filling"
321+
template <typename... A>
322+
void Fill(const A &...args)
323+
{
324+
static_assert(sizeof...(A) >= 2, "need at least two arguments to Fill");
325+
if constexpr (sizeof...(A) >= 2) {
326+
auto t = std::forward_as_tuple(args...);
327+
if constexpr (std::is_same_v<typename Internal::LastType<A...>::type, RWeight>) {
328+
static constexpr std::size_t N = sizeof...(A) - 2;
329+
if (N != GetNDimensions()) {
330+
throw std::invalid_argument("invalid number of arguments to Fill");
331+
}
332+
RWeight weight = std::get<N + 1>(t);
333+
RValueWeightWrapper wrapper(std::get<N>(t), weight.fValue);
334+
fEngine.FillImpl<N>(t, wrapper);
335+
} else {
336+
static constexpr std::size_t N = sizeof...(A) - 1;
337+
if (N != GetNDimensions()) {
338+
throw std::invalid_argument("invalid number of arguments to Fill");
339+
}
340+
RValueWrapper wrapper(std::get<N>(t));
341+
fEngine.FillImpl<N>(t, wrapper);
342+
}
343+
}
344+
}
345+
294346
/// \}
295347

296348
/// %ROOT Streamer function to throw when trying to store an object of this class.

hist/histv7/test/hist_profile.cxx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,14 @@ TEST(RProfile, Fill)
5959
static constexpr std::size_t Bins = 20;
6060
RProfile profile(Bins, {0, Bins});
6161

62+
profile.Fill(8.5, 23.0);
6263
profile.Fill(std::make_tuple(9.5), 25.0);
6364

65+
auto &bin8 = profile.GetBinContent(RBinIndex(8));
66+
EXPECT_EQ(bin8.fSumValues, 23.0);
67+
EXPECT_EQ(bin8.fSumValues2, 529.0);
68+
EXPECT_EQ(bin8.fSum, 1.0);
69+
EXPECT_EQ(bin8.fSum2, 1.0);
6470
std::array<RBinIndex, 1> indices = {9};
6571
auto &bin9 = profile.GetBinContent(indices);
6672
EXPECT_EQ(bin9.fSumValues, 25.0);
@@ -69,13 +75,36 @@ TEST(RProfile, Fill)
6975
EXPECT_EQ(bin9.fSum2, 1.0);
7076
}
7177

78+
TEST(RProfile, FillInvalidNumberOfArguments)
79+
{
80+
static constexpr std::size_t Bins = 20;
81+
const RRegularAxis axis(Bins, {0, Bins});
82+
RProfile profile1(axis);
83+
ASSERT_EQ(profile1.GetNDimensions(), 1);
84+
RProfile profile2(axis, axis);
85+
ASSERT_EQ(profile2.GetNDimensions(), 2);
86+
87+
EXPECT_NO_THROW(profile1.Fill(1, 2));
88+
EXPECT_THROW(profile1.Fill(1, 2, 3), std::invalid_argument);
89+
90+
EXPECT_THROW(profile2.Fill(1, 2), std::invalid_argument);
91+
EXPECT_NO_THROW(profile2.Fill(1, 2, 3));
92+
EXPECT_THROW(profile2.Fill(1, 2, 3, 4), std::invalid_argument);
93+
}
94+
7295
TEST(RProfile, FillWeight)
7396
{
7497
static constexpr std::size_t Bins = 20;
7598
RProfile profile(Bins, {0, Bins});
7699

100+
profile.Fill(8.5, 23.0, RWeight(0.8));
77101
profile.Fill(std::make_tuple(9.5), 25.0, RWeight(0.9));
78102

103+
auto &bin8 = profile.GetBinContent(RBinIndex(8));
104+
EXPECT_FLOAT_EQ(bin8.fSumValues, 18.4);
105+
EXPECT_FLOAT_EQ(bin8.fSumValues2, 423.2);
106+
EXPECT_FLOAT_EQ(bin8.fSum, 0.8);
107+
EXPECT_FLOAT_EQ(bin8.fSum2, 0.64);
79108
std::array<RBinIndex, 1> indices = {9};
80109
auto &bin9 = profile.GetBinContent(indices);
81110
EXPECT_FLOAT_EQ(bin9.fSumValues, 22.5);
@@ -84,14 +113,38 @@ TEST(RProfile, FillWeight)
84113
EXPECT_FLOAT_EQ(bin9.fSum2, 0.81);
85114
}
86115

116+
TEST(RProfile, FillWeightInvalidNumberOfArguments)
117+
{
118+
static constexpr std::size_t Bins = 20;
119+
const RRegularAxis axis(Bins, {0, Bins});
120+
RProfile profile1(axis);
121+
ASSERT_EQ(profile1.GetNDimensions(), 1);
122+
RProfile profile2(axis, axis);
123+
ASSERT_EQ(profile2.GetNDimensions(), 2);
124+
125+
EXPECT_THROW(profile1.Fill(1, RWeight(1)), std::invalid_argument);
126+
EXPECT_NO_THROW(profile1.Fill(1, 2, RWeight(1)));
127+
EXPECT_THROW(profile1.Fill(1, 2, 3, RWeight(1)), std::invalid_argument);
128+
129+
EXPECT_THROW(profile2.Fill(1, 2, RWeight(1)), std::invalid_argument);
130+
EXPECT_NO_THROW(profile2.Fill(1, 2, 3, RWeight(1)));
131+
EXPECT_THROW(profile2.Fill(1, 2, 3, 4, RWeight(1)), std::invalid_argument);
132+
}
133+
87134
TEST(RProfile, FillCategorical)
88135
{
89136
const std::vector<std::string> categories = {"a", "b", "c"};
90137
const RCategoricalAxis axis(categories);
91138
RProfile profile({axis});
92139

140+
profile.Fill("b", 23.0);
93141
profile.Fill(std::make_tuple("c"), 25.0);
94142

143+
auto &bin1 = profile.GetBinContent(RBinIndex(1));
144+
EXPECT_EQ(bin1.fSumValues, 23.0);
145+
EXPECT_EQ(bin1.fSumValues2, 529.0);
146+
EXPECT_EQ(bin1.fSum, 1.0);
147+
EXPECT_EQ(bin1.fSum2, 1.0);
95148
std::array<RBinIndex, 1> indices = {2};
96149
auto &bin2 = profile.GetBinContent(indices);
97150
EXPECT_EQ(bin2.fSumValues, 25.0);
@@ -106,8 +159,14 @@ TEST(RProfile, FillCategoricalWeight)
106159
const RCategoricalAxis axis(categories);
107160
RProfile profile({axis});
108161

162+
profile.Fill("b", 23.0, RWeight(0.8));
109163
profile.Fill(std::make_tuple("c"), 25.0, RWeight(0.9));
110164

165+
auto &bin1 = profile.GetBinContent(RBinIndex(1));
166+
EXPECT_FLOAT_EQ(bin1.fSumValues, 18.4);
167+
EXPECT_FLOAT_EQ(bin1.fSumValues2, 423.2);
168+
EXPECT_FLOAT_EQ(bin1.fSum, 0.8);
169+
EXPECT_FLOAT_EQ(bin1.fSum2, 0.64);
111170
std::array<RBinIndex, 1> indices = {2};
112171
auto &bin2 = profile.GetBinContent(indices);
113172
EXPECT_FLOAT_EQ(bin2.fSumValues, 22.5);
@@ -151,4 +210,11 @@ TEST(RProfile, FillForward)
151210
EXPECT_EQ(profile.GetBinContent(1).fSumValues, 34.5);
152211

153212
ASSERT_FALSE(CopyArgument::HasBeenCopied());
213+
214+
CopyArgument arg(2.5);
215+
profile.Fill(arg, value);
216+
profile.Fill(arg, value, RWeight(0.5));
217+
EXPECT_EQ(profile.GetBinContent(2).fSumValues, 34.5);
218+
219+
ASSERT_FALSE(CopyArgument::HasBeenCopied());
154220
}

0 commit comments

Comments
 (0)