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