The Gaudi Framework  master (b9786168)
Loading...
Searching...
No Matches
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
15namespace concurrency {
16
17 //---------------------------------------------------------------------------
19
20 if ( node.m_modeConcurrent && node.m_modePromptDecision ) {
21
22 if ( !m_foundViolations )
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
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;
85 m_slot = m_slot->parentSlot;
86 m_foundEntryPoint = true;
87 }
88
89 // Examine all parents
90 for ( auto& parent : node.m_parents ) {
91 m_active = true;
92 m_foundEntryPoint = ( m_slot->parentSlot == nullptr );
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
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
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
virtual bool reply() const
Definition Validators.h:71
bool visit(DecisionNode &) override
virtual void visitParents(DecisionNode &)
const std::vector< DecisionNode * > & getParentDecisionHubs() const
Get all parent decision hubs.
const std::vector< DataNode * > & getOutputDataNodes() const
Get all supplier nodes.
bool visit(DecisionNode &) override
const unsigned int & getNodeIndex() const
Get node index.
const std::string & name() const
Get node name.
const std::vector< AlgorithmNode * > & getProducers() const
Get all data object producers.
const std::vector< ControlFlowNode * > & getDaughters() const
Get children nodes.
bool m_modeConcurrent
Whether all daughters will be evaluated concurrently or sequentially.
bool m_modePromptDecision
Whether to evaluate the hub decision ASA its child decisions allow to do that.
std::vector< DecisionNode * > m_parents
Direct parent nodes.
bool visit(DecisionNode &) override
bool visit(DataNode &) override
bool reply() const override
Definition Validators.h:97
void visitParents(DecisionNode &) override
bool on_stack(const AlgorithmNode &node) const
Definition Validators.h:198
std::map< unsigned int, std::vector< AlgorithmNode * > > m_scc
Definition Validators.h:205
std::unordered_map< AlgorithmNode *, std::pair< unsigned int, unsigned int > > m_lowlinks
Definition Validators.h:204
bool visit(AlgorithmNode &nodeAt) override
std::vector< AlgorithmNode * > m_stack
Definition Validators.h:206
std::ostringstream m_status
Definition Validators.h:208
Class representing an event slot.
Definition EventSlot.h:23