Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RActionSnapshot.hxx
Go to the documentation of this file.
1// Author: Vincenzo Eduardo Padulano CERN 06/2025
2
3/*************************************************************************
4 * Copyright (C) 1995-2025, Rene Brun and Fons Rademakers. *
5 * All rights reserved. *
6 * *
7 * For the licensing terms see $ROOTSYS/LICENSE. *
8 * For the list of contributors see $ROOTSYS/README/CREDITS. *
9 *************************************************************************/
10
11#ifndef ROOT_RACTIONSNAPSHOT
12#define ROOT_RACTIONSNAPSHOT
13
20
21#include <cstddef> // std::size_t
22#include <memory>
23#include <string>
24#include <vector>
25
26namespace ROOT::Internal::RDF {
27
28namespace GraphDrawing {
29std::shared_ptr<GraphNode> AddDefinesToGraph(std::shared_ptr<GraphNode> node, const RColumnRegister &colRegister,
30 const std::vector<std::string> &prevNodeDefines,
31 std::unordered_map<void *, std::shared_ptr<GraphNode>> &visitedMap);
32} // namespace GraphDrawing
33
34class SnapshotHelperWithVariations;
35
36template <typename Helper, typename PrevNode>
38
39 // Template needed to avoid dependency on ActionHelpers.hxx
40 Helper fHelper;
41
42 // If the PrevNode is a RJittedFilter, our collection of previous nodes will have to use the RFilterBase type:
43 // we'll have a RJittedFilter for the nominal case, but the others will be concrete filters.
44 using PrevNodeCommon_t = std::conditional_t<std::is_same_v<PrevNode, ROOT::Detail::RDF::RJittedFilter>,
46 /// Previous nodes in the computation graph. First element is nominal, others are varied.
47 std::vector<std::shared_ptr<PrevNodeCommon_t>> fPrevNodes;
48
49 /// Column readers per slot and per input column
50 std::vector<std::vector<RColumnReaderBase *>> fValues;
51
52 /// The nth flag signals whether the nth input column is a custom column or not.
53 std::vector<bool> fIsDefine;
54
55 /// Types of the columns to Snapshot
56 std::vector<const std::type_info *> fColTypeIDs;
57
58 ROOT::RDF::SampleCallback_t GetSampleCallback() final { return fHelper.GetSampleCallback(); }
59
61 {
62 // This method only makes sense if we're appending the varied filters to the list after the nominal
63 assert(fPrevNodes.size() == 1);
64 const auto &currentVariations = GetVariations();
65
66 // If this node hangs from the RLoopManager itself, just use that as the upstream node for each variation
67 auto nominalPrevNode = fPrevNodes.begin();
68 if (static_cast<ROOT::Detail::RDF::RNodeBase *>(nominalPrevNode->get()) == fLoopManager) {
69 fPrevNodes.resize(1 + currentVariations.size(), *nominalPrevNode);
70 return;
71 }
72
73 // Otherwise, append one varied filter per variation
74 const auto &prevVariations = (*nominalPrevNode)->GetVariations();
75
76 fPrevNodes.reserve(1 + prevVariations.size());
77 // Get valid iterator after resizing
78 nominalPrevNode = fPrevNodes.begin();
79
80 // Need to populate parts of the computation graph for which we have empty shells, e.g. RJittedFilters
81 if (!currentVariations.empty())
82 fLoopManager->Jit();
83 for (const auto &variation : currentVariations) {
84 if (IsStrInVec(variation, prevVariations)) {
85 fPrevNodes.emplace_back(
86 std::static_pointer_cast<PrevNodeCommon_t>((*nominalPrevNode)->GetVariedFilter(variation)));
87 } else {
88 fPrevNodes.push_back(*nominalPrevNode);
89 }
90 }
91 }
92
93public:
94 RActionSnapshot(Helper &&h, const std::vector<std::string> &columns,
95 const std::vector<const std::type_info *> &colTypeIDs, std::shared_ptr<PrevNode> pd,
97 : RActionBase(pd->GetLoopManagerUnchecked(), columns, colRegister, pd->GetVariations()),
98 fHelper(std::move(h)),
99 fPrevNodes{std::static_pointer_cast<PrevNodeCommon_t>(pd)},
100 fValues(GetNSlots()),
101 fColTypeIDs(colTypeIDs)
102 {
103 fLoopManager->Register(this);
104
105 const auto nColumns = columns.size();
106 fIsDefine.reserve(nColumns);
107 for (auto i = 0u; i < nColumns; ++i)
108 fIsDefine.push_back(colRegister.IsDefineOrAlias(columns[i]));
109
110 if constexpr (std::is_same_v<Helper, SnapshotHelperWithVariations>) {
111 AppendVariedPrevNodes();
112 }
113 }
114
119
120 ~RActionSnapshot() final { fLoopManager->Deregister(this); }
121
122 /**
123 Retrieve a wrapper to the result of the action that knows how to merge
124 with others of the same type.
125 */
126 std::unique_ptr<ROOT::Detail::RDF::RMergeableValueBase> GetMergeableValue() const final
127 {
128 return fHelper.GetMergeableValue();
129 }
130
131 void Initialize() final { fHelper.Initialize(); }
132
133 void InitSlot(TTreeReader *r, unsigned int slot) final
134 {
135 fValues[slot] = GetUntypedColumnReaders(slot, r, RActionBase::GetColRegister(), *fLoopManager,
136 RActionBase::GetColumnNames(), fColTypeIDs);
137
138 if constexpr (std::is_same_v<Helper, SnapshotHelperWithVariations>) {
139 // In case of systematic variations, append also the varied column readers to the values
140 // that get passed to the helpers
141 auto const &variations = GetVariations();
142 for (unsigned int variationIndex = 0; variationIndex < variations.size(); ++variationIndex) {
143 auto const &readers =
144 GetUntypedColumnReaders(slot, r, RActionBase::GetColRegister(), *fLoopManager,
145 RActionBase::GetColumnNames(), fColTypeIDs, variations[variationIndex]);
146 for (unsigned int i = 0; i < readers.size(); ++i) {
147 if (fValues[slot][i] != readers[i]) {
148 // The reader with variations differs from nominal, so this column needs to be added to the output
149 fValues[slot].push_back(readers[i]);
150 // Both the original and the varied column need to be registered for masking
151 fHelper.RegisterVariedColumn(slot, i, i, 0,
152 "nominal"); // (No harm flagging the nominal multiple times)
153 fHelper.RegisterVariedColumn(slot, fValues[slot].size() - 1, i, variationIndex + 1,
154 variations[variationIndex]);
155 }
156 }
157 }
158 }
159
160 fHelper.InitTask(r, slot);
161 }
162
163 void *GetValue(unsigned int slot, std::size_t readerIdx, Long64_t entry)
164 {
165 assert(slot < fValues.size());
166 assert(readerIdx < fValues[slot].size());
167 if (auto *val = fValues[slot][readerIdx]->template TryGet<void>(entry))
168 return val;
169
170 throw std::out_of_range{"RDataFrame: Action (" + fHelper.GetActionName() +
171 ") could not retrieve value for column '" + fColumnNames[readerIdx] + "' for entry " +
172 std::to_string(entry) +
173 ". You can use the DefaultValueFor operation to provide a default value, or "
174 "FilterAvailable/FilterMissing to discard/keep entries with missing values instead."};
175 }
176
177 void CallExec(unsigned int slot, Long64_t entry)
178 {
179 std::vector<void *> untypedValues;
180 auto nReaders = fValues[slot].size();
181 untypedValues.reserve(nReaders);
182 for (decltype(nReaders) readerIdx{}; readerIdx < nReaders; readerIdx++)
183 untypedValues.push_back(GetValue(slot, readerIdx, entry));
184
185 fHelper.Exec(slot, untypedValues);
186 }
187
188 void Run(unsigned int slot, Long64_t entry) final
189 {
190 if constexpr (std::is_same_v<Helper, SnapshotHelperWithVariations>) {
191 // check if entry passes all filters
192 std::vector<bool> filterPassed(fPrevNodes.size(), false);
193 for (unsigned int variation = 0; variation < fPrevNodes.size(); ++variation) {
194 filterPassed[variation] = fPrevNodes[variation]->CheckFilters(slot, entry);
195 }
196
197 // Currently, every event where any of nominal or variations pass gets written to the output.
198 // This logic could be extended for different use cases if the need arises.
199 if (std::any_of(filterPassed.begin(), filterPassed.end(), [](bool val) { return val; })) {
200 // TODO: Don't allocate
201 std::vector<void *> untypedValues;
202 auto nReaders = fValues[slot].size();
203 untypedValues.reserve(nReaders);
204 for (decltype(nReaders) readerIdx{}; readerIdx < nReaders; readerIdx++)
205 untypedValues.push_back(GetValue(slot, readerIdx, entry));
206
207 fHelper.Exec(slot, untypedValues, filterPassed);
208 }
209 } else {
210 if (fPrevNodes.front()->CheckFilters(slot, entry))
211 CallExec(slot, entry);
212 }
213 }
214
216 {
217 for (auto const &node : fPrevNodes)
218 node->IncrChildrenCount();
219 }
220
221 /// Clean-up operations to be performed at the end of a task.
222 void FinalizeSlot(unsigned int slot) final
223 {
224 fValues[slot].clear();
225 fHelper.CallFinalizeTask(slot);
226 }
227
228 /// Clean-up and finalize the action result (e.g. merging slot-local results).
229 /// It invokes the helper's Finalize method.
231 {
232 fHelper.Finalize();
233 SetHasRun();
234 }
235
236 std::shared_ptr<GraphDrawing::GraphNode>
237 GetGraph(std::unordered_map<void *, std::shared_ptr<GraphDrawing::GraphNode>> &visitedMap) final
238 {
239 // Action nodes do not need to go through CreateFilterNode: they are never common nodes between multiple branches
240 const auto nodeType = HasRun() ? GraphDrawing::ENodeType::kUsedAction : GraphDrawing::ENodeType::kAction;
241 auto thisNode = std::make_shared<GraphDrawing::GraphNode>(fHelper.GetActionName(), visitedMap.size(), nodeType);
242 visitedMap[(void *)this] = thisNode;
243
244 for (auto const &node : fPrevNodes) {
245 auto prevNode = node->GetGraph(visitedMap);
246 const auto &prevColumns = prevNode->GetDefinedColumns();
247 auto upmostNode = AddDefinesToGraph(thisNode, GetColRegister(), prevColumns, visitedMap);
248
249 thisNode->AddDefinedColumns(GetColRegister().GenerateColumnNames());
250 upmostNode->SetPrevNode(prevNode);
251 }
252 return thisNode;
253 }
254
255 /// Forwards to the action helpers; will throw since PartialUpdate not supported for most snapshot helpers.
256 void *PartialUpdate(unsigned int slot) final { return fHelper.CallPartialUpdate(slot); }
257
258 /// Will throw, since varied actions are unsupported. Instead, set a flag in RSnapshotOptions.
259 [[maybe_unused]] std::unique_ptr<RActionBase> MakeVariedAction(std::vector<void *> && /*results*/) final
260 {
261 throw std::logic_error("RDataFrame::Snapshot: The snapshot action cannot be varied. Instead, switch on "
262 "variations in RSnapshotOptions.");
263 }
264
265 /**
266 * \brief Returns a new action with a cloned helper.
267 *
268 * \param[in] newResult The result to be filled by the new action (needed to clone the helper).
269 * \return A unique pointer to the new action.
270 */
271 std::unique_ptr<RActionBase> CloneAction(void *newResult) final
272 {
273 return std::make_unique<RActionSnapshot>(fHelper.CallMakeNew(newResult), GetColumnNames(), fColTypeIDs,
274 std::static_pointer_cast<PrevNode>(fPrevNodes.front()),
275 GetColRegister());
276 }
277};
278
279} // namespace ROOT::Internal::RDF
280
281#endif // ROOT_RACTIONSNAPSHOT
#define h(i)
Definition RSha256.hxx:106
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
long long Long64_t
Portable signed long integer 8 bytes.
Definition RtypesCore.h:83
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 r
Base class for non-leaf nodes of the computational graph.
Definition RNodeBase.hxx:43
RActionSnapshot & operator=(const RActionSnapshot &)=delete
void CallExec(unsigned int slot, Long64_t entry)
ROOT::RDF::SampleCallback_t GetSampleCallback() final
std::unique_ptr< RActionBase > MakeVariedAction(std::vector< void * > &&) final
Will throw, since varied actions are unsupported. Instead, set a flag in RSnapshotOptions.
std::vector< bool > fIsDefine
The nth flag signals whether the nth input column is a custom column or not.
std::vector< std::vector< RColumnReaderBase * > > fValues
Column readers per slot and per input column.
void InitSlot(TTreeReader *r, unsigned int slot) final
void * PartialUpdate(unsigned int slot) final
Forwards to the action helpers; will throw since PartialUpdate not supported for most snapshot helper...
void FinalizeSlot(unsigned int slot) final
Clean-up operations to be performed at the end of a task.
std::conditional_t< std::is_same_v< PrevNode, ROOT::Detail::RDF::RJittedFilter >, ROOT::Detail::RDF::RFilterBase, PrevNode > PrevNodeCommon_t
std::unique_ptr< RActionBase > CloneAction(void *newResult) final
Returns a new action with a cloned helper.
RActionSnapshot(Helper &&h, const std::vector< std::string > &columns, const std::vector< const std::type_info * > &colTypeIDs, std::shared_ptr< PrevNode > pd, const RColumnRegister &colRegister)
std::vector< std::shared_ptr< PrevNodeCommon_t > > fPrevNodes
Previous nodes in the computation graph. First element is nominal, others are varied.
void * GetValue(unsigned int slot, std::size_t readerIdx, Long64_t entry)
RActionSnapshot(RActionSnapshot &&)=delete
std::vector< const std::type_info * > fColTypeIDs
Types of the columns to Snapshot.
void Finalize() final
Clean-up and finalize the action result (e.g.
std::unique_ptr< ROOT::Detail::RDF::RMergeableValueBase > GetMergeableValue() const final
Retrieve a wrapper to the result of the action that knows how to merge with others of the same type.
std::shared_ptr< GraphDrawing::GraphNode > GetGraph(std::unordered_map< void *, std::shared_ptr< GraphDrawing::GraphNode > > &visitedMap) final
RActionSnapshot(const RActionSnapshot &)=delete
RActionSnapshot & operator=(RActionSnapshot &&)=delete
void Run(unsigned int slot, Long64_t entry) final
A binder for user-defined columns, variations and aliases.
const_iterator begin() const
const_iterator end() const
A simple, robust and fast interface to read values from ROOT columnar datasets such as TTree,...
Definition TTreeReader.h:46
std::shared_ptr< GraphNode > AddDefinesToGraph(std::shared_ptr< GraphNode > node, const RColumnRegister &colRegister, const std::vector< std::string > &prevNodeDefines, std::unordered_map< void *, std::shared_ptr< GraphNode > > &visitedMap)
unsigned int GetNSlots()
Definition RDFUtils.cxx:384
std::vector< RDFDetail::RColumnReaderBase * > GetUntypedColumnReaders(unsigned int slot, TTreeReader *treeReader, ROOT::Internal::RDF::RColumnRegister &colRegister, ROOT::Detail::RDF::RLoopManager &lm, const std::vector< std::string > &colNames, const std::vector< const std::type_info * > &colTypeIDs, const std::string &variationName="nominal")
std::function< void(unsigned int, const ROOT::RDF::RSampleInfo &)> SampleCallback_t
The type of a data-block callback, registered with an RDataFrame computation graph via e....