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