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 <algorithm>
36#include <array>
37#include <limits>
38#include <memory>
39#include <mutex>
40#include <stdexcept>
41#include <string>
42#include <string_view>
43#include <type_traits>
44#include <utility> // std::index_sequence
45#include <vector>
46#include <numeric> // std::accumulate in MeanHelper
47
48class TCollection;
49class TStatistic;
50class TTreeReader;
51namespace ROOT::RDF {
52class RCutFlowReport;
53} // namespace ROOT::RDF
54
55/// \cond HIDDEN_SYMBOLS
56
57namespace ROOT {
58namespace Internal {
59namespace RDF {
60using namespace ROOT::TypeTraits;
61using namespace ROOT::VecOps;
62using namespace ROOT::RDF;
63using namespace ROOT::Detail::RDF;
64
65using Hist_t = ::TH1D;
66
67/// The container type for each thread's partial result in an action helper
68// We have to avoid to instantiate std::vector<bool> as that makes it impossible to return a reference to one of
69// the thread-local results. In addition, a common definition for the type of the container makes it easy to swap
70// the type of the underlying container if e.g. we see problems with false sharing of the thread-local results..
71template <typename T>
72using Results = std::conditional_t<std::is_same<T, bool>::value, std::deque<T>, std::vector<T>>;
73
74template <typename F>
75class R__CLING_PTRCHECK(off) ForeachSlotHelper : public RActionImpl<ForeachSlotHelper<F>> {
76 F fCallable;
77
78public:
82 ForeachSlotHelper(const ForeachSlotHelper &) = delete;
83
84 void InitTask(TTreeReader *, unsigned int) {}
85
86 template <typename... Args>
87 void Exec(unsigned int slot, Args &&... args)
88 {
89 // check that the decayed types of Args are the same as the branch types
90 static_assert(std::is_same<TypeList<std::decay_t<Args>...>, ColumnTypes_t>::value, "");
91 fCallable(slot, std::forward<Args>(args)...);
92 }
93
94 void Initialize() { /* noop */}
95
96 void Finalize() { /* noop */}
97
98 std::string GetActionName() { return "ForeachSlot"; }
99};
100
101class R__CLING_PTRCHECK(off) CountHelper : public RActionImpl<CountHelper> {
102 std::shared_ptr<ULong64_t> fResultCount;
103 Results<ULong64_t> fCounts;
104
105public:
106 using ColumnTypes_t = TypeList<>;
107 CountHelper(const std::shared_ptr<ULong64_t> &resultCount, const unsigned int nSlots);
108 CountHelper(CountHelper &&) = default;
109 CountHelper(const CountHelper &) = delete;
110 void InitTask(TTreeReader *, unsigned int) {}
111 void Exec(unsigned int slot);
112 void Initialize() { /* noop */}
113 void Finalize();
114
115 // Helper functions for RMergeableValue
116 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
117 {
118 return std::make_unique<RMergeableCount>(*fResultCount);
119 }
120
121 ULong64_t &PartialUpdate(unsigned int slot);
122
123 std::string GetActionName() { return "Count"; }
124
125 CountHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
126 {
127 auto &result = *static_cast<std::shared_ptr<ULong64_t> *>(newResult);
128 return CountHelper(result, fCounts.size());
129 }
130};
131
132template <typename RNode_t>
133class R__CLING_PTRCHECK(off) ReportHelper : public RActionImpl<ReportHelper<RNode_t>> {
134 std::shared_ptr<RCutFlowReport> fReport;
135 /// Non-owning pointer, never null. As usual, the node is owned by its children nodes (and therefore indirectly by
136 /// the RAction corresponding to this action helper).
137 RNode_t *fNode;
139
140public:
141 using ColumnTypes_t = TypeList<>;
142 ReportHelper(const std::shared_ptr<RCutFlowReport> &report, RNode_t *node, bool emptyRep)
143 : fReport(report), fNode(node), fReturnEmptyReport(emptyRep){};
144 ReportHelper(ReportHelper &&) = default;
145 ReportHelper(const ReportHelper &) = delete;
146 void InitTask(TTreeReader *, unsigned int) {}
147 void Exec(unsigned int /* slot */) {}
148 void Initialize() { /* noop */}
149 void Finalize()
150 {
152 fNode->Report(*fReport);
153 }
154
155 std::string GetActionName() { return "Report"; }
156
157 ReportHelper MakeNew(void *newResult, std::string_view variation = "nominal")
158 {
159 auto &&result = *static_cast<std::shared_ptr<RCutFlowReport> *>(newResult);
160 return ReportHelper{result,
161 std::static_pointer_cast<RNode_t>(fNode->GetVariedFilter(std::string(variation))).get(),
163 }
164};
165
166/// This helper fills TH1Ds for which no axes were specified by buffering the fill values to pick good axes limits.
167///
168/// TH1Ds have an automatic mechanism to pick good limits based on the first N entries they were filled with, but
169/// that does not work in multi-thread event loops as it might yield histograms with incompatible binning in each
170/// thread, making it impossible to merge the per-thread results.
171/// Instead, this helper delays the decision on the axes limits until all threads have done processing, synchronizing
172/// the decision on the limits as part of the merge operation.
173class R__CLING_PTRCHECK(off) BufferedFillHelper : public RActionImpl<BufferedFillHelper> {
174 // this sets a total initial size of 16 MB for the buffers (can increase)
175 static constexpr unsigned int fgTotalBufSize = 2097152;
176 using BufEl_t = double;
177 using Buf_t = std::vector<BufEl_t>;
178
179 std::vector<Buf_t> fBuffers;
180 std::vector<Buf_t> fWBuffers;
181 std::shared_ptr<Hist_t> fResultHist;
182 unsigned int fNSlots;
183 unsigned int fBufSize;
184 /// Histograms containing "snapshots" of partial results. Non-null only if a registered callback requires it.
186 Buf_t fMin;
187 Buf_t fMax;
188
189 void UpdateMinMax(unsigned int slot, double v);
190
191public:
192 BufferedFillHelper(const std::shared_ptr<Hist_t> &h, const unsigned int nSlots);
194 BufferedFillHelper(const BufferedFillHelper &) = delete;
195 void InitTask(TTreeReader *, unsigned int) {}
196 void Exec(unsigned int slot, double v);
197 void Exec(unsigned int slot, double v, double w);
198
200 void Exec(unsigned int slot, const T &vs)
201 {
202 auto &thisBuf = fBuffers[slot];
203 // range-based for results in warnings on some compilers due to vector<bool>'s custom reference type
204 for (auto v = vs.begin(); v != vs.end(); ++v) {
206 thisBuf.emplace_back(*v); // TODO: Can be optimised in case T == BufEl_t
207 }
208 }
209
211 void Exec(unsigned int slot, const T &vs, const W &ws)
212 {
213 auto &thisBuf = fBuffers[slot];
214
215 for (auto &v : vs) {
217 thisBuf.emplace_back(v);
218 }
219
220 auto &thisWBuf = fWBuffers[slot];
221 for (auto &w : ws) {
222 thisWBuf.emplace_back(w); // TODO: Can be optimised in case T == BufEl_t
223 }
224 }
225
227 void Exec(unsigned int slot, const T &vs, const W w)
228 {
229 auto &thisBuf = fBuffers[slot];
230 for (auto &v : vs) {
232 thisBuf.emplace_back(v); // TODO: Can be optimised in case T == BufEl_t
233 }
234
235 auto &thisWBuf = fWBuffers[slot];
236 thisWBuf.insert(thisWBuf.end(), vs.size(), w);
237 }
238
240 void Exec(unsigned int slot, const T v, const W &ws)
241 {
243 auto &thisBuf = fBuffers[slot];
244 thisBuf.insert(thisBuf.end(), ws.size(), v);
245
246 auto &thisWBuf = fWBuffers[slot];
247 thisWBuf.insert(thisWBuf.end(), ws.begin(), ws.end());
248 }
249
250 Hist_t &PartialUpdate(unsigned int);
251
252 void Initialize() { /* noop */}
253
254 void Finalize();
255
256 // Helper functions for RMergeableValue
257 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
258 {
259 return std::make_unique<RMergeableFill<Hist_t>>(*fResultHist);
260 }
261
262 std::string GetActionName()
263 {
264 return std::string(fResultHist->IsA()->GetName()) + "\\n" + std::string(fResultHist->GetName());
265 }
266
267 BufferedFillHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
268 {
269 auto &result = *static_cast<std::shared_ptr<Hist_t> *>(newResult);
270 result->Reset();
271 result->SetDirectory(nullptr);
272 return BufferedFillHelper(result, fNSlots);
273 }
274};
275
276// class which wraps a pointer and implements a no-op increment operator
277template <typename T>
279 const T *obj_;
280
281public:
282 using iterator_category = std::forward_iterator_tag;
283 using difference_type = std::ptrdiff_t;
284 using value_type = T;
285 using pointer = T *;
286 using reference = T &;
287 ScalarConstIterator(const T *obj) : obj_(obj) {}
288 const T &operator*() const { return *obj_; }
289 ScalarConstIterator<T> &operator++() { return *this; }
290};
291
292// return unchanged value for scalar
293template <typename T>
294auto MakeBegin(const T &val)
295{
296 if constexpr (IsDataContainer<T>::value) {
297 return std::begin(val);
298 } else {
299 return ScalarConstIterator<T>(&val);
300 }
301}
302
303// return container size for containers, and 1 for scalars
304template <typename T>
305std::size_t GetSize(const T &val)
306{
307 if constexpr (IsDataContainer<T>::value) {
308 return std::size(val);
309 } else {
310 return 1;
311 }
312}
313
314// Helpers for dealing with histograms and similar:
316void ResetIfPossible(H *h)
317{
318 h->Reset();
319}
320
322void ResetIfPossible(...);
323
326
327/// The generic Fill helper: it calls Fill on per-thread objects and then Merge to produce a final result.
328/// For one-dimensional histograms, if no axes are specified, RDataFrame uses BufferedFillHelper instead.
329template <typename HIST = Hist_t>
330class R__CLING_PTRCHECK(off) FillHelper : public RActionImpl<FillHelper<HIST>> {
331 std::vector<HIST *> fObjects;
332
333 // Merge overload for types with Merge(TCollection*), like TH1s
335 auto Merge(std::vector<H *> &objs, int /*toincreaseoverloadpriority*/)
336 -> decltype(objs[0]->Merge((TCollection *)nullptr), void())
337 {
338 TList l;
339 for (auto it = ++objs.begin(); it != objs.end(); ++it)
340 l.Add(*it);
341 objs[0]->Merge(&l);
342 }
343
344 // Merge overload for types with Merge(const std::vector&)
345 template <typename H>
346 auto Merge(std::vector<H *> &objs, double /*toloweroverloadpriority*/)
347 -> decltype(objs[0]->Merge(std::vector<HIST *>{}), void())
348 {
349 objs[0]->Merge({++objs.begin(), objs.end()});
350 }
351
352 // Merge overload to error out in case no valid HIST::Merge method was detected
353 template <typename T>
354 void Merge(T, ...)
355 {
356 static_assert(sizeof(T) < 0,
357 "The type passed to Fill does not provide a Merge(TCollection*) or Merge(const std::vector&) method.");
358 }
359
360 template <std::size_t ColIdx, typename End_t, typename... Its>
361 void ExecLoop(unsigned int slot, End_t end, Its... its)
362 {
363 for (auto *thisSlotH = fObjects[slot]; GetNthElement<ColIdx>(its...) != end; (std::advance(its, 1), ...)) {
364 thisSlotH->Fill(*its...);
365 }
366 }
367
368public:
369 FillHelper(FillHelper &&) = default;
370 FillHelper(const FillHelper &) = delete;
371
372 FillHelper(const std::shared_ptr<HIST> &h, const unsigned int nSlots) : fObjects(nSlots, nullptr)
373 {
374 fObjects[0] = h.get();
375 // Initialize all other slots
376 for (unsigned int i = 1; i < nSlots; ++i) {
377 fObjects[i] = new HIST(*fObjects[0]);
378 UnsetDirectoryIfPossible(fObjects[i]);
379 }
380 }
381
382 void InitTask(TTreeReader *, unsigned int) {}
383
384 // no container arguments
385 template <typename... ValTypes, std::enable_if_t<!Disjunction<IsDataContainer<ValTypes>...>::value, int> = 0>
386 auto Exec(unsigned int slot, const ValTypes &...x) -> decltype(fObjects[slot]->Fill(x...), void())
387 {
388 fObjects[slot]->Fill(x...);
389 }
390
391 // at least one container argument
392 template <typename... Xs, std::enable_if_t<Disjunction<IsDataContainer<Xs>...>::value, int> = 0>
393 auto Exec(unsigned int slot, const Xs &...xs) -> decltype(fObjects[slot]->Fill(*MakeBegin(xs)...), void())
394 {
395 // array of bools keeping track of which inputs are containers
396 constexpr std::array<bool, sizeof...(Xs)> isContainer{IsDataContainer<Xs>::value...};
397
398 // index of the first container input
399 constexpr std::size_t colidx = FindIdxTrue(isContainer);
400 // if this happens, there is a bug in the implementation
401 static_assert(colidx < sizeof...(Xs), "Error: index of collection-type argument not found.");
402
403 // get the end iterator to the first container
404 auto const xrefend = std::end(GetNthElement<colidx>(xs...));
405
406 // array of container sizes (1 for scalars)
407 std::array<std::size_t, sizeof...(xs)> sizes = {{GetSize(xs)...}};
408
409 for (std::size_t i = 0; i < sizeof...(xs); ++i) {
410 if (isContainer[i] && sizes[i] != sizes[colidx]) {
411 throw std::runtime_error("Cannot fill histogram with values in containers of different sizes.");
412 }
413 }
414
416 }
417
418 template <typename T = HIST>
419 void Exec(...)
420 {
421 static_assert(sizeof(T) < 0,
422 "When filling an object with RDataFrame (e.g. via a Fill action) the number or types of the "
423 "columns passed did not match the signature of the object's `Fill` method.");
424 }
425
426 void Initialize() { /* noop */}
427
428 void Finalize()
429 {
430 if (fObjects.size() == 1)
431 return;
432
433 Merge(fObjects, /*toselectcorrectoverload=*/0);
434
435 // delete the copies we created for the slots other than the first
436 for (auto it = ++fObjects.begin(); it != fObjects.end(); ++it)
437 delete *it;
438 }
439
440 HIST &PartialUpdate(unsigned int slot) { return *fObjects[slot]; }
441
442 // Helper functions for RMergeableValue
443 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
444 {
445 return std::make_unique<RMergeableFill<HIST>>(*fObjects[0]);
446 }
447
448 // if the fObjects vector type is derived from TObject, return the name of the object
450 std::string GetActionName()
451 {
452 return std::string(fObjects[0]->IsA()->GetName()) + "\\n" + std::string(fObjects[0]->GetName());
453 }
454
455 // if fObjects is not derived from TObject, indicate it is some other object
457 std::string GetActionName()
458 {
459 return "Fill custom object";
460 }
461
462 template <typename H = HIST>
463 FillHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
464 {
465 auto &result = *static_cast<std::shared_ptr<H> *>(newResult);
466 ResetIfPossible(result.get());
468 return FillHelper(result, fObjects.size());
469 }
470};
471
473public:
474 using Result_t = ::TGraph;
475
476private:
477 std::vector<::TGraph *> fGraphs;
478
479public:
481 FillTGraphHelper(const FillTGraphHelper &) = delete;
482
483 FillTGraphHelper(const std::shared_ptr<::TGraph> &g, const unsigned int nSlots) : fGraphs(nSlots, nullptr)
484 {
485 fGraphs[0] = g.get();
486 // Initialize all other slots
487 for (unsigned int i = 1; i < nSlots; ++i) {
488 fGraphs[i] = new TGraph(*fGraphs[0]);
489 }
490 }
491
492 void Initialize() {}
493 void InitTask(TTreeReader *, unsigned int) {}
494
495 // case: both types are container types
496 template <typename X0, typename X1,
497 std::enable_if_t<IsDataContainer<X0>::value && IsDataContainer<X1>::value, int> = 0>
498 void Exec(unsigned int slot, const X0 &x0s, const X1 &x1s)
499 {
500 if (x0s.size() != x1s.size()) {
501 throw std::runtime_error("Cannot fill Graph with values in containers of different sizes.");
502 }
503 auto *thisSlotG = fGraphs[slot];
504 auto x0sIt = std::begin(x0s);
505 const auto x0sEnd = std::end(x0s);
506 auto x1sIt = std::begin(x1s);
507 for (; x0sIt != x0sEnd; x0sIt++, x1sIt++) {
508 thisSlotG->SetPoint(thisSlotG->GetN(), *x0sIt, *x1sIt);
509 }
510 }
511
512 // case: both types are non-container types, e.g. scalars
513 template <typename X0, typename X1,
514 std::enable_if_t<!IsDataContainer<X0>::value && !IsDataContainer<X1>::value, int> = 0>
515 void Exec(unsigned int slot, X0 x0, X1 x1)
516 {
517 auto thisSlotG = fGraphs[slot];
518 thisSlotG->SetPoint(thisSlotG->GetN(), x0, x1);
519 }
520
521 // case: types are combination of containers and non-containers
522 // this is not supported, error out
523 template <typename X0, typename X1, typename... ExtraArgsToLowerPriority>
524 void Exec(unsigned int, X0, X1, ExtraArgsToLowerPriority...)
525 {
526 throw std::runtime_error("Graph was applied to a mix of scalar values and collections. This is not supported.");
527 }
528
529 void Finalize()
530 {
531 const auto nSlots = fGraphs.size();
532 auto resGraph = fGraphs[0];
533 TList l;
534 l.SetOwner(); // The list will free the memory associated to its elements upon destruction
535 for (unsigned int slot = 1; slot < nSlots; ++slot) {
536 l.Add(fGraphs[slot]);
537 }
538 resGraph->Merge(&l);
539 }
540
541 // Helper functions for RMergeableValue
542 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
543 {
544 return std::make_unique<RMergeableFill<Result_t>>(*fGraphs[0]);
545 }
546
547 std::string GetActionName() { return "Graph"; }
548
549 Result_t &PartialUpdate(unsigned int slot) { return *fGraphs[slot]; }
550
551 FillTGraphHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
552 {
553 auto &result = *static_cast<std::shared_ptr<TGraph> *>(newResult);
554 result->Set(0);
555 return FillTGraphHelper(result, fGraphs.size());
556 }
557};
558
560 : public ROOT::Detail::RDF::RActionImpl<FillTGraphAsymmErrorsHelper> {
561public:
562 using Result_t = ::TGraphAsymmErrors;
563
564private:
565 std::vector<::TGraphAsymmErrors *> fGraphAsymmErrors;
566
567public:
570
571 FillTGraphAsymmErrorsHelper(const std::shared_ptr<::TGraphAsymmErrors> &g, const unsigned int nSlots)
572 : fGraphAsymmErrors(nSlots, nullptr)
573 {
574 fGraphAsymmErrors[0] = g.get();
575 // Initialize all other slots
576 for (unsigned int i = 1; i < nSlots; ++i) {
578 }
579 }
580
581 void Initialize() {}
582 void InitTask(TTreeReader *, unsigned int) {}
583
584 // case: all types are container types
585 template <
586 typename X, typename Y, typename EXL, typename EXH, typename EYL, typename EYH,
587 std::enable_if_t<IsDataContainer<X>::value && IsDataContainer<Y>::value && IsDataContainer<EXL>::value &&
588 IsDataContainer<EXH>::value && IsDataContainer<EYL>::value && IsDataContainer<EYH>::value,
589 int> = 0>
590 void
591 Exec(unsigned int slot, const X &xs, const Y &ys, const EXL &exls, const EXH &exhs, const EYL &eyls, const EYH &eyhs)
592 {
593 if ((xs.size() != ys.size()) || (xs.size() != exls.size()) || (xs.size() != exhs.size()) ||
594 (xs.size() != eyls.size()) || (xs.size() != eyhs.size())) {
595 throw std::runtime_error("Cannot fill GraphAsymmErrors with values in containers of different sizes.");
596 }
598 auto xsIt = std::begin(xs);
599 auto ysIt = std::begin(ys);
600 auto exlsIt = std::begin(exls);
601 auto exhsIt = std::begin(exhs);
602 auto eylsIt = std::begin(eyls);
603 auto eyhsIt = std::begin(eyhs);
604 while (xsIt != std::end(xs)) {
605 const auto n = thisSlotG->GetN(); // must use the same `n` for SetPoint and SetPointError
606 thisSlotG->SetPoint(n, *xsIt++, *ysIt++);
607 thisSlotG->SetPointError(n, *exlsIt++, *exhsIt++, *eylsIt++, *eyhsIt++);
608 }
609 }
610
611 // case: all types are non-container types, e.g. scalars
612 template <
613 typename X, typename Y, typename EXL, typename EXH, typename EYL, typename EYH,
614 std::enable_if_t<!IsDataContainer<X>::value && !IsDataContainer<Y>::value && !IsDataContainer<EXL>::value &&
615 !IsDataContainer<EXH>::value && !IsDataContainer<EYL>::value && !IsDataContainer<EYH>::value,
616 int> = 0>
617 void Exec(unsigned int slot, X x, Y y, EXL exl, EXH exh, EYL eyl, EYH eyh)
618 {
620 const auto n = thisSlotG->GetN();
621 thisSlotG->SetPoint(n, x, y);
622 thisSlotG->SetPointError(n, exl, exh, eyl, eyh);
623 }
624
625 // case: types are combination of containers and non-containers
626 // this is not supported, error out
627 template <typename X, typename Y, typename EXL, typename EXH, typename EYL, typename EYH,
628 typename... ExtraArgsToLowerPriority>
629 void Exec(unsigned int, X, Y, EXL, EXH, EYL, EYH, ExtraArgsToLowerPriority...)
630 {
631 throw std::runtime_error(
632 "GraphAsymmErrors was applied to a mix of scalar values and collections. This is not supported.");
633 }
634
635 void Finalize()
636 {
637 const auto nSlots = fGraphAsymmErrors.size();
639 TList l;
640 l.SetOwner(); // The list will free the memory associated to its elements upon destruction
641 for (unsigned int slot = 1; slot < nSlots; ++slot) {
643 }
644 resGraphAsymmErrors->Merge(&l);
645 }
646
647 // Helper functions for RMergeableValue
648 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
649 {
650 return std::make_unique<RMergeableFill<Result_t>>(*fGraphAsymmErrors[0]);
651 }
652
653 std::string GetActionName() { return "GraphAsymmErrors"; }
654
655 Result_t &PartialUpdate(unsigned int slot) { return *fGraphAsymmErrors[slot]; }
656
657 FillTGraphAsymmErrorsHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
658 {
659 auto &result = *static_cast<std::shared_ptr<TGraphAsymmErrors> *>(newResult);
660 result->Set(0);
662 }
663};
664
665/// A FillHelper for classes supporting the FillThreadSafe function.
666template <typename HIST>
667class R__CLING_PTRCHECK(off) ThreadSafeFillHelper : public RActionImpl<ThreadSafeFillHelper<HIST>> {
668 std::vector<std::shared_ptr<HIST>> fObjects;
669 std::vector<std::unique_ptr<std::mutex>> fMutexPtrs;
670
671 // This overload matches if the function exists:
672 template <typename T, typename... Args>
673 auto TryCallFillThreadSafe(T &object, std::mutex &, int /*dummy*/, Args... args)
674 -> decltype(ROOT::Internal::FillThreadSafe(object, args...), void())
675 {
676 ROOT::Internal::FillThreadSafe(object, args...);
677 }
678 // This one has lower precedence because of the dummy argument, and uses a lock
679 template <typename T, typename... Args>
680 auto TryCallFillThreadSafe(T &object, std::mutex &mutex, char /*dummy*/, Args... args)
681 {
682 std::scoped_lock lock{mutex};
683 object.Fill(args...);
684 }
685
686 template <std::size_t ColIdx, typename End_t, typename... Its>
687 void ExecLoop(unsigned int slot, End_t end, Its... its)
688 {
689 const auto localSlot = slot % fObjects.size();
690 for (; GetNthElement<ColIdx>(its...) != end; (std::advance(its, 1), ...)) {
692 }
693 }
694
695public:
698
699 ThreadSafeFillHelper(const std::shared_ptr<HIST> &h, const unsigned int nSlots)
700 {
701 fObjects.resize(nSlots);
702 fObjects.front() = h;
703
704 std::generate(fObjects.begin() + 1, fObjects.end(), [h]() {
705 auto hist = std::make_shared<HIST>(*h);
706 UnsetDirectoryIfPossible(hist.get());
707 return hist;
708 });
709 fMutexPtrs.resize(nSlots);
710 std::generate(fMutexPtrs.begin(), fMutexPtrs.end(), []() { return std::make_unique<std::mutex>(); });
711 }
712
713 void InitTask(TTreeReader *, unsigned int) {}
714
715 // no container arguments
716 template <typename... ValTypes, std::enable_if_t<!Disjunction<IsDataContainer<ValTypes>...>::value, int> = 0>
717 void Exec(unsigned int slot, const ValTypes &...x)
718 {
719 const auto localSlot = slot % fObjects.size();
721 }
722
723 // at least one container argument
724 template <typename... Xs, std::enable_if_t<Disjunction<IsDataContainer<Xs>...>::value, int> = 0>
725 void Exec(unsigned int slot, const Xs &...xs)
726 {
727 // array of bools keeping track of which inputs are containers
728 constexpr std::array<bool, sizeof...(Xs)> isContainer{IsDataContainer<Xs>::value...};
729
730 // index of the first container input
731 constexpr std::size_t colidx = FindIdxTrue(isContainer);
732 // if this happens, there is a bug in the implementation
733 static_assert(colidx < sizeof...(Xs), "Error: index of collection-type argument not found.");
734
735 // get the end iterator to the first container
736 auto const xrefend = std::end(GetNthElement<colidx>(xs...));
737
738 // array of container sizes (1 for scalars)
739 std::array<std::size_t, sizeof...(xs)> sizes = {{GetSize(xs)...}};
740
741 for (std::size_t i = 0; i < sizeof...(xs); ++i) {
742 if (isContainer[i] && sizes[i] != sizes[colidx]) {
743 throw std::runtime_error("Cannot fill histogram with values in containers of different sizes.");
744 }
745 }
746
748 }
749
750 template <typename T = HIST>
751 void Exec(...)
752 {
753 static_assert(sizeof(T) < 0,
754 "When filling an object with RDataFrame (e.g. via a Fill action) the number or types of the "
755 "columns passed did not match the signature of the object's `FillThreadSafe` method.");
756 }
757
758 void Initialize() { /* noop */ }
759
760 void Finalize()
761 {
762 if (fObjects.size() > 1) {
763 TList list;
764 for (auto it = fObjects.cbegin() + 1; it != fObjects.end(); ++it) {
765 list.Add(it->get());
766 }
767 fObjects[0]->Merge(&list);
768 }
769
770 fObjects.resize(1);
771 fMutexPtrs.clear();
772 }
773
774 // Helper function for RMergeableValue
775 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
776 {
777 return std::make_unique<RMergeableFill<HIST>>(*fObjects[0]);
778 }
779
780 // if the fObjects vector type is derived from TObject, return the name of the object
782 std::string GetActionName()
783 {
784 return std::string(fObjects[0]->IsA()->GetName()) + "\\n" + std::string(fObjects[0]->GetName());
785 }
786
787 template <typename H = HIST>
788 ThreadSafeFillHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
789 {
790 auto &result = *static_cast<std::shared_ptr<H> *>(newResult);
791 ResetIfPossible(result.get());
793 return ThreadSafeFillHelper(result, fObjects.size());
794 }
795};
796
797// In case of the take helper we have 4 cases:
798// 1. The column is not an RVec, the collection is not a vector
799// 2. The column is not an RVec, the collection is a vector
800// 3. The column is an RVec, the collection is not a vector
801// 4. The column is an RVec, the collection is a vector
802
803template <typename V, typename COLL>
804void FillColl(V&& v, COLL& c) {
805 c.emplace_back(v);
806}
807
808// Use push_back for bool since some compilers do not support emplace_back.
809template <typename COLL>
810void FillColl(bool v, COLL& c) {
811 c.push_back(v);
812}
813
814// Case 1.: The column is not an RVec, the collection is not a vector
815// No optimisations, no transformations: just copies.
816template <typename RealT_t, typename T, typename COLL>
817class R__CLING_PTRCHECK(off) TakeHelper : public RActionImpl<TakeHelper<RealT_t, T, COLL>> {
819
820public:
821 using ColumnTypes_t = TypeList<T>;
822 TakeHelper(const std::shared_ptr<COLL> &resultColl, const unsigned int nSlots)
823 {
824 fColls.emplace_back(resultColl);
825 for (unsigned int i = 1; i < nSlots; ++i)
826 fColls.emplace_back(std::make_shared<COLL>());
827 }
829 TakeHelper(const TakeHelper &) = delete;
830
831 void InitTask(TTreeReader *, unsigned int) {}
832
833 void Exec(unsigned int slot, T &v) { FillColl(v, *fColls[slot]); }
834
835 void Initialize() { /* noop */}
836
837 void Finalize()
838 {
839 auto rColl = fColls[0];
840 for (unsigned int i = 1; i < fColls.size(); ++i) {
841 const auto &coll = fColls[i];
842 const auto end = coll->end();
843 // Use an explicit loop here to prevent compiler warnings introduced by
844 // clang's range-based loop analysis and vector<bool> references.
845 for (auto j = coll->begin(); j != end; j++) {
846 FillColl(*j, *rColl);
847 }
848 }
849 }
850
851 COLL &PartialUpdate(unsigned int slot) { return *fColls[slot].get(); }
852
853 std::string GetActionName() { return "Take"; }
854
855 TakeHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
856 {
857 auto &result = *static_cast<std::shared_ptr<COLL> *>(newResult);
858 result->clear();
859 return TakeHelper(result, fColls.size());
860 }
861};
862
863// Case 2.: The column is not an RVec, the collection is a vector
864// Optimisations, no transformations: just copies.
865template <typename RealT_t, typename T>
866class R__CLING_PTRCHECK(off) TakeHelper<RealT_t, T, std::vector<T>>
867 : public RActionImpl<TakeHelper<RealT_t, T, std::vector<T>>> {
869
870public:
871 using ColumnTypes_t = TypeList<T>;
872 TakeHelper(const std::shared_ptr<std::vector<T>> &resultColl, const unsigned int nSlots)
873 {
874 fColls.emplace_back(resultColl);
875 for (unsigned int i = 1; i < nSlots; ++i) {
876 auto v = std::make_shared<std::vector<T>>();
877 v->reserve(1024);
878 fColls.emplace_back(v);
879 }
880 }
882 TakeHelper(const TakeHelper &) = delete;
883
884 void InitTask(TTreeReader *, unsigned int) {}
885
886 void Exec(unsigned int slot, T &v) { FillColl(v, *fColls[slot]); }
887
888 void Initialize() { /* noop */}
889
890 // This is optimised to treat vectors
891 void Finalize()
892 {
893 ULong64_t totSize = 0;
894 for (auto &coll : fColls)
895 totSize += coll->size();
896 auto rColl = fColls[0];
897 rColl->reserve(totSize);
898 for (unsigned int i = 1; i < fColls.size(); ++i) {
899 auto &coll = fColls[i];
900 rColl->insert(rColl->end(), coll->begin(), coll->end());
901 }
902 }
903
904 std::vector<T> &PartialUpdate(unsigned int slot) { return *fColls[slot]; }
905
906 std::string GetActionName() { return "Take"; }
907
908 TakeHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
909 {
910 auto &result = *static_cast<std::shared_ptr<std::vector<T>> *>(newResult);
911 result->clear();
912 return TakeHelper(result, fColls.size());
913 }
914};
915
916// Case 3.: The column is a RVec, the collection is not a vector
917// No optimisations, transformations from RVecs to vectors
918template <typename RealT_t, typename COLL>
920 : public RActionImpl<TakeHelper<RealT_t, RVec<RealT_t>, COLL>> {
922
923public:
924 using ColumnTypes_t = TypeList<RVec<RealT_t>>;
925 TakeHelper(const std::shared_ptr<COLL> &resultColl, const unsigned int nSlots)
926 {
927 fColls.emplace_back(resultColl);
928 for (unsigned int i = 1; i < nSlots; ++i)
929 fColls.emplace_back(std::make_shared<COLL>());
930 }
932 TakeHelper(const TakeHelper &) = delete;
933
934 void InitTask(TTreeReader *, unsigned int) {}
935
936 void Exec(unsigned int slot, RVec<RealT_t> av) { fColls[slot]->emplace_back(av.begin(), av.end()); }
937
938 void Initialize() { /* noop */}
939
940 void Finalize()
941 {
942 auto rColl = fColls[0];
943 for (unsigned int i = 1; i < fColls.size(); ++i) {
944 auto &coll = fColls[i];
945 for (auto &v : *coll) {
946 rColl->emplace_back(v);
947 }
948 }
949 }
950
951 std::string GetActionName() { return "Take"; }
952
953 TakeHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
954 {
955 auto &result = *static_cast<std::shared_ptr<COLL> *>(newResult);
956 result->clear();
957 return TakeHelper(result, fColls.size());
958 }
959};
960
961// Case 4.: The column is an RVec, the collection is a vector
962// Optimisations, transformations from RVecs to vectors
963template <typename RealT_t>
964class R__CLING_PTRCHECK(off) TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>>
965 : public RActionImpl<TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>>> {
966
968
969public:
970 using ColumnTypes_t = TypeList<RVec<RealT_t>>;
971 TakeHelper(const std::shared_ptr<std::vector<std::vector<RealT_t>>> &resultColl, const unsigned int nSlots)
972 {
973 fColls.emplace_back(resultColl);
974 for (unsigned int i = 1; i < nSlots; ++i) {
975 auto v = std::make_shared<std::vector<RealT_t>>();
976 v->reserve(1024);
977 fColls.emplace_back(v);
978 }
979 }
981 TakeHelper(const TakeHelper &) = delete;
982
983 void InitTask(TTreeReader *, unsigned int) {}
984
985 void Exec(unsigned int slot, RVec<RealT_t> av) { fColls[slot]->emplace_back(av.begin(), av.end()); }
986
987 void Initialize() { /* noop */}
988
989 // This is optimised to treat vectors
990 void Finalize()
991 {
992 ULong64_t totSize = 0;
993 for (auto &coll : fColls)
994 totSize += coll->size();
995 auto rColl = fColls[0];
996 rColl->reserve(totSize);
997 for (unsigned int i = 1; i < fColls.size(); ++i) {
998 auto &coll = fColls[i];
999 rColl->insert(rColl->end(), coll->begin(), coll->end());
1000 }
1001 }
1002
1003 std::string GetActionName() { return "Take"; }
1004
1005 TakeHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1006 {
1007 auto &result = *static_cast<typename decltype(fColls)::value_type *>(newResult);
1008 result->clear();
1009 return TakeHelper(result, fColls.size());
1010 }
1011};
1012
1013// Extern templates for TakeHelper
1014// NOTE: The move-constructor of specializations declared as extern templates
1015// must be defined out of line, otherwise cling fails to find its symbol.
1016template <typename RealT_t, typename T, typename COLL>
1018template <typename RealT_t, typename T>
1020template <typename RealT_t, typename COLL>
1022template <typename RealT_t>
1023TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>>::TakeHelper(TakeHelper<RealT_t, RVec<RealT_t>, std::vector<RealT_t>> &&) = default;
1024
1025// External templates are disabled for gcc5 since this version wrongly omits the C++11 ABI attribute
1026#if __GNUC__ > 5
1027extern template class TakeHelper<bool, bool, std::vector<bool>>;
1031extern template class TakeHelper<int, int, std::vector<int>>;
1032extern template class TakeHelper<long, long, std::vector<long>>;
1034extern template class TakeHelper<float, float, std::vector<float>>;
1036#endif
1037
1038template <typename ResultType>
1039class R__CLING_PTRCHECK(off) MinHelper : public RActionImpl<MinHelper<ResultType>> {
1040 std::shared_ptr<ResultType> fResultMin;
1042
1043public:
1044 MinHelper(MinHelper &&) = default;
1045 MinHelper(const std::shared_ptr<ResultType> &minVPtr, const unsigned int nSlots)
1046 : fResultMin(minVPtr), fMins(nSlots, std::numeric_limits<ResultType>::max())
1047 {
1048 }
1049
1050 void Exec(unsigned int slot, ResultType v) { fMins[slot] = std::min(v, fMins[slot]); }
1051
1052 void InitTask(TTreeReader *, unsigned int) {}
1053
1055 void Exec(unsigned int slot, const T &vs)
1056 {
1057 for (auto &&v : vs)
1058 fMins[slot] = std::min(static_cast<ResultType>(v), fMins[slot]);
1059 }
1060
1061 void Initialize() { /* noop */}
1062
1063 void Finalize()
1064 {
1065 *fResultMin = std::numeric_limits<ResultType>::max();
1066 for (auto &m : fMins)
1067 *fResultMin = std::min(m, *fResultMin);
1068 }
1069
1070 // Helper functions for RMergeableValue
1071 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1072 {
1073 return std::make_unique<RMergeableMin<ResultType>>(*fResultMin);
1074 }
1075
1076 ResultType &PartialUpdate(unsigned int slot) { return fMins[slot]; }
1077
1078 std::string GetActionName() { return "Min"; }
1079
1080 MinHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1081 {
1082 auto &result = *static_cast<std::shared_ptr<ResultType> *>(newResult);
1083 return MinHelper(result, fMins.size());
1084 }
1085};
1086
1087template <typename ResultType>
1088class R__CLING_PTRCHECK(off) MaxHelper : public RActionImpl<MaxHelper<ResultType>> {
1089 std::shared_ptr<ResultType> fResultMax;
1091
1092public:
1093 MaxHelper(MaxHelper &&) = default;
1094 MaxHelper(const MaxHelper &) = delete;
1095 MaxHelper(const std::shared_ptr<ResultType> &maxVPtr, const unsigned int nSlots)
1096 : fResultMax(maxVPtr), fMaxs(nSlots, std::numeric_limits<ResultType>::lowest())
1097 {
1098 }
1099
1100 void InitTask(TTreeReader *, unsigned int) {}
1101 void Exec(unsigned int slot, ResultType v) { fMaxs[slot] = std::max(v, fMaxs[slot]); }
1102
1104 void Exec(unsigned int slot, const T &vs)
1105 {
1106 for (auto &&v : vs)
1107 fMaxs[slot] = std::max(static_cast<ResultType>(v), fMaxs[slot]);
1108 }
1109
1110 void Initialize() { /* noop */}
1111
1112 void Finalize()
1113 {
1114 *fResultMax = std::numeric_limits<ResultType>::lowest();
1115 for (auto &m : fMaxs) {
1116 *fResultMax = std::max(m, *fResultMax);
1117 }
1118 }
1119
1120 // Helper functions for RMergeableValue
1121 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1122 {
1123 return std::make_unique<RMergeableMax<ResultType>>(*fResultMax);
1124 }
1125
1126 ResultType &PartialUpdate(unsigned int slot) { return fMaxs[slot]; }
1127
1128 std::string GetActionName() { return "Max"; }
1129
1130 MaxHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1131 {
1132 auto &result = *static_cast<std::shared_ptr<ResultType> *>(newResult);
1133 return MaxHelper(result, fMaxs.size());
1134 }
1135};
1136
1137template <typename ResultType>
1138class R__CLING_PTRCHECK(off) SumHelper : public RActionImpl<SumHelper<ResultType>> {
1139 std::shared_ptr<ResultType> fResultSum;
1142
1143 /// Evaluate neutral element for this type and the sum operation.
1144 /// This is assumed to be any_value - any_value if operator- is defined
1145 /// for the type, otherwise a default-constructed ResultType{} is used.
1146 template <typename T = ResultType>
1147 auto NeutralElement(const T &v, int /*overloadresolver*/) -> decltype(v - v)
1148 {
1149 return v - v;
1150 }
1151
1152 template <typename T = ResultType, typename Dummy = int>
1153 ResultType NeutralElement(const T &, Dummy) // this overload has lower priority thanks to the template arg
1154 {
1155 return ResultType{};
1156 }
1157
1158public:
1159 SumHelper(SumHelper &&) = default;
1160 SumHelper(const SumHelper &) = delete;
1161 SumHelper(const std::shared_ptr<ResultType> &sumVPtr, const unsigned int nSlots)
1164 {
1165 }
1166 void InitTask(TTreeReader *, unsigned int) {}
1167
1168 void Exec(unsigned int slot, ResultType x)
1169 {
1170 // Kahan Sum:
1172 ResultType t = fSums[slot] + y;
1173 fCompensations[slot] = (t - fSums[slot]) - y;
1174 fSums[slot] = t;
1175 }
1176
1178 void Exec(unsigned int slot, const T &vs)
1179 {
1180 for (auto &&v : vs) {
1181 Exec(slot, v);
1182 }
1183 }
1184
1185 void Initialize() { /* noop */}
1186
1187 void Finalize()
1188 {
1193 for (auto &m : fSums) {
1194 // Kahan Sum:
1195 y = m - compensation;
1196 t = sum + y;
1197 compensation = (t - sum) - y;
1198 sum = t;
1199 }
1200 *fResultSum += sum;
1201 }
1202
1203 // Helper functions for RMergeableValue
1204 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1205 {
1206 return std::make_unique<RMergeableSum<ResultType>>(*fResultSum);
1207 }
1208
1209 ResultType &PartialUpdate(unsigned int slot) { return fSums[slot]; }
1210
1211 std::string GetActionName() { return "Sum"; }
1212
1213 SumHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1214 {
1215 auto &result = *static_cast<std::shared_ptr<ResultType> *>(newResult);
1216 *result = NeutralElement(*result, -1);
1217 return SumHelper(result, fSums.size());
1218 }
1219};
1220
1221class R__CLING_PTRCHECK(off) MeanHelper : public RActionImpl<MeanHelper> {
1222 std::shared_ptr<double> fResultMean;
1223 std::vector<ULong64_t> fCounts;
1224 std::vector<double> fSums;
1225 std::vector<double> fPartialMeans;
1226 std::vector<double> fCompensations;
1227
1228public:
1229 MeanHelper(const std::shared_ptr<double> &meanVPtr, const unsigned int nSlots);
1230 MeanHelper(MeanHelper &&) = default;
1231 MeanHelper(const MeanHelper &) = delete;
1232 void InitTask(TTreeReader *, unsigned int) {}
1233 void Exec(unsigned int slot, double v);
1234
1236 void Exec(unsigned int slot, const T &vs)
1237 {
1238 for (auto &&v : vs) {
1239
1240 fCounts[slot]++;
1241 // Kahan Sum:
1242 double y = v - fCompensations[slot];
1243 double t = fSums[slot] + y;
1244 fCompensations[slot] = (t - fSums[slot]) - y;
1245 fSums[slot] = t;
1246 }
1247 }
1248
1249 void Initialize() { /* noop */}
1250
1251 void Finalize();
1252
1253 // Helper functions for RMergeableValue
1254 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1255 {
1256 const ULong64_t counts = std::accumulate(fCounts.begin(), fCounts.end(), 0ull);
1257 return std::make_unique<RMergeableMean>(*fResultMean, counts);
1258 }
1259
1260 double &PartialUpdate(unsigned int slot);
1261
1262 std::string GetActionName() { return "Mean"; }
1263
1264 MeanHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1265 {
1266 auto &result = *static_cast<std::shared_ptr<double> *>(newResult);
1267 return MeanHelper(result, fSums.size());
1268 }
1269};
1270
1271class R__CLING_PTRCHECK(off) StdDevHelper : public RActionImpl<StdDevHelper> {
1272 // Number of subsets of data
1273 unsigned int fNSlots;
1274 std::shared_ptr<double> fResultStdDev;
1275 // Number of element for each slot
1276 std::vector<ULong64_t> fCounts;
1277 // Mean of each slot
1278 std::vector<double> fMeans;
1279 // Squared distance from the mean
1280 std::vector<double> fDistancesfromMean;
1281
1282public:
1283 StdDevHelper(const std::shared_ptr<double> &meanVPtr, const unsigned int nSlots);
1284 StdDevHelper(StdDevHelper &&) = default;
1285 StdDevHelper(const StdDevHelper &) = delete;
1286 void InitTask(TTreeReader *, unsigned int) {}
1287 void Exec(unsigned int slot, double v);
1288
1290 void Exec(unsigned int slot, const T &vs)
1291 {
1292 for (auto &&v : vs) {
1293 Exec(slot, v);
1294 }
1295 }
1296
1297 void Initialize() { /* noop */}
1298
1299 void Finalize();
1300
1301 // Helper functions for RMergeableValue
1302 std::unique_ptr<RMergeableValueBase> GetMergeableValue() const final
1303 {
1304 const ULong64_t counts = std::accumulate(fCounts.begin(), fCounts.end(), 0ull);
1305 const Double_t mean =
1306 std::inner_product(fMeans.begin(), fMeans.end(), fCounts.begin(), 0.) / static_cast<Double_t>(counts);
1307 return std::make_unique<RMergeableStdDev>(*fResultStdDev, counts, mean);
1308 }
1309
1310 std::string GetActionName() { return "StdDev"; }
1311
1312 StdDevHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1313 {
1314 auto &result = *static_cast<std::shared_ptr<double> *>(newResult);
1315 return StdDevHelper(result, fCounts.size());
1316 }
1317};
1318
1319template <typename PrevNodeType>
1320class R__CLING_PTRCHECK(off) DisplayHelper : public RActionImpl<DisplayHelper<PrevNodeType>> {
1321private:
1323 std::shared_ptr<Display_t> fDisplayerHelper;
1324 std::shared_ptr<PrevNodeType> fPrevNode;
1325 size_t fEntriesToProcess;
1326
1327public:
1328 DisplayHelper(size_t nRows, const std::shared_ptr<Display_t> &d, const std::shared_ptr<PrevNodeType> &prevNode)
1329 : fDisplayerHelper(d), fPrevNode(prevNode), fEntriesToProcess(nRows)
1330 {
1331 }
1332 DisplayHelper(DisplayHelper &&) = default;
1333 DisplayHelper(const DisplayHelper &) = delete;
1334 void InitTask(TTreeReader *, unsigned int) {}
1335
1336 template <typename... Columns>
1337 void Exec(unsigned int, Columns &... columns)
1338 {
1339 if (fEntriesToProcess == 0)
1340 return;
1341
1342 fDisplayerHelper->AddRow(columns...);
1343 --fEntriesToProcess;
1344
1345 if (fEntriesToProcess == 0) {
1346 // No more entries to process. Send a one-time signal that this node
1347 // of the graph is done. It is important that the 'StopProcessing'
1348 // method is only called once from this helper, otherwise it would seem
1349 // like more than one operation has completed its work.
1350 fPrevNode->StopProcessing();
1351 }
1352 }
1353
1354 void Initialize() {}
1355
1356 void Finalize() {}
1357
1358 std::string GetActionName() { return "Display"; }
1359};
1360
1361template <typename Acc, typename Merge, typename R, typename T, typename U,
1362 bool MustCopyAssign = std::is_same<R, U>::value>
1364 : public RActionImpl<AggregateHelper<Acc, Merge, R, T, U, MustCopyAssign>> {
1366 Merge fMerge;
1367 std::shared_ptr<U> fResult;
1369
1370public:
1371 using ColumnTypes_t = TypeList<T>;
1372
1373 AggregateHelper(Acc &&f, Merge &&m, const std::shared_ptr<U> &result, const unsigned int nSlots)
1374 : fAggregate(std::move(f)), fMerge(std::move(m)), fResult(result), fAggregators(nSlots, *result)
1375 {
1376 }
1377
1378 AggregateHelper(Acc &f, Merge &m, const std::shared_ptr<U> &result, const unsigned int nSlots)
1379 : fAggregate(f), fMerge(m), fResult(result), fAggregators(nSlots, *result)
1380 {
1381 }
1382
1383 AggregateHelper(AggregateHelper &&) = default;
1384 AggregateHelper(const AggregateHelper &) = delete;
1385
1386 void InitTask(TTreeReader *, unsigned int) {}
1387
1388 template <bool MustCopyAssign_ = MustCopyAssign, std::enable_if_t<MustCopyAssign_, int> = 0>
1389 void Exec(unsigned int slot, const T &value)
1390 {
1392 }
1393
1394 template <bool MustCopyAssign_ = MustCopyAssign, std::enable_if_t<!MustCopyAssign_, int> = 0>
1395 void Exec(unsigned int slot, const T &value)
1396 {
1398 }
1399
1400 void Initialize() { /* noop */}
1401
1403 bool MergeAll = std::is_same<void, MergeRet>::value>
1404 std::enable_if_t<MergeAll, void> Finalize()
1405 {
1406 fMerge(fAggregators);
1407 *fResult = fAggregators[0];
1408 }
1409
1411 bool MergeTwoByTwo = std::is_same<U, MergeRet>::value>
1412 std::enable_if_t<MergeTwoByTwo, void> Finalize(...) // ... needed to let compiler distinguish overloads
1413 {
1414 for (const auto &acc : fAggregators)
1415 *fResult = fMerge(*fResult, acc);
1416 }
1417
1418 U &PartialUpdate(unsigned int slot) { return fAggregators[slot]; }
1419
1420 std::string GetActionName() { return "Aggregate"; }
1421
1422 AggregateHelper MakeNew(void *newResult, std::string_view /*variation*/ = "nominal")
1423 {
1424 auto &result = *static_cast<std::shared_ptr<U> *>(newResult);
1425 return AggregateHelper(fAggregate, fMerge, result, fAggregators.size());
1426 }
1427};
1428
1429} // end of NS RDF
1430} // end of NS Internal
1431} // end of NS ROOT
1432
1433/// \endcond
1434
1435#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
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.
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:1525
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:927
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 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:232
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:34
ROOT type_traits extensions.
Namespace for new ROOT classes and functions.
void Initialize(Bool_t useTMVAStyle=kTRUE)
Definition tmvaglob.cxx:176
TMarker m
Definition textangle.C:8
TLine l
Definition textangle.C:4
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2340