Logo ROOT  
Reference Guide
RDFGraphUtils.cxx
Go to the documentation of this file.
1// Author: Enrico Guiraud, Danilo Piparo, CERN, Massimo Tumolo Politecnico di Torino 08/2018
2
3/*************************************************************************
4 * Copyright (C) 1995-2020, 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
13
14#include <algorithm> // std::find
15
16namespace ROOT {
17namespace Internal {
18namespace RDF {
19namespace GraphDrawing {
20
21std::string GraphCreatorHelper::FromGraphLeafToDot(std::shared_ptr<GraphNode> leaf)
22{
23 // Only the mapping between node id and node label (i.e. name)
24 std::stringstream dotStringLabels;
25 // Representation of the relationships between nodes
26 std::stringstream dotStringGraph;
27
28 // Explore the graph bottom-up and store its dot representation.
29 while (leaf) {
30 dotStringLabels << "\t" << leaf->fCounter << " [label=\"" << leaf->fName << "\", style=\"filled\", fillcolor=\""
31 << leaf->fColor << "\", shape=\"" << leaf->fShape << "\"];\n";
32 if (leaf->fPrevNode) {
33 dotStringGraph << "\t" << leaf->fPrevNode->fCounter << " -> " << leaf->fCounter << ";\n";
34 }
35 leaf = leaf->fPrevNode;
36 }
37
38 return "digraph {\n" + dotStringLabels.str() + dotStringGraph.str() + "}";
39}
40
41std::string GraphCreatorHelper::FromGraphActionsToDot(std::vector<std::shared_ptr<GraphNode>> leaves)
42{
43 // Only the mapping between node id and node label (i.e. name)
44 std::stringstream dotStringLabels;
45 // Representation of the relationships between nodes
46 std::stringstream dotStringGraph;
47
48 for (auto leaf : leaves) {
49 while (leaf && !leaf->fIsExplored) {
50 dotStringLabels << "\t" << leaf->fCounter << " [label=\"" << leaf->fName
51 << "\", style=\"filled\", fillcolor=\"" << leaf->fColor << "\", shape=\"" << leaf->fShape
52 << "\"];\n";
53 if (leaf->fPrevNode) {
54 dotStringGraph << "\t" << leaf->fPrevNode->fCounter << " -> " << leaf->fCounter << ";\n";
55 }
56 // Multiple branches may share the same nodes. It is wrong to explore them more than once.
57 leaf->fIsExplored = true;
58 leaf = leaf->fPrevNode;
59 }
60 }
61 return "digraph {\n" + dotStringLabels.str() + dotStringGraph.str() + "}";
62}
63
65{
66 auto loopManager = rDataFrame.GetLoopManager();
67 // Jitting is triggered because nodes must not be empty at the time of the calling in order to draw the graph.
68 loopManager->Jit();
69
70 return RepresentGraph(loopManager);
71}
72
74{
75 const auto actions = loopManager->GetAllActions();
76 const auto edges = loopManager->GetGraphEdges();
77
78 std::vector<std::shared_ptr<GraphNode>> nodes;
79 nodes.reserve(actions.size() + edges.size());
80
81 for (auto *action : actions)
82 nodes.emplace_back(action->GetGraph(fVisitedMap));
83 for (auto *edge : edges)
84 nodes.emplace_back(edge->GetGraph(fVisitedMap));
85
86 return FromGraphActionsToDot(nodes);
87}
88
89std::shared_ptr<GraphNode> CreateDefineNode(const std::string &columnName,
90 const ROOT::Detail::RDF::RDefineBase *columnPtr,
91 std::unordered_map<void *, std::shared_ptr<GraphNode>> &visitedMap)
92{
93 // If there is already a node for this define (recognized by the custom column it is defining) return it. If there is
94 // not, return a new one.
95 auto duplicateDefineIt = visitedMap.find((void *)columnPtr);
96 if (duplicateDefineIt != visitedMap.end())
97 return duplicateDefineIt->second;
98
99 auto node = std::make_shared<GraphNode>("Define\n" + columnName);
100 node->SetDefine();
101 node->SetCounter(visitedMap.size());
102 visitedMap[(void *)columnPtr] = node;
103 return node;
104}
105
106std::shared_ptr<GraphNode> CreateFilterNode(const ROOT::Detail::RDF::RFilterBase *filterPtr,
107 std::unordered_map<void *, std::shared_ptr<GraphNode>> &visitedMap)
108{
109 // If there is already a node for this filter return it. If there is not, return a new one.
110 auto duplicateFilterIt = visitedMap.find((void *)filterPtr);
111 if (duplicateFilterIt != visitedMap.end()) {
112 duplicateFilterIt->second->SetIsNew(false);
113 return duplicateFilterIt->second;
114 }
115
116 auto node = std::make_shared<GraphNode>((filterPtr->HasName() ? filterPtr->GetName() : "Filter"));
117 node->SetFilter();
118 node->SetCounter(visitedMap.size());
119 visitedMap[(void *)filterPtr] = node;
120 return node;
121}
122
123std::shared_ptr<GraphNode> CreateRangeNode(const ROOT::Detail::RDF::RRangeBase *rangePtr,
124 std::unordered_map<void *, std::shared_ptr<GraphNode>> &visitedMap)
125{
126 // If there is already a node for this range return it. If there is not, return a new one.
127 auto duplicateRangeIt = visitedMap.find((void *)rangePtr);
128 if (duplicateRangeIt != visitedMap.end()) {
129 duplicateRangeIt->second->SetIsNew(false);
130 return duplicateRangeIt->second;
131 }
132
133 auto node = std::make_shared<GraphNode>("Range");
134 node->SetRange();
135 node->SetCounter(visitedMap.size());
136 visitedMap[(void *)rangePtr] = node;
137 return node;
138}
139
140std::shared_ptr<GraphNode> AddDefinesToGraph(std::shared_ptr<GraphNode> node, const RColumnRegister &colRegister,
141 const std::vector<std::string> &prevNodeDefines,
142 std::unordered_map<void *, std::shared_ptr<GraphNode>> &visitedMap)
143{
144 auto upmostNode = node;
145 const auto &defineNames = colRegister.GetNames();
146 const auto &defineMap = colRegister.GetColumns();
147 for (auto i = int(defineNames.size()) - 1; i >= 0; --i) { // walk backwards through the names of defined columns
148 const auto colName = defineNames[i];
149 const bool isAlias = defineMap.find(colName) == defineMap.end();
150 if (isAlias || IsInternalColumn(colName))
151 continue; // aliases appear in the list of defineNames but we don't support them yet
152 const bool isANewDefine =
153 std::find(prevNodeDefines.begin(), prevNodeDefines.end(), colName) == prevNodeDefines.end();
154 if (!isANewDefine)
155 break; // we walked back through all new defines, the rest is stuff that was already in the graph
156
157 // create a node for this new Define
158 auto defineNode = RDFGraphDrawing::CreateDefineNode(colName, defineMap.at(colName).get(), visitedMap);
159 upmostNode->SetPrevNode(defineNode);
160 upmostNode = defineNode;
161 }
162
163 return upmostNode;
164}
165
166} // namespace GraphDrawing
167} // namespace RDF
168} // namespace Internal
169} // namespace ROOT
std::string GetName() const
Definition: RFilterBase.cxx:39
The head node of a RDF computation graph.
std::vector< RDFInternal::RActionBase * > GetAllActions() const
Return all actions, either booked or already run.
std::vector< RNodeBase * > GetGraphEdges() const
Return all graph edges known to RLoopManager This includes Filters and Ranges but not Defines.
void Jit()
Add RDF nodes that require just-in-time compilation to the computation graph.
std::string FromGraphLeafToDot(std::shared_ptr< GraphNode > leaf)
Starting from any leaf (Action, Filter, Range) it draws the dot representation of the branch.
std::string FromGraphActionsToDot(std::vector< std::shared_ptr< GraphNode > > leaves)
Starting by an array of leaves, it draws the entire graph.
std::string RepresentGraph(ROOT::RDataFrame &rDataFrame)
Starting from the root node, prints the entire graph.
std::unordered_map< void *, std::shared_ptr< GraphNode > > fVisitedMap
Map to keep track of visited nodes when constructing the computation graph (SaveGraph)
Definition: GraphUtils.hxx:61
A binder for user-defined columns and aliases.
const RDefineBasePtrMap_t & GetColumns() const
Returns the list of the pointers to the defined columns.
ColumnNames_t GetNames() const
Returns the list of the names of the defined columns (Defines + Aliases)
RLoopManager * GetLoopManager() const
ROOT's RDataFrame offers a modern, high-level interface for analysis of data stored in TTree ,...
Definition: RDataFrame.hxx:40
std::shared_ptr< GraphNode > CreateFilterNode(const ROOT::Detail::RDF::RFilterBase *filterPtr, std::unordered_map< void *, std::shared_ptr< GraphNode > > &visitedMap)
std::shared_ptr< GraphNode > CreateRangeNode(const ROOT::Detail::RDF::RRangeBase *rangePtr, std::unordered_map< void *, std::shared_ptr< GraphNode > > &visitedMap)
std::shared_ptr< GraphNode > CreateDefineNode(const std::string &columnName, const ROOT::Detail::RDF::RDefineBase *columnPtr, std::unordered_map< void *, std::shared_ptr< GraphNode > > &visitedMap)
std::shared_ptr< GraphNode > AddDefinesToGraph(std::shared_ptr< GraphNode > node, const RDFInternal::RColumnRegister &colRegister, const std::vector< std::string > &prevNodeDefines, std::unordered_map< void *, std::shared_ptr< GraphNode > > &visitedMap)
bool IsInternalColumn(std::string_view colName)
Whether custom column with name colName is an "internal" column such as rdfentry_ or rdfslot_.
Definition: RDFUtils.cxx:368
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.