Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ActionHelpers.hxx
Go to the documentation of this file.
1/**
2 \file ROOT/RDF/ActionHelpers.hxx
3 \ingroup dataframe
4 \author Enrico Guiraud, CERN
5 \author Danilo Piparo, CERN
6 \date 2016-12
7 \author Vincenzo Eduardo Padulano
8 \date 2020-06
9*/
10
11/*************************************************************************
12 * Copyright (C) 1995-2020, Rene Brun and Fons Rademakers. *
13 * All rights reserved. *
14 * *
15 * For the licensing terms see $ROOTSYS/LICENSE. *
16 * For the list of contributors see $ROOTSYS/README/CREDITS. *
17 *************************************************************************/
18
19#ifndef ROOT_RDFOPERATIONS
20#define ROOT_RDFOPERATIONS
21
22#include "ROOT/RVec.hxx"
23#include "ROOT/RDF/Utils.hxx"
24#include "ROOT/TypeTraits.hxx"
25#include "ROOT/RDF/RDisplay.hxx"
26#include "RtypesCore.h"
27#include "TH1.h"
28#include "TH3.h"
29#include "TGraph.h"
30#include "TGraphAsymmErrors.h"
31#include "TObject.h"
34
35#include "RConfigure.h" // for R__HAS_ROOT7
36#ifdef R__HAS_ROOT7
37#include <ROOT/RHist.hxx>
39#include <ROOT/RHistEngine.hxx>
40#include <ROOT/RWeight.hxx>
41#endif
42
43#include <algorithm>
44#include <array>
45#include <limits>
46#include <memory>
47#include <mutex>
48#include <stdexcept>
49#include <string>
50#include <string_view>
51#include <tuple>
52#include <type_traits>
53#include <utility> // std::index_sequence
54#include <vector>
55#include <numeric> // std::accumulate in MeanHelper
56
57class TCollection;
58class TStatistic;
59class TTreeReader;
60namespace ROOT::RDF {
61class RCutFlowReport;
62} // namespace ROOT::RDF
63
64/// \cond HIDDEN_SYMBOLS
65
66namespace ROOT {
67namespace Internal {
68namespace RDF {
69using namespace ROOT::TypeTraits;
70using namespace ROOT::VecOps;
71using namespace ROOT::RDF;
72using namespace ROOT::Detail::RDF;
73
74using Hist_t = ::TH1D;
75
76/// The container type for each thread's partial result in an action helper
77// We have to avoid to instantiate std::vector<bool> as that makes it impossible to return a reference to one of
78// the thread-local results. In addition, a common definition for the type of the container makes it easy to swap
79// the type of the underlying container if e.g. we see problems with false sharing of the thread-local results..
80template <typename T>
81using Results = std::conditional_t<std::is_same<T, bool>::value, std::deque<T>, std::vector<T>>;
82
83template <typename F>
84class R__CLING_PTRCHECK(off) ForeachSlotHelper : public RActionImpl<ForeachSlotHelper<F>> {
85 F fCallable;
86
87public:
91 ForeachSlotHelper(const ForeachSlotHelper &) = delete;
92
93 void InitTask(TTreeReader *, unsigned int) {}
94
95 template <typename... Args>
96 void Exec(unsigned int slot, Args &&... args)
97 {
98 // check that the decayed types of Args are the same as the branch types
99 static_assert(std::is_same<TypeList<std::decay_t<Args>...>, ColumnTypes_t>::value, "");
100 fCallable(slot, std::forward<Args>(args)...);
101 }
102
103 void Initialize() { /* noop */}
104
105 void Finalize() { /* noop */}
106
107 std::string GetActionName() { return "ForeachSlot"; }
108};
109
110class R__CLING_PTRCHECK(off) CountHelper : public RActionImpl<CountHelper> {
111 std::shared_ptr<ULong64_t> fResultCount;
112 Results<ULong64_t> fCounts;
113
114public:
115 using ColumnTypes_t = TypeList<>;
116 CountHelper(const std::shared_ptr<ULong64_t> &resultCount, const unsigned int nSlots);
117 CountHelper(CountHelper &&) = default;
118 CountHelper(const CountHelper &) = delete;
119 void InitTask(TTreeReader *, unsigned int) {}
120 void Exec(unsigned int slot);
121 void Initialize() { /* noop */}
122 void Finalize();
123
124 // Helper functions for RMergeableValue
125 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
126 {
127 return std::make_unique<RMergeableCount>(*fResultCount);
128 }
129
130 ULong64_t &PartialUpdate(unsigned int slot);
131
132 std::string GetActionName() { return "Count"; }
133
134 CountHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
135 {
136 auto &result = *static_cast<std::shared_ptr<ULong64_t> *>(newResult);
137 return CountHelper(result, fCounts.size());
138 }
139};
140
141template <typename RNode_t>
142class R__CLING_PTRCHECK(off) ReportHelper : public RActionImpl<ReportHelper<RNode_t>> {
143 std::shared_ptr<RCutFlowReport> fReport;
144 /// Non-owning pointer, never null. As usual, the node is owned by its children nodes (and therefore indirectly by
145 /// the RAction corresponding to this action helper).
146 RNode_t *fNode;
148
149public:
150 using ColumnTypes_t = TypeList<>;
151 ReportHelper(const std::shared_ptr<RCutFlowReport> &report, RNode_t *node, bool emptyRep)
152 : fReport(report), fNode(node), fReturnEmptyReport(emptyRep){};
153 ReportHelper(ReportHelper &&) = default;
154 ReportHelper(const ReportHelper &) = delete;
155 void InitTask(TTreeReader *, unsigned int) {}
156 void Exec(unsigned int /* slot */) {}
157 void Initialize() { /* noop */}
158 void Finalize()
159 {
161 fNode->Report(*fReport);
162 }
163
164 std::string GetActionName() { return "Report"; }
165
166 ReportHelper MakeNew(void *newResult, std::string_view variation = "nominal")
167 {
168 auto &&result = *static_cast<std::shared_ptr<RCutFlowReport> *>(newResult);
169 return ReportHelper{result,
170 std::static_pointer_cast<RNode_t>(fNode->GetVariedFilter(std::string(variation))).get(),
172 }
173};
174
175/// This helper fills TH1Ds for which no axes were specified by buffering the fill values to pick good axes limits.
176///
177/// TH1Ds have an automatic mechanism to pick good limits based on the first N entries they were filled with, but
178/// that does not work in multi-thread event loops as it might yield histograms with incompatible binning in each
179/// thread, making it impossible to merge the per-thread results.
180/// Instead, this helper delays the decision on the axes limits until all threads have done processing, synchronizing
181/// the decision on the limits as part of the merge operation.
182class R__CLING_PTRCHECK(off) BufferedFillHelper : public RActionImpl<BufferedFillHelper> {
183 // this sets a total initial size of 16 MB for the buffers (can increase)
184 static constexpr unsigned int fgTotalBufSize = 2097152;
185 using BufEl_t = double;
186 using Buf_t = std::vector<BufEl_t>;
187
188 std::vector<Buf_t> fBuffers;
189 std::vector<Buf_t> fWBuffers;
190 std::shared_ptr<Hist_t> fResultHist;
191 unsigned int fNSlots;
192 unsigned int fBufSize;
193 /// Histograms containing "snapshots" of partial results. Non-null only if a registered callback requires it.
195 Buf_t fMin;
196 Buf_t fMax;
197
198 void UpdateMinMax(unsigned int slot, double v);
199
200public:
201 BufferedFillHelper(const std::shared_ptr<Hist_t> &h, const unsigned int nSlots);
203 BufferedFillHelper(const BufferedFillHelper &) = delete;
204 void InitTask(TTreeReader *, unsigned int) {}
205 void Exec(unsigned int slot, double v);
206 void Exec(unsigned int slot, double v, double w);
207
209 void Exec(unsigned int slot, const T &vs)
210 {
211 auto &thisBuf = fBuffers[slot];
212 // range-based for results in warnings on some compilers due to vector<bool>'s custom reference type
213 for (auto v = vs.begin(); v != vs.end(); ++v) {
215 thisBuf.emplace_back(*v); // TODO: Can be optimised in case T == BufEl_t
216 }
217 }
218
220 void Exec(unsigned int slot, const T &vs, const W &ws)
221 {
222 auto &thisBuf = fBuffers[slot];
223
224 for (auto &v : vs) {
226 thisBuf.emplace_back(v);
227 }
228
229 auto &thisWBuf = fWBuffers[slot];
230 for (auto &w : ws) {
231 thisWBuf.emplace_back(w); // TODO: Can be optimised in case T == BufEl_t
232 }
233 }
234
236 void Exec(unsigned int slot, const T &vs, const W w)
237 {
238 auto &thisBuf = fBuffers[slot];
239 for (auto &v : vs) {
241 thisBuf.emplace_back(v); // TODO: Can be optimised in case T == BufEl_t
242 }
243
244 auto &thisWBuf = fWBuffers[slot];
245 thisWBuf.insert(thisWBuf.end(), vs.size(), w);
246 }
247
249 void Exec(unsigned int slot, const T v, const W &ws)
250 {
252 auto &thisBuf = fBuffers[slot];
253 thisBuf.insert(thisBuf.end(), ws.size(), v);
254
255 auto &thisWBuf = fWBuffers[slot];
256 thisWBuf.insert(thisWBuf.end(), ws.begin(), ws.end());
257 }
258
259 Hist_t &PartialUpdate(unsigned int);
260
261 void Initialize() { /* noop */}
262
263 void Finalize();
264
265 // Helper functions for RMergeableValue
266 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
267 {
268 return std::make_unique<RMergeableFill<Hist_t>>(*fResultHist);
269 }
270
271 std::string GetActionName()
272 {
273 return std::string(fResultHist->IsA()->GetName()) + "\\n" + std::string(fResultHist->GetName());
274 }
275
276 BufferedFillHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
277 {
278 auto &result = *static_cast<std::shared_ptr<Hist_t> *>(newResult);
279 result->Reset();
280 result->SetDirectory(nullptr);
281 return BufferedFillHelper(result, fNSlots);
282 }
283};
284
285// class which wraps a pointer and implements a no-op increment operator
286template <typename T>
288 const T *obj_;
289
290public:
291 using iterator_category = std::forward_iterator_tag;
292 using difference_type = std::ptrdiff_t;
293 using value_type = T;
294 using pointer = T *;
295 using reference = T &;
296 ScalarConstIterator(const T *obj) : obj_(obj) {}
297 const T &operator*() const { return *obj_; }
298 ScalarConstIterator<T> &operator++() { return *this; }
299};
300
301// return unchanged value for scalar
302template <typename T>
303auto MakeBegin(const T &val)
304{
305 if constexpr (IsDataContainer<T>::value) {
306 return std::begin(val);
307 } else {
308 return ScalarConstIterator<T>(&val);
309 }
310}
311
312// return container size for containers, and 1 for scalars
313template <typename T>
314std::size_t GetSize(const T &val)
315{
316 if constexpr (IsDataContainer<T>::value) {
317 return std::size(val);
318 } else {
319 return 1;
320 }
321}
322
323// Helpers for dealing with histograms and similar:
325void ResetIfPossible(H *h)
326{
327 h->Reset();
328}
329
331void ResetIfPossible(...);
332
335
336/// The generic Fill helper: it calls Fill on per-thread objects and then Merge to produce a final result.
337/// For one-dimensional histograms, if no axes are specified, RDataFrame uses BufferedFillHelper instead.
338template <typename HIST = Hist_t>
339class R__CLING_PTRCHECK(off) FillHelper : public RActionImpl<FillHelper<HIST>> {
340 std::vector<HIST *> fObjects;
341
342 // Merge overload for types with Merge(TCollection*), like TH1s
344 auto Merge(std::vector<H *> &objs, int /*toincreaseoverloadpriority*/)
345 -> decltype(objs[0]->Merge((TCollection *)nullptr), void())
346 {
347 TList l;
348 for (auto it = ++objs.begin(); it != objs.end(); ++it)
349 l.Add(*it);
350 objs[0]->Merge(&l);
351 }
352
353 // Merge overload for types with Merge(const std::vector&)
354 template <typename H>
355 auto Merge(std::vector<H *> &objs, double /*toloweroverloadpriority*/)
356 -> decltype(objs[0]->Merge(std::vector<HIST *>{}), void())
357 {
358 objs[0]->Merge({++objs.begin(), objs.end()});
359 }
360
361 // Merge overload to error out in case no valid HIST::Merge method was detected
362 template <typename T>
363 void Merge(T, ...)
364 {
365 static_assert(sizeof(T) < 0,
366 "The type passed to Fill does not provide a Merge(TCollection*) or Merge(const std::vector&) method.");
367 }
368
369 template <std::size_t ColIdx, typename End_t, typename... Its>
370 void ExecLoop(unsigned int slot, End_t end, Its... its)
371 {
372 for (auto *thisSlotH = fObjects[slot]; GetNthElement<ColIdx>(its...) != end; (std::advance(its, 1), ...)) {
373 thisSlotH->Fill(*its...);
374 }
375 }
376
377public:
378 FillHelper(FillHelper &&) = default;
379 FillHelper(const FillHelper &) = delete;
380
381 FillHelper(const std::shared_ptr<HIST> &h, const unsigned int nSlots) : fObjects(nSlots, nullptr)
382 {
383 fObjects[0] = h.get();
384 // Initialize all other slots
385 for (unsigned int i = 1; i < nSlots; ++i) {
386 fObjects[i] = new HIST(*fObjects[0]);
387 UnsetDirectoryIfPossible(fObjects[i]);
388 }
389 }
390
391 void InitTask(TTreeReader *, unsigned int) {}
392
393 // no container arguments
394 template <typename... ValTypes, std::enable_if_t<!Disjunction<IsDataContainer<ValTypes>...>::value, int> = 0>
395 auto Exec(unsigned int slot, const ValTypes &...x) -> decltype(fObjects[slot]->Fill(x...), void())
396 {
397 fObjects[slot]->Fill(x...);
398 }
399
400 // at least one container argument
401 template <typename... Xs, std::enable_if_t<Disjunction<IsDataContainer<Xs>...>::value, int> = 0>
402 auto Exec(unsigned int slot, const Xs &...xs) -> decltype(fObjects[slot]->Fill(*MakeBegin(xs)...), void())
403 {
404 // array of bools keeping track of which inputs are containers
405 constexpr std::array<bool, sizeof...(Xs)> isContainer{IsDataContainer<Xs>::value...};
406
407 // index of the first container input
408 constexpr std::size_t colidx = FindIdxTrue(isContainer);
409 // if this happens, there is a bug in the implementation
410 static_assert(colidx < sizeof...(Xs), "Error: index of collection-type argument not found.");
411
412 // get the end iterator to the first container
413 auto const xrefend = std::end(GetNthElement<colidx>(xs...));
414
415 // array of container sizes (1 for scalars)
416 std::array<std::size_t, sizeof...(xs)> sizes = {{GetSize(xs)...}};
417
418 for (std::size_t i = 0; i < sizeof...(xs); ++i) {
419 if (isContainer[i] && sizes[i] != sizes[colidx]) {
420 throw std::runtime_error("Cannot fill histogram with values in containers of different sizes.");
421 }
422 }
423
425 }
426
427 template <typename T = HIST>
428 void Exec(...)
429 {
430 static_assert(sizeof(T) < 0,
431 "When filling an object with RDataFrame (e.g. via a Fill action) the number or types of the "
432 "columns passed did not match the signature of the object's `Fill` method.");
433 }
434
435 void Initialize() { /* noop */}
436
437 void Finalize()
438 {
439 if (fObjects.size() == 1)
440 return;
441
442 Merge(fObjects, /*toselectcorrectoverload=*/0);
443
444 // delete the copies we created for the slots other than the first
445 for (auto it = ++fObjects.begin(); it != fObjects.end(); ++it)
446 delete *it;
447 }
448
449 HIST &PartialUpdate(unsigned int slot) { return *fObjects[slot]; }
450
451 // Helper functions for RMergeableValue
452 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
453 {
454 return std::make_unique<RMergeableFill<HIST>>(*fObjects[0]);
455 }
456
457 // if the fObjects vector type is derived from TObject, return the name of the object
459 std::string GetActionName()
460 {
461 return std::string(fObjects[0]->IsA()->GetName()) + "\\n" + std::string(fObjects[0]->GetName());
462 }
463
464 // if fObjects is not derived from TObject, indicate it is some other object
466 std::string GetActionName()
467 {
468 return "Fill custom object";
469 }
470
471 template <typename H = HIST>
472 FillHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
473 {
474 auto &result = *static_cast<std::shared_ptr<H> *>(newResult);
475 ResetIfPossible(result.get());
477 return FillHelper(result, fObjects.size());
478 }
479};
480
481#ifdef R__HAS_ROOT7
482template <typename BinContentType, bool WithWeight = false>
484 : public ROOT::Detail::RDF::RActionImpl<RHistFillHelper<BinContentType, WithWeight>> {
485public:
487
488private:
489 std::unique_ptr<ROOT::Experimental::RHistConcurrentFiller<BinContentType>> fFiller;
490 std::vector<std::shared_ptr<ROOT::Experimental::RHistFillContext<BinContentType>>> fContexts;
491
492public:
494 : fFiller(new ROOT::Experimental::RHistConcurrentFiller<BinContentType>(h)), fContexts(nSlots)
495 {
496 for (unsigned int i = 0; i < nSlots; i++) {
497 fContexts[i] = fFiller->CreateFillContext();
498 }
499 }
500 RHistFillHelper(const RHistFillHelper &) = delete;
501 RHistFillHelper(RHistFillHelper &&) = default;
502 RHistFillHelper &operator=(const RHistFillHelper &) = delete;
504 ~RHistFillHelper() = default;
505
506 std::shared_ptr<Result_t> GetResultPtr() const { return fFiller.GetHist(); }
507
508 void Initialize() {}
509 void InitTask(TTreeReader *, unsigned int) {}
510
511 template <typename... ColumnTypes, const std::size_t... I>
512 void
513 ExecWithWeight(unsigned int slot, const std::tuple<const ColumnTypes &...> &columnValues, std::index_sequence<I...>)
514 {
515 // Build a tuple of const references with the actual arguments, stripping the weight and avoiding copies.
516 std::tuple<const std::tuple_element_t<I, std::tuple<ColumnTypes...>> &...> args(std::get<I>(columnValues)...);
517 ROOT::Experimental::RWeight weight(std::get<sizeof...(ColumnTypes) - 1>(columnValues));
518 fContexts[slot]->Fill(args, weight);
519 }
520
521 template <typename... ColumnTypes>
522 void Exec(unsigned int slot, const ColumnTypes &...columnValues)
523 {
524 if constexpr (WithWeight) {
525 auto t = std::forward_as_tuple(columnValues...);
526 ExecWithWeight(slot, t, std::make_index_sequence<sizeof...(ColumnTypes) - 1>());
527 } else {
528 fContexts[slot]->Fill(columnValues...);
529 }
530 }
531
532 void Finalize()
533 {
534 for (auto &&context : fContexts) {
535 context->Flush();
536 }
537 }
538
539 std::string GetActionName() { return "Hist"; }
540};
541
542template <typename BinContentType, bool WithWeight = false>
544 : public ROOT::Detail::RDF::RActionImpl<RHistEngineFillHelper<BinContentType, WithWeight>> {
545public:
547
548private:
549 std::shared_ptr<Result_t> fHist;
550
551public:
557 ~RHistEngineFillHelper() = default;
558
559 std::shared_ptr<Result_t> GetResultPtr() const { return fHist; }
560
561 void Initialize() {}
562 void InitTask(TTreeReader *, unsigned int) {}
563
564 template <typename... ColumnTypes, const std::size_t... I>
565 void ExecWithWeight(const std::tuple<const ColumnTypes &...> &columnValues, std::index_sequence<I...>)
566 {
567 // Build a tuple of const references with the actual arguments, stripping the weight and avoiding copies.
568 std::tuple<const std::tuple_element_t<I, std::tuple<ColumnTypes...>> &...> args(std::get<I>(columnValues)...);
569 ROOT::Experimental::RWeight weight(std::get<sizeof...(ColumnTypes) - 1>(columnValues));
570 fHist->FillAtomic(args, weight);
571 }
572
573 template <typename... ColumnTypes>
574 void Exec(unsigned int, const ColumnTypes &...columnValues)
575 {
576 if constexpr (WithWeight) {
577 auto t = std::forward_as_tuple(columnValues...);
578 ExecWithWeight(t, std::make_index_sequence<sizeof...(ColumnTypes) - 1>());
579 } else {
580 fHist->FillAtomic(columnValues...);
581 }
582 }
583
584 void Finalize() {}
585
586 std::string GetActionName() { return "Hist"; }
587};
588#endif
589
591public:
592 using Result_t = ::TGraph;
593
594private:
595 std::vector<::TGraph *> fGraphs;
596
597public:
599 FillTGraphHelper(const FillTGraphHelper &) = delete;
600
601 FillTGraphHelper(const std::shared_ptr<::TGraph> &g, const unsigned int nSlots) : fGraphs(nSlots, nullptr)
602 {
603 fGraphs[0] = g.get();
604 // Initialize all other slots
605 for (unsigned int i = 1; i < nSlots; ++i) {
606 fGraphs[i] = new TGraph(*fGraphs[0]);
607 }
608 }
609
610 void Initialize() {}
611 void InitTask(TTreeReader *, unsigned int) {}
612
613 // case: both types are container types
614 template <typename X0, typename X1,
615 std::enable_if_t<IsDataContainer<X0>::value && IsDataContainer<X1>::value, int> = 0>
616 void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s)
617 {
618 if (x0s.size() != x1s.size()) {
619 throw std::runtime_error("Cannot fill Graph with values in containers of different sizes.");
620 }
621 auto *thisSlotG = fGraphs[slot];
622 auto x0sIt = std::begin(x0s);
623 const auto x0sEnd = std::end(x0s);
624 auto x1sIt = std::begin(x1s);
625 for (; x0sIt != x0sEnd; x0sIt++, x1sIt++) {
626 thisSlotG->SetPoint(thisSlotG->GetN(), *x0sIt, *x1sIt);
627 }
628 }
629
630 // case: both types are non-container types, e.g. scalars
631 template <typename X0, typename X1,
632 std::enable_if_t<!IsDataContainer<X0>::value && !IsDataContainer<X1>::value, int> = 0>
633 void Exec(unsigned int slot, X0 x0, X1 x1)
634 {
635 auto thisSlotG = fGraphs[slot];
636 thisSlotG->SetPoint(thisSlotG->GetN(), x0, x1);
637 }
638
639 // case: types are combination of containers and non-containers
640 // this is not supported, error out
641 template <typename X0, typename X1, typename... ExtraArgsToLowerPriority>
642 void Exec(unsigned int, X0, X1, ExtraArgsToLowerPriority...)
643 {
644 throw std::runtime_error("Graph was applied to a mix of scalar values and collections. This is not supported.");
645 }
646
647 void Finalize()
648 {
649 const auto nSlots = fGraphs.size();
650 auto resGraph = fGraphs[0];
651 TList l;
652 l.SetOwner(); // The list will free the memory associated to its elements upon destruction
653 for (unsigned int slot = 1; slot < nSlots; ++slot) {
654 l.Add(fGraphs[slot]);
655 }
656 resGraph->Merge(&l);
657 }
658
659 // Helper functions for RMergeableValue
660 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
661 {
662 return std::make_unique<RMergeableFill<Result_t>>(*fGraphs[0]);
663 }
664
665 std::string GetActionName() { return "Graph"; }
666
667 Result_t &PartialUpdate(unsigned int slot) { return *fGraphs[slot]; }
668
669 FillTGraphHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
670 {
671 auto &result = *static_cast<std::shared_ptr<TGraph> *>(newResult);
672 result->Set(0);
673 return FillTGraphHelper(result, fGraphs.size());
674 }
675};
676
678 : public ROOT::Detail::RDF::RActionImpl<FillTGraphAsymmErrorsHelper> {
679public:
680 using Result_t = ::TGraphAsymmErrors;
681
682private:
683 std::vector<::TGraphAsymmErrors *> fGraphAsymmErrors;
684
685public:
688
689 FillTGraphAsymmErrorsHelper(const std::shared_ptr<::TGraphAsymmErrors> &g, const unsigned int nSlots)
690 : fGraphAsymmErrors(nSlots, nullptr)
691 {
692 fGraphAsymmErrors[0] = g.get();
693 // Initialize all other slots
694 for (unsigned int i = 1; i < nSlots; ++i) {
696 }
697 }
698
699 void Initialize() {}
700 void InitTask(TTreeReader *, unsigned int) {}
701
702 // case: all types are container types
703 template <
704 typename X, typename Y, typename EXL, typename EXH, typename EYL, typename EYH,
705 std::enable_if_t<IsDataContainer<X>::value && IsDataContainer<Y>::value && IsDataContainer<EXL>::value &&
706 IsDataContainer<EXH>::value && IsDataContainer<EYL>::value && IsDataContainer<EYH>::value,
707 int> = 0>
708 void
709 Exec(unsigned int slot, const X &xs, const Y &ys, const EXL &exls, const EXH &exhs, const EYL &eyls, const EYH &eyhs)
710 {
711 if ((xs.size() != ys.size()) || (xs.size() != exls.size()) || (xs.size() != exhs.size()) ||
712 (xs.size() != eyls.size()) || (xs.size() != eyhs.size())) {
713 throw std::runtime_error("Cannot fill GraphAsymmErrors with values in containers of different sizes.");
714 }
716 auto xsIt = std::begin(xs);
717 auto ysIt = std::begin(ys);
718 auto exlsIt = std::begin(exls);
719 auto exhsIt = std::begin(exhs);
720 auto eylsIt = std::begin(eyls);
721 auto eyhsIt = std::begin(eyhs);
722 while (xsIt != std::end(xs)) {
723 const auto n = thisSlotG->GetN(); // must use the same `n` for SetPoint and SetPointError
724 thisSlotG->SetPoint(n, *xsIt++, *ysIt++);
725 thisSlotG->SetPointError(n, *exlsIt++, *exhsIt++, *eylsIt++, *eyhsIt++);
726 }
727 }
728
729 // case: all types are non-container types, e.g. scalars
730 template <
731 typename X, typename Y, typename EXL, typename EXH, typename EYL, typename EYH,
732 std::enable_if_t<!IsDataContainer<X>::value && !IsDataContainer<Y>::value && !IsDataContainer<EXL>::value &&
733 !IsDataContainer<EXH>::value && !IsDataContainer<EYL>::value && !IsDataContainer<EYH>::value,
734 int> = 0>
735 void Exec(unsigned int slot, X x, Y y, EXL exl, EXH exh, EYL eyl, EYH eyh)
736 {
738 const auto n = thisSlotG->GetN();
739 thisSlotG->SetPoint(n, x, y);
740 thisSlotG->SetPointError(n, exl, exh, eyl, eyh);
741 }
742
743 // case: types are combination of containers and non-containers
744 // this is not supported, error out
745 template <typename X, typename Y, typename EXL, typename EXH, typename EYL, typename EYH,
746 typename... ExtraArgsToLowerPriority>
747 void Exec(unsigned int, X, Y, EXL, EXH, EYL, EYH, ExtraArgsToLowerPriority...)
748 {
749 throw std::runtime_error(
750 "GraphAsymmErrors was applied to a mix of scalar values and collections. This is not supported.");
751 }
752
753 void Finalize()
754 {
755 const auto nSlots = fGraphAsymmErrors.size();
757 TList l;
758 l.SetOwner(); // The list will free the memory associated to its elements upon destruction
759 for (unsigned int slot = 1; slot < nSlots; ++slot) {
761 }
762 resGraphAsymmErrors->Merge(&l);
763 }
764
765 // Helper functions for RMergeableValue
766 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
767 {
768 return std::make_unique<RMergeableFill<Result_t>>(*fGraphAsymmErrors[0]);
769 }
770
771 std::string GetActionName() { return "GraphAsymmErrors"; }
772
773 Result_t &PartialUpdate(unsigned int slot) { return *fGraphAsymmErrors[slot]; }
774
775 FillTGraphAsymmErrorsHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
776 {
777 auto &result = *static_cast<std::shared_ptr<TGraphAsymmErrors> *>(newResult);
778 result->Set(0);
780 }
781};
782
783/// A FillHelper for classes supporting the FillThreadSafe function.
784template <typename HIST>
785class R__CLING_PTRCHECK(off) ThreadSafeFillHelper : public RActionImpl<ThreadSafeFillHelper<HIST>> {
786 std::vector<std::shared_ptr<HIST>> fObjects;
787 std::vector<std::unique_ptr<std::mutex>> fMutexPtrs;
788
789 // This overload matches if the function exists:
790 template <typename T, typename... Args>
791 auto TryCallFillThreadSafe(T &object, std::mutex &, int /*dummy*/, Args... args)
792 -> decltype(ROOT::Internal::FillThreadSafe(object, args...), void())
793 {
794 ROOT::Internal::FillThreadSafe(object, args...);
795 }
796 // This one has lower precedence because of the dummy argument, and uses a lock
797 template <typename T, typename... Args>
798 auto TryCallFillThreadSafe(T &object, std::mutex &mutex, char /*dummy*/, Args... args)
799 {
800 std::scoped_lock lock{mutex};
801 object.Fill(args...);
802 }
803
804 template <std::size_t ColIdx, typename End_t, typename... Its>
805 void ExecLoop(unsigned int slot, End_t end, Its... its)
806 {
807 const auto localSlot = slot % fObjects.size();
808 for (; GetNthElement<ColIdx>(its...) != end; (std::advance(its, 1), ...)) {
810 }
811 }
812
813public:
816
817 ThreadSafeFillHelper(const std::shared_ptr<HIST> &h, const unsigned int nSlots)
818 {
819 fObjects.resize(nSlots);
820 fObjects.front() = h;
821
822 std::generate(fObjects.begin() + 1, fObjects.end(), [h]() {
823 auto hist = std::make_shared<HIST>(*h);
824 UnsetDirectoryIfPossible(hist.get());
825 return hist;
826 });
827 fMutexPtrs.resize(nSlots);
828 std::generate(fMutexPtrs.begin(), fMutexPtrs.end(), []() { return std::make_unique<std::mutex>(); });
829 }
830
831 void InitTask(TTreeReader *, unsigned int) {}
832
833 // no container arguments
834 template <typename... ValTypes, std::enable_if_t<!Disjunction<IsDataContainer<ValTypes>...>::value, int> = 0>
835 void Exec(unsigned int slot, const ValTypes &...x)
836 {
837 const auto localSlot = slot % fObjects.size();
839 }
840
841 // at least one container argument
842 template <typename... Xs, std::enable_if_t<Disjunction<IsDataContainer<Xs>...>::value, int> = 0>
843 void Exec(unsigned int slot, const Xs &...xs)
844 {
845 // array of bools keeping track of which inputs are containers
846 constexpr std::array<bool, sizeof...(Xs)> isContainer{IsDataContainer<Xs>::value...};
847
848 // index of the first container input
849 constexpr std::size_t colidx = FindIdxTrue(isContainer);
850 // if this happens, there is a bug in the implementation
851 static_assert(colidx < sizeof...(Xs), "Error: index of collection-type argument not found.");
852
853 // get the end iterator to the first container
854 auto const xrefend = std::end(GetNthElement<colidx>(xs...));
855
856 // array of container sizes (1 for scalars)
857 std::array<std::size_t, sizeof...(xs)> sizes = {{GetSize(xs)...}};
858
859 for (std::size_t i = 0; i < sizeof...(xs); ++i) {
860 if (isContainer[i] && sizes[i] != sizes[colidx]) {
861 throw std::runtime_error("Cannot fill histogram with values in containers of different sizes.");
862 }
863 }
864
866 }
867
868 template <typename T = HIST>
869 void Exec(...)
870 {
871 static_assert(sizeof(T) < 0,
872 "When filling an object with RDataFrame (e.g. via a Fill action) the number or types of the "
873 "columns passed did not match the signature of the object's `FillThreadSafe` method.");
874 }
875
876 void Initialize() { /* noop */ }
877
878 void Finalize()
879 {
880 if (fObjects.size() > 1) {
881 TList list;
882 for (auto it = fObjects.cbegin() + 1; it != fObjects.end(); ++it) {
883 list.Add(it->get());
884 }
885 fObjects[0]->Merge(&list);
886 }
887
888 fObjects.resize(1);
889 fMutexPtrs.clear();
890 }
891
892 // Helper function for RMergeableValue
893 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
894 {
895 return std::make_unique<RMergeableFill<HIST>>(*fObjects[0]);
896 }
897
898 // if the fObjects vector type is derived from TObject, return the name of the object
900 std::string GetActionName()
901 {
902 return std::string(fObjects[0]->IsA()->GetName()) + "\\n" + std::string(fObjects[0]->GetName());
903 }
904
905 template <typename H = HIST>
906 ThreadSafeFillHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
907 {
908 auto &result = *static_cast<std::shared_ptr<H> *>(newResult);
909 ResetIfPossible(result.get());
911 return ThreadSafeFillHelper(result, fObjects.size());
912 }
913};
914
915// In case of the take helper we have 4 cases:
916// 1. The column is not an RVec, the collection is not a vector
917// 2. The column is not an RVec, the collection is a vector
918// 3. The column is an RVec, the collection is not a vector
919// 4. The column is an RVec, the collection is a vector
920
921template <typename V, typename COLL>
922void FillColl(V&& v, COLL& c) {
923 c.emplace_back(v);
924}
925
926// Use push_back for bool since some compilers do not support emplace_back.
927template <typename COLL>
928void FillColl(bool v, COLL& c) {
929 c.push_back(v);
930}
931
932// Case 1.: The column is not an RVec, the collection is not a vector
933// No optimisations, no transformations: just copies.
934template <typename RealT_t, typename T, typename COLL>
935class R__CLING_PTRCHECK(off) TakeHelper : public RActionImpl<TakeHelper<RealT_t, T, COLL>> {
937
938public:
939 using ColumnTypes_t = TypeList<T>;
940 TakeHelper(const std::shared_ptr<COLL> &resultColl, const unsigned int nSlots)
941 {
942 fColls.emplace_back(resultColl);
943 for (unsigned int i = 1; i < nSlots; ++i)
944 fColls.emplace_back(std::make_shared<COLL>());
945 }
947 TakeHelper(const TakeHelper &) = delete;
948
949 void InitTask(TTreeReader *, unsigned int) {}
950
951 void Exec(unsigned int slot, T &v) { FillColl(v, *fColls[slot]); }
952
953 void Initialize() { /* noop */}
954
955 void Finalize()
956 {
957 auto rColl = fColls[0];
958 for (unsigned int i = 1; i < fColls.size(); ++i) {
959 const auto &coll = fColls[i];
960 const auto end = coll->end();
961 // Use an explicit loop here to prevent compiler warnings introduced by
962 // clang's range-based loop analysis and vector<bool> references.
963 for (auto j = coll->begin(); j != end; j++) {
964 FillColl(*j, *rColl);
965 }
966 }
967 }
968
969 COLL &PartialUpdate(unsigned int slot) { return *fColls[slot].get(); }
970
971 std::string GetActionName() { return "Take"; }
972
973 TakeHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
974 {
975 auto &result = *static_cast<std::shared_ptr<COLL> *>(newResult);
976 result->clear();
977 return TakeHelper(result, fColls.size());
978 }
979};
980
981// Case 2.: The column is not an RVec, the collection is a vector
982// Optimisations, no transformations: just copies.
983template <typename RealT_t, typename T>
984class R__CLING_PTRCHECK(off) TakeHelper<RealT_t, T, std::vector<T>>
985 : public RActionImpl<TakeHelper<RealT_t, T, std::vector<T>>> {
987
988public:
989 using ColumnTypes_t = TypeList<T>;
990 TakeHelper(const std::shared_ptr<std::vector<T>> &resultColl, const unsigned int nSlots)
991 {
992 fColls.emplace_back(resultColl);
993 for (unsigned int i = 1; i < nSlots; ++i) {
994 auto v = std::make_shared<std::vector<T>>();
995 v->reserve(1024);
996 fColls.emplace_back(v);
997 }
998 }
1000 TakeHelper(const TakeHelper &) = delete;
1001
1002 void InitTask(TTreeReader *, unsigned int) {}
1003
1004 void Exec(unsigned int slot, T &v) { FillColl(v, *fColls[slot]); }
1005
1006 void Initialize() { /* noop */}
1007
1008 // This is optimised to treat vectors
1009 void Finalize()
1010 {
1011 ULong64_t totSize = 0;
1012 for (auto &coll : fColls)
1013 totSize += coll->size();
1014 auto rColl = fColls[0];
1015 rColl->reserve(totSize);
1016 for (unsigned int i = 1; i < fColls.size(); ++i) {
1017 auto &coll = fColls[i];
1018 rColl->insert(rColl->end(), coll->begin(), coll->end());
1019 }
1020 }
1021
1022 std::vector<T> &PartialUpdate(unsigned int slot) { return *fColls[slot]; }
1023
1024 std::string GetActionName() { return "Take"; }
1025
1026 TakeHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1027 {
1028 auto &result = *static_cast<std::shared_ptr<std::vector<T>> *>(newResult);
1029 result->clear();
1030 return TakeHelper(result, fColls.size());
1031 }
1032};
1033
1034// Case 3.: The column is a RVec, the collection is not a vector
1035// No optimisations, transformations from RVecs to vectors
1036template <typename RealT_t, typename COLL>
1038 : public RActionImpl<TakeHelper<RealT_t, RVec<RealT_t>, COLL>> {
1040
1041public:
1042 using ColumnTypes_t = TypeList<RVec<RealT_t>>;
1043 TakeHelper(const std::shared_ptr<COLL> &resultColl, const unsigned int nSlots)
1044 {
1045 fColls.emplace_back(resultColl);
1046 for (unsigned int i = 1; i < nSlots; ++i)
1047 fColls.emplace_back(std::make_shared<COLL>());
1048 }
1050 TakeHelper(const TakeHelper &) = delete;
1051
1052 void InitTask(TTreeReader *, unsigned int) {}
1053
1054 void Exec(unsigned int slot, RVec<RealT_t> av) { fColls[slot]->emplace_back(av.begin(), av.end()); }
1055
1056 void Initialize() { /* noop */}
1057
1058 void Finalize()
1059 {
1060 auto rColl = fColls[0];
1061 for (unsigned int i = 1; i < fColls.size(); ++i) {
1062 auto &coll = fColls[i];
1063 for (auto &v : *coll) {
1064 rColl->emplace_back(v);
1065 }
1066 }
1067 }
1068
1069 std::string GetActionName() { return "Take"; }
1070
1071 TakeHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1072 {
1073 auto &result = *static_cast<std::shared_ptr<COLL> *>(newResult);
1074 result->clear();
1075 return TakeHelper(result, fColls.size());
1076 }
1077};
1078
1079// Case 4.: The column is an RVec, the collection is a vector
1080// Optimisations, transformations from RVecs to vectors
1081template <typename RealT_t>
1082class R__CLING_PTRCHECK(off) TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>>
1083 : public RActionImpl<TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>>> {
1084
1086
1087public:
1088 using ColumnTypes_t = TypeList<RVec<RealT_t>>;
1089 TakeHelper(const std::shared_ptr<std::vector<std::vector<RealT_t>>> &resultColl, const unsigned int nSlots)
1090 {
1091 fColls.emplace_back(resultColl);
1092 for (unsigned int i = 1; i < nSlots; ++i) {
1093 auto v = std::make_shared<std::vector<RealT_t>>();
1094 v->reserve(1024);
1095 fColls.emplace_back(v);
1096 }
1097 }
1099 TakeHelper(const TakeHelper &) = delete;
1100
1101 void InitTask(TTreeReader *, unsigned int) {}
1102
1103 void Exec(unsigned int slot, RVec<RealT_t> av) { fColls[slot]->emplace_back(av.begin(), av.end()); }
1104
1105 void Initialize() { /* noop */}
1106
1107 // This is optimised to treat vectors
1108 void Finalize()
1109 {
1110 ULong64_t totSize = 0;
1111 for (auto &coll : fColls)
1112 totSize += coll->size();
1113 auto rColl = fColls[0];
1114 rColl->reserve(totSize);
1115 for (unsigned int i = 1; i < fColls.size(); ++i) {
1116 auto &coll = fColls[i];
1117 rColl->insert(rColl->end(), coll->begin(), coll->end());
1118 }
1119 }
1120
1121 std::string GetActionName() { return "Take"; }
1122
1123 TakeHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1124 {
1125 auto &result = *static_cast<typename decltype(fColls)::value_type *>(newResult);
1126 result->clear();
1127 return TakeHelper(result, fColls.size());
1128 }
1129};
1130
1131// Extern templates for TakeHelper
1132// NOTE: The move-constructor of specializations declared as extern templates
1133// must be defined out of line, otherwise cling fails to find its symbol.
1134template <typename RealT_t, typename T, typename COLL>
1136template <typename RealT_t, typename T>
1138template <typename RealT_t, typename COLL>
1140template <typename RealT_t>
1141TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>>::TakeHelper(TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>> &&) = default;
1142
1143// External templates are disabled for gcc5 since this version wrongly omits the C++11 ABI attribute
1144#if __GNUC__ > 5
1145extern template class TakeHelper<bool, bool, std::vector<bool>>;
1149extern template class TakeHelper<int, int, std::vector<int>>;
1150extern template class TakeHelper<long, long, std::vector<long>>;
1152extern template class TakeHelper<float, float, std::vector<float>>;
1154#endif
1155
1156template <typename ResultType>
1157class R__CLING_PTRCHECK(off) MinHelper : public RActionImpl<MinHelper<ResultType>> {
1158 std::shared_ptr<ResultType> fResultMin;
1160
1161public:
1162 MinHelper(MinHelper &&) = default;
1163 MinHelper(const std::shared_ptr<ResultType> &minVPtr, const unsigned int nSlots)
1164 : fResultMin(minVPtr), fMins(nSlots, std::numeric_limits<ResultType>::max())
1165 {
1166 }
1167
1168 void Exec(unsigned int slot, ResultType v) { fMins[slot] = std::min(v, fMins[slot]); }
1169
1170 void InitTask(TTreeReader *, unsigned int) {}
1171
1173 void Exec(unsigned int slot, const T &vs)
1174 {
1175 for (auto &&v : vs)
1176 fMins[slot] = std::min(static_cast<ResultType>(v), fMins[slot]);
1177 }
1178
1179 void Initialize() { /* noop */}
1180
1181 void Finalize()
1182 {
1183 *fResultMin = std::numeric_limits<ResultType>::max();
1184 for (auto &m : fMins)
1185 *fResultMin = std::min(m, *fResultMin);
1186 }
1187
1188 // Helper functions for RMergeableValue
1189 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1190 {
1191 return std::make_unique<RMergeableMin<ResultType>>(*fResultMin);
1192 }
1193
1194 ResultType &PartialUpdate(unsigned int slot) { return fMins[slot]; }
1195
1196 std::string GetActionName() { return "Min"; }
1197
1198 MinHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1199 {
1200 auto &result = *static_cast<std::shared_ptr<ResultType> *>(newResult);
1201 return MinHelper(result, fMins.size());
1202 }
1203};
1204
1205template <typename ResultType>
1206class R__CLING_PTRCHECK(off) MaxHelper : public RActionImpl<MaxHelper<ResultType>> {
1207 std::shared_ptr<ResultType> fResultMax;
1209
1210public:
1211 MaxHelper(MaxHelper &&) = default;
1212 MaxHelper(const MaxHelper &) = delete;
1213 MaxHelper(const std::shared_ptr<ResultType> &maxVPtr, const unsigned int nSlots)
1214 : fResultMax(maxVPtr), fMaxs(nSlots, std::numeric_limits<ResultType>::lowest())
1215 {
1216 }
1217
1218 void InitTask(TTreeReader *, unsigned int) {}
1219 void Exec(unsigned int slot, ResultType v) { fMaxs[slot] = std::max(v, fMaxs[slot]); }
1220
1222 void Exec(unsigned int slot, const T &vs)
1223 {
1224 for (auto &&v : vs)
1225 fMaxs[slot] = std::max(static_cast<ResultType>(v), fMaxs[slot]);
1226 }
1227
1228 void Initialize() { /* noop */}
1229
1230 void Finalize()
1231 {
1232 *fResultMax = std::numeric_limits<ResultType>::lowest();
1233 for (auto &m : fMaxs) {
1234 *fResultMax = std::max(m, *fResultMax);
1235 }
1236 }
1237
1238 // Helper functions for RMergeableValue
1239 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1240 {
1241 return std::make_unique<RMergeableMax<ResultType>>(*fResultMax);
1242 }
1243
1244 ResultType &PartialUpdate(unsigned int slot) { return fMaxs[slot]; }
1245
1246 std::string GetActionName() { return "Max"; }
1247
1248 MaxHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1249 {
1250 auto &result = *static_cast<std::shared_ptr<ResultType> *>(newResult);
1251 return MaxHelper(result, fMaxs.size());
1252 }
1253};
1254
1255template <typename ResultType>
1256class R__CLING_PTRCHECK(off) SumHelper : public RActionImpl<SumHelper<ResultType>> {
1257 std::shared_ptr<ResultType> fResultSum;
1260
1261 /// Evaluate neutral element for this type and the sum operation.
1262 /// This is assumed to be any_value - any_value if operator- is defined
1263 /// for the type, otherwise a default-constructed ResultType{} is used.
1264 template <typename T = ResultType>
1265 auto NeutralElement(const T &v, int /*overloadresolver*/) -> decltype(v - v)
1266 {
1267 return v - v;
1268 }
1269
1270 template <typename T = ResultType, typename Dummy = int>
1271 ResultType NeutralElement(const T &, Dummy) // this overload has lower priority thanks to the template arg
1272 {
1273 return ResultType{};
1274 }
1275
1276public:
1277 SumHelper(SumHelper &&) = default;
1278 SumHelper(const SumHelper &) = delete;
1279 SumHelper(const std::shared_ptr<ResultType> &sumVPtr, const unsigned int nSlots)
1282 {
1283 }
1284 void InitTask(TTreeReader *, unsigned int) {}
1285
1286 void Exec(unsigned int slot, ResultType x)
1287 {
1288 // Kahan Sum:
1290 ResultType t = fSums[slot] + y;
1291 fCompensations[slot] = (t - fSums[slot]) - y;
1292 fSums[slot] = t;
1293 }
1294
1296 void Exec(unsigned int slot, const T &vs)
1297 {
1298 for (auto &&v : vs) {
1299 Exec(slot, v);
1300 }
1301 }
1302
1303 void Initialize() { /* noop */}
1304
1305 void Finalize()
1306 {
1311 for (auto &m : fSums) {
1312 // Kahan Sum:
1313 y = m - compensation;
1314 t = sum + y;
1315 compensation = (t - sum) - y;
1316 sum = t;
1317 }
1318 *fResultSum += sum;
1319 }
1320
1321 // Helper functions for RMergeableValue
1322 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1323 {
1324 return std::make_unique<RMergeableSum<ResultType>>(*fResultSum);
1325 }
1326
1327 ResultType &PartialUpdate(unsigned int slot) { return fSums[slot]; }
1328
1329 std::string GetActionName() { return "Sum"; }
1330
1331 SumHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1332 {
1333 auto &result = *static_cast<std::shared_ptr<ResultType> *>(newResult);
1334 *result = NeutralElement(*result, -1);
1335 return SumHelper(result, fSums.size());
1336 }
1337};
1338
1339class R__CLING_PTRCHECK(off) MeanHelper : public RActionImpl<MeanHelper> {
1340 std::shared_ptr<double> fResultMean;
1341 std::vector<ULong64_t> fCounts;
1342 std::vector<double> fSums;
1343 std::vector<double> fPartialMeans;
1344 std::vector<double> fCompensations;
1345
1346public:
1347 MeanHelper(const std::shared_ptr<double> &meanVPtr, const unsigned int nSlots);
1348 MeanHelper(MeanHelper &&) = default;
1349 MeanHelper(const MeanHelper &) = delete;
1350 void InitTask(TTreeReader *, unsigned int) {}
1351 void Exec(unsigned int slot, double v);
1352
1354 void Exec(unsigned int slot, const T &vs)
1355 {
1356 for (auto &&v : vs) {
1357
1358 fCounts[slot]++;
1359 // Kahan Sum:
1360 double y = v - fCompensations[slot];
1361 double t = fSums[slot] + y;
1362 fCompensations[slot] = (t - fSums[slot]) - y;
1363 fSums[slot] = t;
1364 }
1365 }
1366
1367 void Initialize() { /* noop */}
1368
1369 void Finalize();
1370
1371 // Helper functions for RMergeableValue
1372 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1373 {
1374 const ULong64_t counts = std::accumulate(fCounts.begin(), fCounts.end(), 0ull);
1375 return std::make_unique<RMergeableMean>(*fResultMean, counts);
1376 }
1377
1378 double &PartialUpdate(unsigned int slot);
1379
1380 std::string GetActionName() { return "Mean"; }
1381
1382 MeanHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1383 {
1384 auto &result = *static_cast<std::shared_ptr<double> *>(newResult);
1385 return MeanHelper(result, fSums.size());
1386 }
1387};
1388
1389class R__CLING_PTRCHECK(off) StdDevHelper : public RActionImpl<StdDevHelper> {
1390 // Number of subsets of data
1391 unsigned int fNSlots;
1392 std::shared_ptr<double> fResultStdDev;
1393 // Number of element for each slot
1394 std::vector<ULong64_t> fCounts;
1395 // Mean of each slot
1396 std::vector<double> fMeans;
1397 // Squared distance from the mean
1398 std::vector<double> fDistancesfromMean;
1399
1400public:
1401 StdDevHelper(const std::shared_ptr<double> &meanVPtr, const unsigned int nSlots);
1402 StdDevHelper(StdDevHelper &&) = default;
1403 StdDevHelper(const StdDevHelper &) = delete;
1404 void InitTask(TTreeReader *, unsigned int) {}
1405 void Exec(unsigned int slot, double v);
1406
1408 void Exec(unsigned int slot, const T &vs)
1409 {
1410 for (auto &&v : vs) {
1411 Exec(slot, v);
1412 }
1413 }
1414
1415 void Initialize() { /* noop */}
1416
1417 void Finalize();
1418
1419 // Helper functions for RMergeableValue
1420 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1421 {
1422 const ULong64_t counts = std::accumulate(fCounts.begin(), fCounts.end(), 0ull);
1423 const Double_t mean =
1424 std::inner_product(fMeans.begin(), fMeans.end(), fCounts.begin(), 0.) / static_cast<Double_t>(counts);
1425 return std::make_unique<RMergeableStdDev>(*fResultStdDev, counts, mean);
1426 }
1427
1428 std::string GetActionName() { return "StdDev"; }
1429
1430 StdDevHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1431 {
1432 auto &result = *static_cast<std::shared_ptr<double> *>(newResult);
1433 return StdDevHelper(result, fCounts.size());
1434 }
1435};
1436
1437template <typename PrevNodeType>
1438class R__CLING_PTRCHECK(off) DisplayHelper : public RActionImpl<DisplayHelper<PrevNodeType>> {
1439private:
1441 std::shared_ptr<Display_t> fDisplayerHelper;
1442 std::shared_ptr<PrevNodeType> fPrevNode;
1443 size_t fEntriesToProcess;
1444
1445public:
1446 DisplayHelper(size_t nRows, const std::shared_ptr<Display_t> &d, const std::shared_ptr<PrevNodeType> &prevNode)
1447 : fDisplayerHelper(d), fPrevNode(prevNode), fEntriesToProcess(nRows)
1448 {
1449 }
1450 DisplayHelper(DisplayHelper &&) = default;
1451 DisplayHelper(const DisplayHelper &) = delete;
1452 void InitTask(TTreeReader *, unsigned int) {}
1453
1454 template <typename... Columns>
1455 void Exec(unsigned int, Columns &... columns)
1456 {
1457 if (fEntriesToProcess == 0)
1458 return;
1459
1460 fDisplayerHelper->AddRow(columns...);
1461 --fEntriesToProcess;
1462
1463 if (fEntriesToProcess == 0) {
1464 // No more entries to process. Send a one-time signal that this node
1465 // of the graph is done. It is important that the 'StopProcessing'
1466 // method is only called once from this helper, otherwise it would seem
1467 // like more than one operation has completed its work.
1468 fPrevNode->StopProcessing();
1469 }
1470 }
1471
1472 void Initialize() {}
1473
1474 void Finalize() {}
1475
1476 std::string GetActionName() { return "Display"; }
1477};
1478
1479template <typename Acc, typename Merge, typename R, typename T, typename U,
1480 bool MustCopyAssign = std::is_same<R, U>::value>
1482 : public RActionImpl<AggregateHelper<Acc, Merge, R, T, U, MustCopyAssign>> {
1484 Merge fMerge;
1485 std::shared_ptr<U> fResult;
1487
1488public:
1489 using ColumnTypes_t = TypeList<T>;
1490
1491 AggregateHelper(Acc &&f, Merge &&m, const std::shared_ptr<U> &result, const unsigned int nSlots)
1492 : fAggregate(std::move(f)), fMerge(std::move(m)), fResult(result), fAggregators(nSlots, *result)
1493 {
1494 }
1495
1496 AggregateHelper(Acc &f, Merge &m, const std::shared_ptr<U> &result, const unsigned int nSlots)
1497 : fAggregate(f), fMerge(m), fResult(result), fAggregators(nSlots, *result)
1498 {
1499 }
1500
1501 AggregateHelper(AggregateHelper &&) = default;
1502 AggregateHelper(const AggregateHelper &) = delete;
1503
1504 void InitTask(TTreeReader *, unsigned int) {}
1505
1506 template <bool MustCopyAssign_ = MustCopyAssign, std::enable_if_t<MustCopyAssign_, int> = 0>
1507 void Exec(unsigned int slot, const T &value)
1508 {
1510 }
1511
1512 template <bool MustCopyAssign_ = MustCopyAssign, std::enable_if_t<!MustCopyAssign_, int> = 0>
1513 void Exec(unsigned int slot, const T &value)
1514 {
1516 }
1517
1518 void Initialize() { /* noop */}
1519
1521 bool MergeAll = std::is_same<void, MergeRet>::value>
1522 std::enable_if_t<MergeAll, void> Finalize()
1523 {
1524 fMerge(fAggregators);
1525 *fResult = fAggregators[0];
1526 }
1527
1529 bool MergeTwoByTwo = std::is_same<U, MergeRet>::value>
1530 std::enable_if_t<MergeTwoByTwo, void> Finalize(...) // ... needed to let compiler distinguish overloads
1531 {
1532 for (const auto &acc : fAggregators)
1533 *fResult = fMerge(*fResult, acc);
1534 }
1535
1536 U &PartialUpdate(unsigned int slot) { return fAggregators[slot]; }
1537
1538 std::string GetActionName() { return "Aggregate"; }
1539
1540 AggregateHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1541 {
1542 auto &result = *static_cast<std::shared_ptr<U> *>(newResult);
1543 return AggregateHelper(fAggregate, fMerge, result, fAggregators.size());
1544 }
1545};
1546
1547} // end of NS RDF
1548} // end of NS Internal
1549} // end of NS ROOT
1550
1551/// \endcond
1552
1553#endif
PyObject * fCallable
Handle_t Display_t
Display handle.
Definition GuiTypes.h:27
#define d(i)
Definition RSha256.hxx:102
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define g(i)
Definition RSha256.hxx:105
#define h(i)
Definition RSha256.hxx:106
#define R(a, b, c, d, e, f, g, h, i)
Definition RSha256.hxx:110
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
Basic types used by ROOT and required by TInterpreter.
double Double_t
Double 8 bytes.
Definition RtypesCore.h:73
unsigned long long ULong64_t
Portable unsigned long integer 8 bytes.
Definition RtypesCore.h:84
#define X(type, name)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char x1
Binding & operator=(OUT(*fun)(void))
TClass * IsA() const override
Definition TStringLong.h:20
TTime operator*(const TTime &t1, const TTime &t2)
Definition TTime.h:85
Base class for action helpers, see RInterface::Book() for more information.
A histogram data structure to bin data along multiple dimensions.
A histogram for aggregation of data along multiple dimensions.
Definition RHist.hxx:64
This class is the textual representation of the content of a columnar dataset.
Definition RDisplay.hxx:65
const_iterator begin() const
const_iterator end() const
A "std::vector"-like collection of values implementing handy operation to analyse them.
Definition RVec.hxx:1524
Collection abstract base class.
Definition TCollection.h:65
TGraph with asymmetric error bars.
A TGraph is an object made of two arrays X and Y with npoints each.
Definition TGraph.h:41
1-D histogram with a double per channel (see TH1 documentation)
Definition TH1.h:926
TH1 is the base class of all histogram classes in ROOT.
Definition TH1.h:109
A doubly linked list.
Definition TList.h:38
void Add(TObject *obj) override
Definition TList.h:81
Statistical variable, defined by its mean and variance (RMS).
Definition TStatistic.h:33
A simple, robust and fast interface to read values from ROOT columnar datasets such as TTree,...
Definition TTreeReader.h:46
RooCmdArg Columns(Int_t ncol)
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
#define I(x, y, z)
#define H(x, y, z)
CPYCPPYY_EXTERN bool Exec(const std::string &cmd)
Definition API.cxx:455
std::unique_ptr< RMergeableVariations< T > > GetMergeableValue(ROOT::RDF::Experimental::RResultMap< T > &rmap)
Retrieve mergeable values after calling ROOT::RDF::VariationsFor .
void ResetIfPossible(TStatistic *h)
constexpr std::size_t FindIdxTrue(const T &arr)
Definition Utils.hxx:235
void UnsetDirectoryIfPossible(TH1 *h)
auto FillThreadSafe(T &histo, Args... args) -> decltype(histo.FillThreadSafe(args...), void())
Entrypoint for thread-safe filling from RDataFrame.
Definition TH3.h:39
ROOT type_traits extensions.
void Initialize(Bool_t useTMVAStyle=kTRUE)
Definition tmvaglob.cxx:176
A weight for filling histograms.
Definition RWeight.hxx:17
TMarker m
Definition textangle.C:8
TLine l
Definition textangle.C:4
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2338