The Gaudi Framework  master (37c0b60a)
Validators.cpp
Go to the documentation of this file.
1 /***********************************************************************************\
2 * (c) Copyright 1998-2019 CERN for the benefit of the LHCb and ATLAS collaborations *
3 * *
4 * This software is distributed under the terms of the Apache version 2 licence, *
5 * copied verbatim in the file "LICENSE". *
6 * *
7 * In applying this licence, CERN does not waive the privileges and immunities *
8 * granted to it by virtue of its status as an Intergovernmental Organization *
9 * or submit itself to any jurisdiction. *
10 \***********************************************************************************/
11 #include "Validators.h"
12 
13 #include <algorithm>
14 
15 namespace concurrency {
16 
17  //---------------------------------------------------------------------------
19 
20  if ( node.m_modeConcurrent && node.m_modePromptDecision ) {
21 
22  if ( !m_foundViolations )
23  m_status
24  << " 'Concurrent'/'Prompt' contradiction(s) found. Settings are mutually exclusive within a task group. "
25  "Discarding 'Prompt' for ";
26 
27  m_status << ( m_foundViolations ? ", " : "" ) << node.name();
28 
29  if ( !m_foundViolations ) m_foundViolations = true;
30 
31  return false;
32  }
33 
34  return true;
35  }
36 
37  //---------------------------------------------------------------------------
39 
40  // Test if this node is already resolved
41  if ( m_slot->controlFlowState[node.getNodeIndex()] != -1 ) {
42  m_active = false;
43  return m_active;
44  }
45 
46  // Test if the node that sent this scout is out-of-sequence in this node
47  if ( !node.m_modeConcurrent ) {
48 
49  for ( auto& child : node.getDaughters() ) {
50 
51  if ( child->name() == m_previousNodeName ) break;
52 
53  if ( m_slot->controlFlowState[child->getNodeIndex()] == -1 ) {
54  m_active = false;
55  return m_active;
56  }
57  }
58  }
59 
60  this->visitParents( node );
61 
62  return this->reply();
63  }
64 
65  //---------------------------------------------------------------------------
67 
68  for ( auto& parent : node.m_parents ) {
69  m_active = true;
70  m_previousNodeName = node.name();
71  parent->accept( *this );
72 
73  // Any active parent means that this node is active
74  if ( this->reply() ) break;
75  }
76  }
77 
78  //---------------------------------------------------------------------------
80 
81  // Leave a sub-slot if this is the exit node
82  const EventSlot* oldSlot = nullptr;
83  if ( m_slot->parentSlot && m_slot->entryPoint == node.name() ) {
84  oldSlot = m_slot;
86  m_foundEntryPoint = true;
87  }
88 
89  // Examine all parents
90  for ( auto& parent : node.m_parents ) {
91  m_active = true;
93  m_previousNodeName = node.name();
94  parent->accept( *this );
95 
96  // Any active parent means that this node is active
97  if ( this->reply() ) break;
98  }
99 
100  if ( oldSlot ) m_slot = oldSlot;
101  }
102 
103  //---------------------------------------------------------------------------
105 
106  auto propValidator = NodePropertiesValidator();
107  propValidator.visit( node );
108 
109  // check if the visitor found a conditional path
110  if ( node.m_modePromptDecision && propValidator.passed() ) {
111  m_positive = true;
112  return true;
113  }
114 
115  // check if the visitor found an unconditional path
116  if ( node.m_parents.empty() ) {
117  m_negative = true;
118  return true;
119  }
120 
121  for ( const auto& parent : node.m_parents ) {
122  this->visit( *parent );
123  // check if a node is on both conditional and unconditional branches
124  // and stop since further navigation won't change the conclusion
125  if ( this->positive() && this->negative() ) break;
126  }
127 
128  return true;
129  }
130 
131  //---------------------------------------------------------------------------
133 
134  for ( const auto& parent : node.getParentDecisionHubs() ) {
135  this->visit( *parent );
136  if ( this->positive() && this->negative() ) break;
137  }
138 
139  return true;
140  }
141 
142  //---------------------------------------------------------------------------
144 
145  if ( node.getProducers().size() > 1 ) {
146 
147  if ( !m_foundViolations ) m_foundViolations = true;
148 
149  auto scout = ConditionalLineageFinder();
150 
151  for ( const auto& producer : node.getProducers() ) {
152 
153  producer->accept( scout );
154 
155  if ( scout.negative() ) {
156  // register unconditional violation
157  auto pr = m_unconditionalProducers.try_emplace( &node );
158  pr.first->second.insert( producer );
159  } else {
160  // register conditional violation
161  auto pr = m_conditionalProducers.try_emplace( &node );
162  pr.first->second.insert( producer );
163  }
164 
165  scout.reset();
166  }
167  }
168 
169  return true;
170  }
171 
172  //---------------------------------------------------------------------------
174 
175  if ( node.getProducers().size() > 1 ) {
176 
177  if ( !m_foundViolations ) m_foundViolations = true;
178 
179  // all violations related to Condition Node are unconditional as
180  // Condition Algorithms are detached from the CF realm
181  for ( const auto& producer : node.getProducers() ) {
182  // register unconditional violation
183  auto pr = m_unconditionalProducers.try_emplace( &node );
184  pr.first->second.insert( producer );
185  }
186  }
187 
188  return true;
189  }
190 
191  //---------------------------------------------------------------------------
193 
194  std::ostringstream status{ " No topology violations found in the DF realm" };
195 
196  if ( m_foundViolations ) {
197  status << " Conditional (C) and/or unconditional (U) topology violations found in the DF realm:\n\n";
198 
199  for ( const auto& upr : m_unconditionalProducers ) {
200 
201  // add unconditional violations to the report
202  status << " (U): " << upr.first->name() << " <---- |";
203  for ( const auto& algo : upr.second )
204  status << " " << algo->name() << " (U)"
205  << " |";
206 
207  // add conditional violations to the report
208  auto result = m_conditionalProducers.find( upr.first );
209  if ( result != m_conditionalProducers.end() ) {
210  for ( const auto& algo : result->second )
211  status << " " << algo->name() << " (C)"
212  << " |";
213  }
214 
215  status << "\n";
216  }
217 
218  // add remaining conditional violations to the report
219  for ( const auto& cpr : m_conditionalProducers ) {
220 
221  if ( m_unconditionalProducers.find( cpr.first ) != m_unconditionalProducers.end() ) continue;
222 
223  status << " (C): " << cpr.first->name() << " <---- |";
224  for ( const auto& algo : cpr.second )
225  status << " " << algo->name() << " (C)"
226  << " |";
227  status << "\n";
228  }
229  }
230 
231  return status.str();
232  }
233 
234  //---------------------------------------------------------------------------
236 
237  // DFS ******************************************************
238 
239  // put the node on stack
240  m_stack.push_back( &nodeAt );
241 
242  // initialize and cache the low-link value of this node
243  m_nodes_count += 1;
244  unsigned int lowlink_init{ m_nodes_count };
245  // record initial low-link value of this node
246  m_lowlinks.insert( { &nodeAt, { lowlink_init, lowlink_init } } );
247  auto& lowlink = m_lowlinks[&nodeAt].second;
248 
249  for ( const auto& output : nodeAt.getOutputDataNodes() ) {
250  for ( const auto& consumer : output->getConsumers() ) {
251  consumer->accept( *this );
252  // DFS callback ******************************************
253  // propagate the low-link value back
254  if ( on_stack( *consumer ) ) {
255  if ( lowlink > m_lowlinks[consumer].second ) lowlink = m_lowlinks[consumer].second;
256  // this allows to detect loops (i.e., a cycle consisting of a single algorithm node: A->d->A),
257  // but there is a protection against this case at Algorithm::initialize()
258  if ( consumer == &nodeAt ) m_scc.insert( { lowlink, { &nodeAt } } );
259  }
260  }
261  }
262 
263  // If an SCC is found, book-keep it and take it off the stack
264  if ( lowlink_init == lowlink ) {
265  if ( m_scc.find( lowlink ) == m_scc.end() ) m_scc.insert( { lowlink, {} } );
266 
267  for ( auto stackNodeRIter = m_stack.rbegin(); stackNodeRIter != m_stack.rend(); ++stackNodeRIter ) {
268 
269  bool lastSCCNodeOnStack{ *stackNodeRIter == &nodeAt };
270  if ( lowlink == m_lowlinks[*stackNodeRIter].second ) m_scc[lowlink].push_back( *stackNodeRIter );
271 
272  m_stack.pop_back();
273  if ( lastSCCNodeOnStack ) break;
274  }
275  }
276 
277  return true;
278  }
279 
280  //---------------------------------------------------------------------------
282 
283  if ( !passed() ) {
284 
285  m_status << " Strongly connected components found in DF realm:";
286 
287  for ( auto& pr : m_scc ) {
288  if ( pr.second.size() > 1 ) {
289  m_status << "\n o [lowlink:" << std::to_string( pr.first ) << "] |";
290 
291  // rotate SCCs to get reproducible order
292  std::vector sortedSCC( pr.second.begin(), pr.second.end() );
293  std::sort( sortedSCC.begin(), sortedSCC.end(), CompareNodes<AlgorithmNode*>() );
294  std::rotate( pr.second.begin(), std::find( pr.second.begin(), pr.second.end(), sortedSCC.front() ),
295  pr.second.end() );
296 
297  for ( const auto& algoPtr : pr.second ) m_status << " " << algoPtr->name() << " |";
298  }
299  }
300  }
301 
302  return m_status.str();
303  }
304 
305 } // namespace concurrency
concurrency::SubSlotScout::m_foundEntryPoint
bool m_foundEntryPoint
Definition: Validators.h:103
concurrency::ProductionAmbiguityFinder::m_conditionalProducers
visitor_book m_conditionalProducers
Definition: Validators.h:159
std::string
STL class.
concurrency::ProductionAmbiguityFinder::m_foundViolations
bool m_foundViolations
Definition: Validators.h:155
concurrency::ConditionalLineageFinder::positive
bool positive() const
Definition: Validators.h:118
std::vector
STL class.
std::map::find
T find(T... args)
concurrency::NodePropertiesValidator::m_status
std::ostringstream m_status
Definition: Validators.h:47
EventSlot
Class representing an event slot.
Definition: EventSlot.h:24
concurrency::ActiveLineageScout::m_previousNodeName
std::string m_previousNodeName
Definition: Validators.h:80
concurrency::ControlFlowNode::name
const std::string & name() const
Get node name.
Definition: PrecedenceRulesGraph.h:429
concurrency::ActiveLineageScout::m_slot
const EventSlot * m_slot
Definition: Validators.h:77
concurrency::ActiveLineageScout::visit
bool visit(DecisionNode &) override
Definition: Validators.cpp:38
concurrency::AlgorithmNode::getOutputDataNodes
const std::vector< DataNode * > & getOutputDataNodes() const
Get all supplier nodes.
Definition: PrecedenceRulesGraph.h:508
std::rotate
T rotate(T... args)
concurrency::DecisionNode::m_modePromptDecision
bool m_modePromptDecision
Whether to evaluate the hub decision ASA its child decisions allow to do that.
Definition: PrecedenceRulesGraph.h:468
Gaudi::Units::second
constexpr double second
Definition: SystemOfUnits.h:139
concurrency::ActiveLineageScout::reply
virtual bool reply() const
Definition: Validators.h:72
gaudirun.output
output
Definition: gaudirun.py:521
concurrency::AlgorithmNode::getParentDecisionHubs
const std::vector< DecisionNode * > & getParentDecisionHubs() const
Get all parent decision hubs.
Definition: PrecedenceRulesGraph.h:501
concurrency::TarjanSCCFinder::visit
bool visit(AlgorithmNode &nodeAt) override
Definition: Validators.cpp:235
std::vector::front
T front(T... args)
std::sort
T sort(T... args)
concurrency::AlgorithmNode
Definition: PrecedenceRulesGraph.h:484
EventSlot::entryPoint
std::string entryPoint
Event Views bookkeeping (TODO: optimize view bookkeeping)
Definition: EventSlot.h:94
concurrency::TarjanSCCFinder::reply
std::string reply()
Definition: Validators.cpp:281
concurrency::TarjanSCCFinder::m_stack
std::vector< AlgorithmNode * > m_stack
Definition: Validators.h:207
concurrency::ConditionNode
Definition: PrecedenceRulesGraph.h:591
concurrency::ProductionAmbiguityFinder::visit
bool visit(DataNode &) override
Definition: Validators.cpp:143
concurrency::TarjanSCCFinder::m_lowlinks
std::unordered_map< AlgorithmNode *, std::pair< unsigned int, unsigned int > > m_lowlinks
Definition: Validators.h:205
concurrency::NodePropertiesValidator::m_foundViolations
bool m_foundViolations
Definition: Validators.h:48
EventSlot::parentSlot
EventSlot * parentSlot
Pointer to parent slot (null for top level)
Definition: EventSlot.h:96
concurrency::ProductionAmbiguityFinder::m_unconditionalProducers
visitor_book m_unconditionalProducers
Definition: Validators.h:160
concurrency::ConditionalLineageFinder::visit
bool visit(DecisionNode &) override
Definition: Validators.cpp:104
concurrency::ConditionalLineageFinder::negative
bool negative() const
Definition: Validators.h:119
concurrency::ConditionalLineageFinder
Definition: Validators.h:107
concurrency::DecisionNode::m_modeConcurrent
bool m_modeConcurrent
Whether all daughters will be evaluated concurrently or sequentially.
Definition: PrecedenceRulesGraph.h:465
std::to_string
T to_string(T... args)
GaudiPython.Bindings.nullptr
nullptr
Definition: Bindings.py:87
Validators.h
concurrency::ConditionalLineageFinder::m_positive
bool m_positive
Definition: Validators.h:127
concurrency::DecisionNode::getDaughters
const std::vector< ControlFlowNode * > & getDaughters() const
Get children nodes.
Definition: PrecedenceRulesGraph.h:459
concurrency::TarjanSCCFinder::on_stack
bool on_stack(const AlgorithmNode &node) const
Definition: Validators.h:199
concurrency
Definition: PrecedenceRulesGraph.cpp:38
concurrency::ActiveLineageScout::m_active
bool m_active
Definition: Validators.h:79
concurrency::DecisionNode
Definition: PrecedenceRulesGraph.h:439
concurrency::ActiveLineageScout::visitParents
virtual void visitParents(DecisionNode &)
Definition: Validators.cpp:66
std::ostringstream
STL class.
concurrency::TarjanSCCFinder::m_scc
std::map< unsigned int, std::vector< AlgorithmNode * > > m_scc
Definition: Validators.h:206
std::vector::begin
T begin(T... args)
concurrency::NodePropertiesValidator
Definition: Validators.h:28
std::map::insert
T insert(T... args)
concurrency::ConditionalLineageFinder::m_negative
bool m_negative
Definition: Validators.h:128
concurrency::TarjanSCCFinder::passed
bool passed() const
Definition: Validators.h:183
std::ostringstream::str
T str(T... args)
concurrency::DataNode
Definition: PrecedenceRulesGraph.h:553
concurrency::CompareNodes
Definition: PrecedenceRulesGraph.h:616
EventSlot::controlFlowState
std::vector< int > controlFlowState
State of the control flow.
Definition: EventSlot.h:87
std::map::end
T end(T... args)
concurrency::DecisionNode::m_parents
std::vector< DecisionNode * > m_parents
Direct parent nodes.
Definition: PrecedenceRulesGraph.h:478
concurrency::SubSlotScout::reply
bool reply() const override
Definition: Validators.h:98
concurrency::SubSlotScout::visitParents
void visitParents(DecisionNode &) override
Definition: Validators.cpp:79
concurrency::NodePropertiesValidator::visit
bool visit(DecisionNode &) override
Definition: Validators.cpp:18
concurrency::ProductionAmbiguityFinder::reply
std::string reply() const
Definition: Validators.cpp:192
concurrency::TarjanSCCFinder::m_status
std::ostringstream m_status
Definition: Validators.h:209
concurrency::TarjanSCCFinder::m_nodes_count
unsigned int m_nodes_count
Definition: Validators.h:203
concurrency::DataNode::getProducers
const std::vector< AlgorithmNode * > & getProducers() const
Get all data object producers.
Definition: PrecedenceRulesGraph.h:578
concurrency::ControlFlowNode::getNodeIndex
const unsigned int & getNodeIndex() const
Get node index.
Definition: PrecedenceRulesGraph.h:427