The Gaudi Framework  master (e68eea06)
Loading...
Searching...
No Matches
ControlFlow.py
Go to the documentation of this file.
11"""
12Classes for the implementation of the Control Flow Structure Syntax.
13
14@see: https://github.com/lhcb/scheduling-event-model/tree/master/controlflow_syntax
15"""
16
17
18class ControlFlowNode(object):
19 """
20 Basic entry in the control flow graph.
21 """
22
23 def __and__(self, rhs):
24 if rhs is CFTrue:
25 return self
26 elif rhs is CFFalse:
27 return CFFalse
28 return AndNode(self, rhs)
29
30 def __or__(self, rhs):
31 if rhs is CFFalse:
32 return self
33 elif rhs is CFTrue:
34 return CFTrue
35 return OrNode(self, rhs)
36
37 def __invert__(self):
38 return InvertNode(self)
39
40 def __rshift__(self, rhs):
41 return OrderedNode(self, rhs)
42
43 def visitNode(self, visitor):
44 visitor.enter(self)
45 self._visitSubNodes(visitor)
46 visitor.leave(self)
47
48 def _visitSubNodes(self, visitor):
49 pass
50
51 def __eq__(self, other):
52 return repr(self) == repr(other)
53
54 def __hash__(self):
55 """Return a unique identifier for this object.
56
57 As we use the `repr` of this object to check for equality, we use it
58 here to define uniqueness.
59 """
60 # The hash of the 1-tuple containing the repr of this object
61 return hash((repr(self),))
62
63 def getFullName(self):
64 """
65 Allow use of an expression as an algorihtm/sequence in a Gaudi job
66 configuration.
67
68 Convert the expression in nested sequencers and return the full name of
69 the top one.
70 """
71 if not hasattr(self, "_fullname"):
72 from GaudiKernel.Configurable import makeSequences
73
74 self._fullname = makeSequences(self).getFullName()
75 return self._fullname
76
77
79 """
80 Class used to identify a note without sub-nodes.
81 """
82
83 pass
84
85
86class ControlFlowBool(ControlFlowLeaf):
87 def __init__(self, value):
88 self.value = value
89
90 def __and__(self, rhs):
91 return rhs if self.value else self
92
93 def __or__(self, rhs):
94 return self if self.value else rhs
95
96 def __invert__(self):
97 return CFFalse if self.value else CFTrue
98
99 def __repr__(self):
100 return "CFTrue" if self.value else "CFFalse"
101
102
103CFTrue = ControlFlowBool(True)
104CFFalse = ControlFlowBool(False)
105del ControlFlowBool
106
107
109 """
110 Represent order of execution of nodes.
111 """
112
113 def __init__(self, lhs, rhs):
114 self.lhs = lhs
115 self.rhs = rhs
116
117 def __repr__(self):
118 return "(%r >> %r)" % (self.lhs, self.rhs)
119
120 def _visitSubNodes(self, visitor):
121 self.lhs.visitNode(visitor)
122 self.rhs.visitNode(visitor)
123
124
126 """
127 And operation between control flow nodes.
128 """
129
130 def __init__(self, lhs, rhs):
131 self.lhs = lhs
132 self.rhs = rhs
133
134 def __repr__(self):
135 return "(%r & %r)" % (self.lhs, self.rhs)
136
137 def _visitSubNodes(self, visitor):
138 self.lhs.visitNode(visitor)
139 self.rhs.visitNode(visitor)
140
141
143 """
144 Or operation between control flow nodes.
145 """
146
147 def __init__(self, lhs, rhs):
148 self.lhs = lhs
149 self.rhs = rhs
150
151 def __repr__(self):
152 return "(%r | %r)" % (self.lhs, self.rhs)
153
154 def _visitSubNodes(self, visitor):
155 self.lhs.visitNode(visitor)
156 self.rhs.visitNode(visitor)
157
158
160 """
161 Invert logic (negation) of a control flow node.
162 """
163
164 def __init__(self, item):
165 self.item = item
166
167 def __repr__(self):
168 return "~%r" % self.item
169
170 def _visitSubNodes(self, visitor):
171 self.item.visitNode(visitor)
172
173
175 """
176 Treat a control flow node as always successful, equivalent to (a | ~ a).
177 """
178
179 def __init__(self, item):
180 self.item = item
181
182 def __repr__(self):
183 return "ignore(%r)" % self.item
184
185 def _visitSubNodes(self, visitor):
186 self.item.visitNode(visitor)
187
188
190 def __init__(self, item):
191 self.item = item
192
193 def __repr__(self):
194 return "par(%r)" % self.item
195
196 def _visitSubNodes(self, visitor):
197 self.item.visitNode(visitor)
198
199
201 def __init__(self, item):
202 self.item = item
203
204 def __repr__(self):
205 return "seq(%r)" % self.item
206
207 def _visitSubNodes(self, visitor):
208 self.item.visitNode(visitor)
209
210
211class line(object):
212 def __init__(self, name, item):
213 self.name = name
214 self.item = item
215
216 def __repr__(self):
217 return "line(%r, %r)" % (self.name, self.item)
218
219 def _visitSubNodes(self, visitor):
220 self.item.visitNode(visitor)
221
222
223class _TestVisitor(object):
224 def __init__(self):
225 self.depths = 0
226
227 def enter(self, visitee):
228 self.depths += 1
229 print("%sEntering %s" % (self.depths * " ", type(visitee)))
230 if isinstance(visitee, ControlFlowLeaf):
231 print("%s Algorithm name: %s" % (" " * self.depths, visitee))
232
233 def leave(self, visitee):
234 print("%sLeaving %s" % (self.depths * " ", type(visitee)))
235 self.depths -= 1
236
237
239 def __init__(self, name):
240 self._name = name
241
242 def __repr__(self):
243 return self._name
244
245 def name(self):
246 return self._name
247
248
249class DotVisitor(object):
250 def __init__(self):
251 self.nodes = []
252 self.edges = []
253 self.number = 0
254 self.stack = []
255 self.ids = {}
256
257 def enter(self, visitee):
258 if visitee not in self.ids:
259 self.number += 1
260 dot_id = self.ids[visitee] = "T%s" % self.number
261 dot_id = self.ids[visitee]
262 mother = None
263 if self.is_needed(visitee):
264 if isinstance(visitee, ControlFlowLeaf):
265 entry = '%s [label="%s", shape=box]' % (dot_id, visitee.name())
266 elif isinstance(visitee, OrNode):
267 entry = '%s [label="OR", shape=invhouse]' % dot_id
268 elif isinstance(visitee, AndNode):
269 entry = '%s [label="AND", shape=invhouse]' % dot_id
270 elif isinstance(visitee, OrderedNode):
271 entry = '%s [label=">>", shape=point]' % dot_id
272 elif isinstance(visitee, InvertNode):
273 entry = '%s [label="NOT", shape=circle, color=red]' % dot_id
274 elif isinstance(visitee, par):
275 entry = '%s [label="PAR", shape=circle]' % dot_id
276 elif isinstance(visitee, seq):
277 entry = '%s [label="SEQ", shape=circle]' % dot_id
278 else:
279 entry = '%s [label="%s", shape=circle]' % (dot_id, type(visitee))
280 self.nodes.append(entry)
281 if len(self.stack) != 0:
282 mother = self.collapse_identical_ancestors(type(self.stack[-1][0]))
283 if not mother:
284 mother = self.stack[-1][1]
285 edge = "%s->%s" % (dot_id, mother)
286 self.edges.append(edge)
287 self.stack.append((visitee, dot_id))
288
289 def leave(self, visitee):
290 self.stack.pop()
291
293 """
294 If AND nodes are inside AND nodes, the graph could be simplified
295 to not contain those (same true for OR and ordered)
296 """
297 if len(self.stack) != 0:
298 mother = self.stack[-1][1]
299 for entry in self.stack[::-1]:
300 if not isinstance(entry[0], thetype):
301 break
302 mother = entry[1]
303 return mother
304 return None
305
306 def is_needed(self, visitee):
307 """
308 Check whether this node is actually needed
309 """
310 if len(self.stack) != 0:
311 return not isinstance(visitee, type(self.stack[-1][0]))
312 return True
313
314 def write(self, filename):
315 output = """
316digraph graphname {
317rankdir=LR
318
319%s
320
321%s
322
323}
324""" % (
325 "\n".join(self.nodes),
326 "\n".join(self.edges),
327 )
328
329 with open(filename, "w") as outfile:
330 outfile.write(output)
331
332
333def test():
334 Algorithm = _TestAlgorithm
335
336 a = Algorithm("a")
337 b = Algorithm("b")
338 c = Algorithm("c")
339 d = Algorithm("d")
340 e = Algorithm("e")
341 f = Algorithm("f")
342 g = Algorithm("g")
343 sequence = seq(b >> a >> f)
344 expression = sequence | ~c & par(d & e & g)
345 a = expression == expression
346 aLine = line("MyTriggerPath", expression)
347 visitor = _TestVisitor()
348 visitor2 = DotVisitor()
349 print("\nPrinting trigger line:")
350 print(aLine)
351 print("\nPrinting expression:")
352 print(expression)
353 print("\nTraversing through expression:\n")
354 expression.visitNode(visitor)
355 expression.visitNode(visitor2)
356 visitor2.write("out.dot")
Alias for backward compatibility.
Definition Algorithm.h:58
_visitSubNodes(self, visitor)
__init__(self, lhs, rhs)
(c) Copyright 1998-2023 CERN for the benefit of the LHCb and ATLAS collaborations # # This software i...
_visitSubNodes(self, visitor)
collapse_identical_ancestors(self, thetype)
is_needed(self, visitee)
enter(self, visitee)
write(self, filename)
leave(self, visitee)
_visitSubNodes(self, visitor)
_visitSubNodes(self, visitor)
__init__(self, lhs, rhs)
_visitSubNodes(self, visitor)
__init__(self, lhs, rhs)
_visitSubNodes(self, visitor)
__init__(self, item)
_visitSubNodes(self, visitor)
__init__(self, name, item)
__init__(self, item)
_visitSubNodes(self, visitor)
__init__(self, item)
_visitSubNodes(self, visitor)