Logo ROOT  
Reference Guide
RMergeableValue.hxx
Go to the documentation of this file.
1/**
2 \file ROOT/RDF/RMergeableValue.hxx
3 \ingroup dataframe
4 \author Vincenzo Eduardo Padulano
5 \author Enrico Guiraud
6 \date 2020-06
7*/
8
9/*************************************************************************
10 * Copyright (C) 1995-2022, Rene Brun and Fons Rademakers. *
11 * All rights reserved. *
12 * *
13 * For the licensing terms see $ROOTSYS/LICENSE. *
14 * For the list of contributors see $ROOTSYS/README/CREDITS. *
15 *************************************************************************/
16
17#ifndef ROOT_RDF_RMERGEABLEVALUE
18#define ROOT_RDF_RMERGEABLEVALUE
19
20#include <algorithm> // std::find, std::min, std::max
21#include <iterator> // std::distance
22#include <memory>
23#include <stdexcept>
24#include <string>
25#include <vector>
26
27#include "RtypesCore.h"
28#include "TError.h" // R__ASSERT
29#include "TList.h" // RMergeableFill::Merge
30
31namespace ROOT {
32namespace Detail {
33namespace RDF {
34
35// Fwd declarations for RMergeableValue
36template <typename T>
37class RMergeableValue;
38
39template <typename T>
40class RMergeableVariations;
41
42template <typename T, typename... Ts>
43std::unique_ptr<RMergeableValue<T>> MergeValues(std::unique_ptr<RMergeableValue<T>> OutputMergeable,
44 std::unique_ptr<RMergeableValue<Ts>>... InputMergeables);
45
46template <typename T, typename... Ts>
47void MergeValues(RMergeableValue<T> &OutputMergeable, const RMergeableValue<Ts> &... InputMergeables);
48
49template <typename T, typename... Ts>
50void MergeValues(RMergeableVariations<T> &OutputMergeable, const RMergeableVariations<Ts> &... InputMergeables);
51
52/**
53\class ROOT::Detail::RDF::RMergeableValueBase
54\brief Base class of RMergeableValue.
55\ingroup dataframe
56Base class of the mergeable RDataFrame results family of classes. Provides a
57non-templated custom type to allow passing a `std::unique_ptr` to the mergeable
58object along the call chain. This class is never used in the public API and has
59no meaning for the final user.
60*/
62public:
63 virtual ~RMergeableValueBase() = default;
64 /**
65 Default constructor. Needed to allow serialization of ROOT objects. See
66 [TBufferFile::WriteObjectClass]
67 (classTBufferFile.html#a209078a4cb58373b627390790bf0c9c1)
68 */
74};
75
76/**
77\class ROOT::Detail::RDF::RMergeableValue
78\ingroup dataframe
79\brief A result of an RDataFrame execution, that knows how to merge with other
80results of the same type.
81\tparam T Type of the action result.
82
83Results of the execution of an RDataFrame computation graph do not natively
84know how to merge with other results of the same type. In a distributed
85environment it is often needed to have a merging mechanism for partial results
86coming from the execution of an analysis on different chunks of the same dataset
87that has happened on different executors. In order to achieve this,
88RMergeableValue stores the result of the RDataFrame action and has a `Merge`
89method to allow the aggregation of information coming from another similar
90result into the current.
91
92A mergeable value can be retrieved from an RResultPtr through the
93[GetMergeableValue]
94(namespaceROOT_1_1Detail_1_1RDF.html#a8b3a9c7b416826acc952d78a56d14ecb) free
95function and a sequence of mergeables can be merged together with the helper
96function [MergeValues]
97(namespaceROOT_1_1Detail_1_1RDF.html#af16fefbe2d120983123ddf8a1e137277).
98All the classes and functions involved are inside the `ROOT::Detail::RDF`
99namespace.
100
101In a nutshell:
102~~~{.cpp}
103using namespace ROOT::Detail::RDF;
104ROOT::RDataFrame d("myTree", "file_*.root");
105auto h1 = d.Histo1D("Branch_A");
106auto h2 = d.Histo1D("Branch_A");
107
108// Retrieve mergeables from the `RResultPtr`s
109auto mergeableh1 = GetMergeableValue(h1);
110auto mergeableh2 = GetMergeableValue(h2);
111
112// Merge the values and get another mergeable back
113auto mergedptr = MergeValues(std::move(mergeableh1), std::move(mergeableh2));
114
115// Retrieve the merged TH1D object
116const auto &mergedhisto = mergedptr->GetValue();
117~~~
118
119Though this snippet can run on a single thread of a single machine, it is
120straightforward to generalize it to a distributed case, e.g. where `mergeableh1`
121and `mergeableh2` are created on separate machines and sent to a `reduce`
122process where the `MergeValues` function is called. The final user would then
123just be given the final merged result coming from `mergedptr->GetValue`.
124
125RMergeableValue is the base class for all the different specializations that may
126be needed according to the peculiarities of the result types. The following
127subclasses, their names hinting at the action operation of the result, are
128currently available:
129
130- RMergeableCount
131- RMergeableFill, responsible for the following actions:
132 - Graph
133 - Histo{1,2,3}D
134 - Profile{1,2}D
135 - Stats
136- RMergeableMax
137- RMergeableMean
138- RMergeableMin
139- RMergeableStdDev
140- RMergeableSum
141*/
142template <typename T>
144 // Friend function declarations
145 template <typename T1, typename... Ts>
146 friend std::unique_ptr<RMergeableValue<T1>> MergeValues(std::unique_ptr<RMergeableValue<T1>> OutputMergeable,
147 std::unique_ptr<RMergeableValue<Ts>>... InputMergeables);
148 template <typename T1, typename... Ts>
149 friend void MergeValues(RMergeableValue<T1> &OutputMergeable, const RMergeableValue<Ts> &... InputMergeables);
150
151 /////////////////////////////////////////////////////////////////////////////
152 /// \brief Aggregate the information contained in another RMergeableValue
153 /// into this.
154 ///
155 /// Virtual function reimplemented in all the subclasses.
156 ///
157 /// \note All the `Merge` methods in the RMergeableValue family are private.
158 /// To merge multiple RMergeableValue objects please use [MergeValues]
159 /// (namespaceROOT_1_1Detail_1_1RDF.html#af16fefbe2d120983123ddf8a1e137277).
160 virtual void Merge(const RMergeableValue<T> &) = 0;
161
162protected:
164
165public:
166 /**
167 Constructor taking the action result by const reference. This involves a
168 copy of the result into the data member, but gives full ownership of data
169 to the mergeable.
170 */
172 /**
173 Default constructor. Needed to allow serialization of ROOT objects. See
174 [TBufferFile::WriteObjectClass]
175 (classTBufferFile.html#a209078a4cb58373b627390790bf0c9c1)
176 */
177 RMergeableValue() = default;
182 /////////////////////////////////////////////////////////////////////////////
183 /// \brief Retrieve the result wrapped by this mergeable.
184 const T &GetValue() const { return fValue; }
185};
186
187/**
188\class ROOT::Detail::RDF::RMergeableCount
189\ingroup dataframe
190\brief Specialization of RMergeableValue for the
191[Count](classROOT_1_1RDF_1_1RInterface.html#a9678150c9c18cddd7b599690ba854734)
192action.
193*/
194class RMergeableCount final : public RMergeableValue<ULong64_t> {
195 /////////////////////////////////////////////////////////////////////////////
196 /// \brief Aggregate the information contained in another RMergeableValue
197 /// into this.
198 /// \param[in] other Another RMergeableValue object.
199 /// \throws std::invalid_argument If the cast of the other object to the same
200 /// type as this one fails.
201 ///
202 /// The other RMergeableValue object is cast to the same type as this object.
203 /// This is needed to make sure that only results of the same type of action
204 /// are merged together. Then the two results are added together to update
205 /// the value held by the current object.
206 ///
207 /// \note All the `Merge` methods in the RMergeableValue family are private.
208 /// To merge multiple RMergeableValue objects please use [MergeValues]
209 /// (namespaceROOT_1_1Detail_1_1RDF.html#af16fefbe2d120983123ddf8a1e137277).
210 void Merge(const RMergeableValue<ULong64_t> &other) final
211 {
212 try {
213 const auto &othercast = dynamic_cast<const RMergeableCount &>(other);
214 this->fValue += othercast.fValue;
215 } catch (const std::bad_cast &) {
216 throw std::invalid_argument("Results from different actions cannot be merged together.");
217 }
218 }
219
220public:
221 /////////////////////////////////////////////////////////////////////////////
222 /// \brief Constructor that initializes data members.
223 /// \param[in] value The action result.
225 /**
226 Default constructor. Needed to allow serialization of ROOT objects. See
227 [TBufferFile::WriteObjectClass]
228 (classTBufferFile.html#a209078a4cb58373b627390790bf0c9c1)
229 */
230 RMergeableCount() = default;
235};
236
237/**
238\class ROOT::Detail::RDF::RMergeableFill
239\ingroup dataframe
240\brief Specialization of RMergeableValue for histograms and statistics.
241
242This subclass is responsible for merging results coming from the following
243actions:
244- [Graph](classROOT_1_1RDF_1_1RInterface.html#a804b466ebdbddef5c7e3400cc6b89301)
245- [Histo{1D,2D,3D}]
246 (classROOT_1_1RDF_1_1RInterface.html#a247ca3aeb7ce5b95015b7fae72983055)
247- [HistoND](classROOT_1_1RDF_1_1RInterface.html#a0c9956a0f48c26f8e4294e17376c7fea)
248- [Profile{1D,2D}]
249 (classROOT_1_1RDF_1_1RInterface.html#a8ef7dc16b0e9f7bc9cfbe2d9e5de0cef)
250- [Stats](classROOT_1_1RDF_1_1RInterface.html#abc68922c464e472f5f856e8981955af6)
251
252*/
253template <typename T>
254class RMergeableFill final : public RMergeableValue<T> {
255
256 // RDataFrame's generic Fill method supports two possible signatures for Merge.
257 // Templated to create a dependent type to SFINAE on - in reality, `U` will always be `T`.
258 // This overload handles Merge(TCollection*)...
260 auto DoMerge(const RMergeableFill<U> &other, int /*toincreaseoverloadpriority*/)
261 -> decltype(((U &)this->fValue).Merge((TCollection *)nullptr), void())
262 {
263 TList l; // The `Merge` method accepts a TList
264 l.Add(const_cast<U *>(&other.fValue)); // Ugly but needed because of the signature of TList::Add
265 this->fValue.Merge(&l); // if `T == TH1D` Eventually calls TH1::ExtendAxis that creates new instances of TH1D
266 }
267
268 // ...and this one handles Merge(const std::vector<T*> &)
269 template <typename U>
270 auto DoMerge(const RMergeableFill<U> &other, double /*todecreaseoverloadpriority*/)
271 -> decltype(this->fValue.Merge(std::vector<U *>{}), void())
272 {
273 this->fValue.Merge({const_cast<U *>(&other.fValue)});
274 }
275
276 /////////////////////////////////////////////////////////////////////////////
277 /// \brief Aggregate the information contained in another RMergeableValue
278 /// into this.
279 /// \param[in] other Another RMergeableValue object.
280 /// \throws std::invalid_argument If the cast of the other object to the same
281 /// type as this one fails.
282 ///
283 /// The other RMergeableValue object is cast to the same type as this object.
284 /// This is needed to make sure that only results of the same type of action
285 /// are merged together. The function then calls the right `Merge` method
286 /// according to the class of the fValue data member.
287 ///
288 /// \note All the `Merge` methods in the RMergeableValue family are private.
289 /// To merge multiple RMergeableValue objects please use [MergeValues]
290 /// (namespaceROOT_1_1Detail_1_1RDF.html#af16fefbe2d120983123ddf8a1e137277).
291 void Merge(const RMergeableValue<T> &other) final
292 {
293 try {
294 const auto &othercast = dynamic_cast<const RMergeableFill<T> &>(other);
295 DoMerge(othercast, /*toselecttherightoverload=*/0);
296 } catch (const std::bad_cast &) {
297 throw std::invalid_argument("Results from different actions cannot be merged together.");
298 }
299 }
300
301public:
302 /////////////////////////////////////////////////////////////////////////////
303 /// \brief Constructor that initializes data members.
304 /// \param[in] value The action result.
306 /**
307 Default constructor. Needed to allow serialization of ROOT objects. See
308 [TBufferFile::WriteObjectClass]
309 (classTBufferFile.html#a209078a4cb58373b627390790bf0c9c1)
310 */
311 RMergeableFill() = default;
316};
317
318template <typename T>
319class RMergeableMax final : public RMergeableValue<T> {
320
321 void Merge(const RMergeableValue<T> &other) final
322 {
323 try {
324 const auto &othercast = dynamic_cast<const RMergeableMax<T> &>(other);
325 this->fValue = std::max(this->fValue, othercast.fValue);
326 } catch (const std::bad_cast &) {
327 throw std::invalid_argument("Results from different actions cannot be merged together.");
328 }
329 }
330
331public:
332 /////////////////////////////////////////////////////////////////////////////
333 /// \brief Constructor that initializes data members.
334 /// \param[in] value The action result.
336 /**
337 Default constructor. Needed to allow serialization of ROOT objects. See
338 [TBufferFile::WriteObjectClass]
339 (classTBufferFile.html#a209078a4cb58373b627390790bf0c9c1)
340 */
341 RMergeableMax() = default;
342 RMergeableMax(const RMergeableMax &) = delete;
346};
347
348/**
349\class ROOT::Detail::RDF::RMergeableMean
350\ingroup dataframe
351\brief Specialization of RMergeableValue for the
352[Mean](classROOT_1_1RDF_1_1RInterface.html#ade6b020284f2f4fe9d3b09246b5f376a)
353action.
354
355This subclass is responsible for merging results coming from Mean actions. Other
356than the result itself, the number of entries that were used to compute that
357mean is also stored in the object.
358*/
359class RMergeableMean final : public RMergeableValue<Double_t> {
360 ULong64_t fCounts; ///< The number of entries used to compute the mean.
361
362 /////////////////////////////////////////////////////////////////////////////
363 /// \brief Aggregate the information contained in another RMergeableValue
364 /// into this.
365 /// \param[in] other Another RMergeableValue object.
366 /// \throws std::invalid_argument If the cast of the other object to the same
367 /// type as this one fails.
368 ///
369 /// The other RMergeableValue object is cast to the same type as this object.
370 /// This is needed to make sure that only results of the same type of action
371 /// are merged together. The function then computes the weighted mean of the
372 /// two means held by the mergeables.
373 ///
374 /// \note All the `Merge` methods in the RMergeableValue family are private.
375 /// To merge multiple RMergeableValue objects please use [MergeValues]
376 /// (namespaceROOT_1_1Detail_1_1RDF.html#af16fefbe2d120983123ddf8a1e137277).
377 void Merge(const RMergeableValue<Double_t> &other) final
378 {
379 try {
380 const auto &othercast = dynamic_cast<const RMergeableMean &>(other);
381 const auto &othervalue = othercast.fValue;
382 const auto &othercounts = othercast.fCounts;
383
384 // Compute numerator and denumerator of the weighted mean
385 const auto num = this->fValue * fCounts + othervalue * othercounts;
386 const auto denum = static_cast<Double_t>(fCounts + othercounts);
387
388 // Update data members
389 this->fValue = num / denum;
390 fCounts += othercounts;
391 } catch (const std::bad_cast &) {
392 throw std::invalid_argument("Results from different actions cannot be merged together.");
393 }
394 }
395
396public:
397 /////////////////////////////////////////////////////////////////////////////
398 /// \brief Constructor that initializes data members.
399 /// \param[in] value The action result.
400 /// \param[in] counts The number of entries used to compute that result.
402 /**
403 Default constructor. Needed to allow serialization of ROOT objects. See
404 [TBufferFile::WriteObjectClass]
405 (classTBufferFile.html#a209078a4cb58373b627390790bf0c9c1)
406 */
407 RMergeableMean() = default;
412};
413
414template <typename T>
415class RMergeableMin final : public RMergeableValue<T> {
416
417 void Merge(const RMergeableValue<T> &other) final
418 {
419 try {
420 const auto &othercast = dynamic_cast<const RMergeableMin<T> &>(other);
421 this->fValue = std::min(this->fValue, othercast.fValue);
422 } catch (const std::bad_cast &) {
423 throw std::invalid_argument("Results from different actions cannot be merged together.");
424 }
425 }
426
427public:
428 /////////////////////////////////////////////////////////////////////////////
429 /// \brief Constructor that initializes data members.
430 /// \param[in] value The action result.
432 /**
433 Default constructor. Needed to allow serialization of ROOT objects. See
434 [TBufferFile::WriteObjectClass]
435 (classTBufferFile.html#a209078a4cb58373b627390790bf0c9c1)
436 */
437 RMergeableMin() = default;
438 RMergeableMin(const RMergeableMin &) = delete;
442};
443
444/**
445\class ROOT::Detail::RDF::RMergeableStdDev
446\ingroup dataframe
447\brief Specialization of RMergeableValue for the
448[StdDev](classROOT_1_1RDF_1_1RInterface.html#a482c4e4f81fe1e421c016f89cd281572)
449action.
450
451This class also stores information about the number of entries and the average
452used to compute the standard deviation.
453*/
454class RMergeableStdDev final : public RMergeableValue<Double_t> {
455 ULong64_t fCounts; ///< Number of entries of the set.
456 Double_t fMean; ///< Average of the set.
457
458 /////////////////////////////////////////////////////////////////////////////
459 /// \brief Aggregate the information contained in another RMergeableValue
460 /// into this.
461 /// \param[in] other Another RMergeableValue object.
462 /// \throws std::invalid_argument If the cast of the other object to the same
463 /// type as this one fails.
464 ///
465 /// The other RMergeableValue object is cast to the same type as this object.
466 /// This is needed to make sure that only results of the same type of action
467 /// are merged together. The function then computes the aggregated standard
468 /// deviation of the two samples using an algorithm by
469 /// [Chan et al. (1979)]
470 /// (http://i.stanford.edu/pub/cstr/reports/cs/tr/79/773/CS-TR-79-773.pdf)
471 ///
472 /// \note All the `Merge` methods in the RMergeableValue family are private.
473 /// To merge multiple RMergeableValue objects please use [MergeValues]
474 /// (namespaceROOT_1_1Detail_1_1RDF.html#af16fefbe2d120983123ddf8a1e137277).
475 void Merge(const RMergeableValue<Double_t> &other) final
476 {
477 try {
478 const auto &othercast = dynamic_cast<const RMergeableStdDev &>(other);
479 const auto &othercounts = othercast.fCounts;
480 const auto &othermean = othercast.fMean;
481
482 // Compute the aggregated variance using an algorithm by Chan et al.
483 // See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Parallel_algorithm
484 const auto thisvariance = std::pow(this->fValue, 2);
485 const auto othervariance = std::pow(othercast.fValue, 2);
486
487 const auto delta = othermean - fMean;
488
489 const auto m_a = thisvariance * (fCounts - 1);
490 const auto m_b = othervariance * (othercounts - 1);
491
492 const auto sumcounts = static_cast<Double_t>(fCounts + othercounts);
493
494 const auto M2 = m_a + m_b + std::pow(delta, 2) * fCounts * othercounts / sumcounts;
495
496 const auto meannum = fMean * fCounts + othermean * othercounts;
497
498 // Update the data members
499 this->fValue = std::sqrt(M2 / (sumcounts - 1));
500 fMean = meannum / sumcounts;
501 fCounts += othercounts;
502 } catch (const std::bad_cast &) {
503 throw std::invalid_argument("Results from different actions cannot be merged together.");
504 }
505 }
506
507public:
508 /////////////////////////////////////////////////////////////////////////////
509 /// \brief Constructor that initializes data members.
510 /// \param[in] value The action result.
511 /// \param[in] counts The number of entries of the set.
512 /// \param[in] mean The average of the set.
514 : RMergeableValue<Double_t>(value), fCounts{counts}, fMean{mean}
515 {
516 }
517 /**
518 Default constructor. Needed to allow serialization of ROOT objects. See
519 [TBufferFile::WriteObjectClass]
520 (classTBufferFile.html#a209078a4cb58373b627390790bf0c9c1)
521 */
522 RMergeableStdDev() = default;
527};
528
529template <typename T>
530class RMergeableSum final : public RMergeableValue<T> {
531
532 void Merge(const RMergeableValue<T> &other) final
533 {
534 try {
535 const auto &othercast = dynamic_cast<const RMergeableSum<T> &>(other);
536 this->fValue += othercast.fValue;
537 } catch (const std::bad_cast &) {
538 throw std::invalid_argument("Results from different actions cannot be merged together.");
539 }
540 }
541
542public:
543 /////////////////////////////////////////////////////////////////////////////
544 /// \brief Constructor that initializes data members.
545 /// \param[in] value The action result.
547 /**
548 Default constructor. Needed to allow serialization of ROOT objects. See
549 [TBufferFile::WriteObjectClass]
550 (classTBufferFile.html#a209078a4cb58373b627390790bf0c9c1)
551 */
552 RMergeableSum() = default;
553 RMergeableSum(const RMergeableSum &) = delete;
557};
558
559/**
560\class ROOT::Detail::RDF::RMergeableVariationsBase
561\ingroup dataframe
562\brief A container for variation names and variation results.
563
564The class stores two vectors: one with the variation names, the other with
565corresponding mergeable variation values. These are retrieved from an RVariedAction
566(resulting from a call to ROOT::RDF::VariationsFor). The results are stored as
567type-erased RMergeableValueBase objects.
568*/
570protected:
571 std::vector<std::string> fKeys;
572 std::vector<std::unique_ptr<RMergeableValueBase>> fValues;
573
574public:
575 /**
576 Default constructor. Needed to allow serialization of ROOT objects. See
577 [TBufferFile::WriteObjectClass]
578 (classTBufferFile.html#a209078a4cb58373b627390790bf0c9c1)
579 */
583 /////////////////////////////////////////////////////////////////////////////
584 /// \brief Constructor that moves the data members from the input object.
585 /// \param[in] other The container from which the data members are moved.
586 ///
587 /// This constructor is needed as an helper in the RMergeableVariations
588 /// constructor that takes an RMergeableVariationsBase as input.
590 : fKeys{std::move(other.fKeys)}, fValues{std::move(other.fValues)}
591 {
592 }
594
595 /////////////////////////////////////////////////////////////////////////////
596 /// \brief Constructor that initializes data members.
597 /// \param[in] keys The names of the variations.
598 /// \param[in] values The mergeable values containing the results of the
599 /// variations.
600 RMergeableVariationsBase(std::vector<std::string> &&keys, std::vector<std::unique_ptr<RMergeableValueBase>> &&values)
601 : fKeys{std::move(keys)}, fValues{std::move(values)}
602 {
603 }
604};
605
606/**
607\class ROOT::Detail::RDF::RMergeableVariations
608\ingroup dataframe
609\brief A container for variation names and variation results that knows how to
610 merge with others of the same type.
611\tparam T Type of the action result.
612*/
613template <typename T>
615
616 template <typename T1, typename... Ts>
617 friend void
618 MergeValues(RMergeableVariations<T1> &OutputMergeable, const RMergeableVariations<Ts> &... InputMergeables);
619
620 /////////////////////////////////////////////////////////////////////////////
621 /// \brief Aggregate the information contained in another RMergeableVariations
622 /// into this.
623 /// \param[in] other The other mergeable.
624 ///
625 /// Iterates over all values of the current object and calls
626 /// ROOT::Detail::RDF::MergeValues to merge with the corresponding value of
627 /// the other object.
628 ///
629 /// \note All the `Merge` methods in the RMergeableValue family are private.
630 /// To merge multiple RMergeableValue objects please use ROOT::Detail::RDF::MergeValues
632 {
633 R__ASSERT(fKeys == other.fKeys && "Mergeable variations have different names.");
634
635 for (std::size_t i = 0; i < fValues.size(); i++) {
636 // Cast to concrete types according to MergeValues signature
637 MergeValues(static_cast<RMergeableValue<T> &>(*fValues[i]),
638 static_cast<const RMergeableValue<T> &>(*other.fValues[i]));
639 }
640 }
641
642public:
643 /**
644 Default constructor. Needed to allow serialization of ROOT objects. See
645 [TBufferFile::WriteObjectClass]
646 (classTBufferFile.html#a209078a4cb58373b627390790bf0c9c1)
647 */
653
654 /////////////////////////////////////////////////////////////////////////////
655 /// \brief Constructor that initializes data members.
656 /// \param[in] base The container of the names and values.
657 ///
658 /// The variation names and values are moved from the base container into this.
660
661 /////////////////////////////////////////////////////////////////////////////
662 /// \brief Get the list of variation names.
663 const std::vector<std::string> &GetKeys() const { return fKeys; }
664 /////////////////////////////////////////////////////////////////////////////
665 /// \brief Get the final value from the mergeable corresponding to a certain
666 /// variation name.
667 /// \param[in] variationName The name.
668 ///
669 /// The variation name is used to retrieve the corresponding RMergeableValue
670 /// contained in this object. From that, the actual value is retrieved by
671 /// calling the ROOT::Detail::RDF::RMergeableValue::GetValue function.
672 const T &GetVariation(const std::string &variationName) const
673 {
674 auto it = std::find(std::begin(fKeys), std::end(fKeys), variationName);
675 if (it == std::end(fKeys)) {
676 throw std::runtime_error("RMergeableVariations: no result with key \"" + variationName + "\".");
677 } else {
678 auto pos = std::distance(std::begin(fKeys), it);
679 return static_cast<const RMergeableValue<T> &>(*fValues[pos]).GetValue();
680 }
681 }
682};
683
684/// \cond HIDDEN_SYMBOLS
685// What follows mimics C++17 std::conjunction without using recursive template instantiations.
686// Used in `MergeValues` to check that all the mergeables hold values of the same type.
687template <bool...>
688struct bool_pack {
689};
690template <class... Ts>
691using conjunction = std::is_same<bool_pack<true, Ts::value...>, bool_pack<Ts::value..., true>>;
692/// \endcond
693
694////////////////////////////////////////////////////////////////////////////////
695/// \brief Merge multiple RMergeableValue objects into one.
696/// \param[in] OutputMergeable The mergeable object where all the information
697/// will be aggregated.
698/// \param[in] InputMergeables Other mergeables containing the partial results.
699/// \returns An RMergeableValue holding the aggregated value wrapped in an
700/// `std::unique_ptr`.
701///
702/// This is the recommended way of merging multiple RMergeableValue objects.
703/// This overload takes ownership of the mergeables and gives back to the user
704/// a mergeable with the aggregated information. All the mergeables with the
705/// partial results get destroyed in the process.
706///
707/// Example usage:
708/// ~~~{.cpp}
709/// using namespace ROOT::Detail::RDF;
710/// // mh1, mh2, mh3 are std::unique_ptr<RMergeableValue<TH1D>>
711/// auto mergedptr = MergeValues(std::move(mh1), std::move(mh2), std::move(mh3));
712/// const auto &mergedhisto = mergedptr->GetValue(); // Final merged histogram
713/// // Do stuff with it
714/// mergedhisto.Draw();
715/// ~~~
716template <typename T, typename... Ts>
717std::unique_ptr<RMergeableValue<T>> MergeValues(std::unique_ptr<RMergeableValue<T>> OutputMergeable,
718 std::unique_ptr<RMergeableValue<Ts>>... InputMergeables)
719{
720 // Check all mergeables have the same template type
721 static_assert(conjunction<std::is_same<Ts, T>...>::value, "Values must all be of the same type.");
722
723 // Using dummy array initialization inspired by https://stackoverflow.com/a/25683817
724 using expander = int[];
725 // Cast to void to suppress unused-value warning in Clang
726 (void)expander{0, (OutputMergeable->Merge(*InputMergeables), 0)...};
727
728 return OutputMergeable;
729}
730
731////////////////////////////////////////////////////////////////////////////////
732/// \brief Merge multiple RMergeableValue objects into one.
733/// \param[in,out] OutputMergeable The mergeable object where all the
734/// information will be aggregated.
735/// \param[in] InputMergeables Other mergeables containing the partial results.
736///
737/// This overload modifies the mergeable objects in-place. The ownership is left
738/// to the caller. The first argument to the function will get all the
739/// values contained in the other arguments merged into itself. This is a
740/// convenience overload introduced for the ROOT Python API.
741///
742/// Example usage:
743/// ~~~{.cpp}
744/// // mh1, mh2, mh3 are std::unique_ptr<RMergeableValue<TH1D>>
745/// ROOT::Detail::RDF::MergeValues(*mh1, *mh2, *mh3);
746/// const auto &mergedhisto = mh1->GetValue(); // Final merged histogram
747/// // Do stuff with it
748/// mergedhisto.Draw();
749/// ~~~
750template <typename T, typename... Ts>
751void MergeValues(RMergeableValue<T> &OutputMergeable, const RMergeableValue<Ts> &... InputMergeables)
752{
753 // Check all mergeables are of the same type
754 static_assert(conjunction<std::is_same<Ts, T>...>::value, "Values must all be of the same type.");
755
756 // Using dummy array initialization inspired by https://stackoverflow.com/a/25683817
757 using expander = int[];
758 // Cast to void to suppress unused-value warning in Clang
759 (void)expander{0, (OutputMergeable.Merge(InputMergeables), 0)...};
760}
761
762////////////////////////////////////////////////////////////////////////////////
763/// \brief Merge multiple RMergeableVariations objects into one.
764/// \param[in,out] OutputMergeable The mergeable object where all the
765/// information will be aggregated.
766/// \param[in] InputMergeables Other mergeables containing the partial results.
767///
768/// This overload modifies the mergeable objects in-place. The ownership is left
769/// to the caller. The first argument to the function will get all the
770/// values contained in the other arguments merged into itself. This is a
771/// convenience overload introduced for the ROOT Python API.
772///
773/// Example usage:
774/// ~~~{.cpp}
775/// // mv1, mv2 are std::unique_ptr<RMergeableVariations<TH1D>>
776/// ROOT::Detail::RDF::MergeValues(*mv1, *mv2);
777/// const auto &keys = mv1->GetKeys(); // Names of the variations
778/// // Do stuff with the variations
779/// for(const auto &key: keys){
780/// const auto &histo = mv1->GetVariation(key); // Varied histogram
781/// std::cout << histo.GetEntries() << "\n";
782/// }
783/// ~~~
784template <typename T, typename... Ts>
785void MergeValues(RMergeableVariations<T> &OutputMergeable, const RMergeableVariations<Ts> &... InputMergeables)
786{
787 // Check all mergeables are of the same type
788 static_assert(conjunction<std::is_same<Ts, T>...>::value, "Values must all be of the same type.");
789
790 // Using dummy array initialization inspired by https://stackoverflow.com/a/25683817
791 using expander = int[];
792 // Cast to void to suppress unused-value warning in Clang
793 (void)expander{0, (OutputMergeable.Merge(InputMergeables), 0)...};
794}
795} // namespace RDF
796} // namespace Detail
797} // namespace ROOT
798
799#endif // ROOT_RDF_RMERGEABLEVALUE
unsigned long long ULong64_t
Definition: RtypesCore.h:81
#define R__ASSERT(e)
Definition: TError.h:118
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Specialization of RMergeableValue for the Count action.
RMergeableCount(const RMergeableCount &)=delete
void Merge(const RMergeableValue< ULong64_t > &other) final
Aggregate the information contained in another RMergeableValue into this.
RMergeableCount()=default
Default constructor.
RMergeableCount(ULong64_t value)
Constructor that initializes data members.
RMergeableCount & operator=(RMergeableCount &&)=delete
RMergeableCount(RMergeableCount &&)=delete
RMergeableCount & operator=(const RMergeableCount &)=delete
Specialization of RMergeableValue for histograms and statistics.
auto DoMerge(const RMergeableFill< U > &other, double) -> decltype(this->fValue.Merge(std::vector< U * >{}), void())
auto DoMerge(const RMergeableFill< U > &other, int) -> decltype(((U &) this->fValue).Merge((TCollection *) nullptr), void())
RMergeableFill()=default
Default constructor.
RMergeableFill(RMergeableFill &&)=delete
RMergeableFill(const T &value)
Constructor that initializes data members.
RMergeableFill & operator=(RMergeableFill &&)=delete
void Merge(const RMergeableValue< T > &other) final
Aggregate the information contained in another RMergeableValue into this.
RMergeableFill & operator=(const RMergeableFill &)=delete
RMergeableFill(const RMergeableFill &)=delete
RMergeableMax(const T &value)
Constructor that initializes data members.
void Merge(const RMergeableValue< T > &other) final
Aggregate the information contained in another RMergeableValue into this.
RMergeableMax()=default
Default constructor.
RMergeableMax & operator=(const RMergeableMax &)=delete
RMergeableMax(RMergeableMax &&)=delete
RMergeableMax & operator=(RMergeableMax &&)=delete
RMergeableMax(const RMergeableMax &)=delete
Specialization of RMergeableValue for the Mean action.
RMergeableMean(Double_t value, ULong64_t counts)
Constructor that initializes data members.
RMergeableMean(const RMergeableMean &)=delete
ULong64_t fCounts
The number of entries used to compute the mean.
RMergeableMean()=default
Default constructor.
RMergeableMean & operator=(const RMergeableMean &)=delete
void Merge(const RMergeableValue< Double_t > &other) final
Aggregate the information contained in another RMergeableValue into this.
RMergeableMean(RMergeableMean &&)=delete
RMergeableMean & operator=(RMergeableMean &&)=delete
RMergeableMin()=default
Default constructor.
RMergeableMin(RMergeableMin &&)=delete
RMergeableMin(const RMergeableMin &)=delete
RMergeableMin(const T &value)
Constructor that initializes data members.
void Merge(const RMergeableValue< T > &other) final
Aggregate the information contained in another RMergeableValue into this.
RMergeableMin & operator=(const RMergeableMin &)=delete
RMergeableMin & operator=(RMergeableMin &&)=delete
Specialization of RMergeableValue for the StdDev action.
RMergeableStdDev(Double_t value, ULong64_t counts, Double_t mean)
Constructor that initializes data members.
void Merge(const RMergeableValue< Double_t > &other) final
Aggregate the information contained in another RMergeableValue into this.
RMergeableStdDev(const RMergeableStdDev &)=delete
RMergeableStdDev & operator=(RMergeableStdDev &&)=delete
ULong64_t fCounts
Number of entries of the set.
RMergeableStdDev(RMergeableStdDev &&)=delete
Double_t fMean
Average of the set.
RMergeableStdDev & operator=(const RMergeableStdDev &)=delete
RMergeableStdDev()=default
Default constructor.
RMergeableSum()=default
Default constructor.
RMergeableSum(const T &value)
Constructor that initializes data members.
RMergeableSum(const RMergeableSum &)=delete
RMergeableSum & operator=(RMergeableSum &&)=delete
RMergeableSum(RMergeableSum &&)=delete
void Merge(const RMergeableValue< T > &other) final
Aggregate the information contained in another RMergeableValue into this.
RMergeableSum & operator=(const RMergeableSum &)=delete
Base class of RMergeableValue.
RMergeableValueBase()=default
Default constructor.
RMergeableValueBase(RMergeableValueBase &&)=delete
RMergeableValueBase & operator=(RMergeableValueBase &&)=delete
RMergeableValueBase & operator=(const RMergeableValueBase &)=delete
RMergeableValueBase(const RMergeableValueBase &)=delete
A result of an RDataFrame execution, that knows how to merge with other results of the same type.
RMergeableValue()=default
Default constructor.
RMergeableValue(const RMergeableValue &)=delete
RMergeableValue(RMergeableValue &&)=delete
const T & GetValue() const
Retrieve the result wrapped by this mergeable.
RMergeableValue & operator=(const RMergeableValue &)=delete
friend void MergeValues(RMergeableValue< T1 > &OutputMergeable, const RMergeableValue< Ts > &... InputMergeables)
friend std::unique_ptr< RMergeableValue< T1 > > MergeValues(std::unique_ptr< RMergeableValue< T1 > > OutputMergeable, std::unique_ptr< RMergeableValue< Ts > >... InputMergeables)
RMergeableValue(const T &value)
Constructor taking the action result by const reference.
virtual void Merge(const RMergeableValue< T > &)=0
Aggregate the information contained in another RMergeableValue into this.
RMergeableValue & operator=(RMergeableValue &&)=delete
A container for variation names and variation results.
RMergeableVariationsBase()=default
Default constructor.
RMergeableVariationsBase & operator=(RMergeableVariationsBase &&)=delete
std::vector< std::unique_ptr< RMergeableValueBase > > fValues
RMergeableVariationsBase(std::vector< std::string > &&keys, std::vector< std::unique_ptr< RMergeableValueBase > > &&values)
Constructor that initializes data members.
RMergeableVariationsBase & operator=(const RMergeableVariationsBase &)=delete
RMergeableVariationsBase(const RMergeableVariationsBase &)=delete
RMergeableVariationsBase(RMergeableVariationsBase &&other)
Constructor that moves the data members from the input object.
A container for variation names and variation results that knows how to merge with others of the same...
void Merge(const RMergeableVariations< T > &other)
Aggregate the information contained in another RMergeableVariations into this.
const T & GetVariation(const std::string &variationName) const
Get the final value from the mergeable corresponding to a certain variation name.
RMergeableVariations & operator=(const RMergeableVariations &)=delete
RMergeableVariations()=default
Default constructor.
RMergeableVariations & operator=(RMergeableVariations &&)=delete
const std::vector< std::string > & GetKeys() const
Get the list of variation names.
RMergeableVariations(RMergeableVariations &&)=delete
RMergeableVariations(RMergeableVariationsBase &&base)
Constructor that initializes data members.
friend void MergeValues(RMergeableVariations< T1 > &OutputMergeable, const RMergeableVariations< Ts > &... InputMergeables)
RMergeableVariations(const RMergeableVariations &)=delete
Collection abstract base class.
Definition: TCollection.h:65
A doubly linked list.
Definition: TList.h:38
RVec< PromoteTypes< T0, T1 > > pow(const T0 &x, const RVec< T1 > &v)
Definition: RVec.hxx:1753
#define T1
Definition: md5.inl:146
std::unique_ptr< RMergeableValue< T > > MergeValues(std::unique_ptr< RMergeableValue< T > > OutputMergeable, std::unique_ptr< RMergeableValue< Ts > >... InputMergeables)
Merge multiple RMergeableValue objects into one.
void(off) SmallVectorTemplateBase< T
double T(double x)
Definition: ChebyshevPol.h:34
VecExpr< UnaryOp< Sqrt< T >, VecExpr< A, T, D >, T >, T, D > sqrt(const VecExpr< A, T, D > &rhs)
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.
auto * l
Definition: textangle.C:4