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