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