More compact graphs: do back edges before forward expansion.
This commit is contained in:
parent
226de0a42d
commit
daf1e229f7
1 changed files with 80 additions and 46 deletions
|
@ -1330,105 +1330,140 @@ def graph(self, **kwargs):
|
||||||
N = kwargs.get('node', 'o') # Node character
|
N = kwargs.get('node', 'o') # Node character
|
||||||
color = kwargs.get('color', True)
|
color = kwargs.get('color', True)
|
||||||
out = kwargs.get('out', ColorStream(sys.stdout, color=color))
|
out = kwargs.get('out', ColorStream(sys.stdout, color=color))
|
||||||
|
debug = kwargs.get('debug', False)
|
||||||
indent = kwargs.get('indent', 0)
|
indent = kwargs.get('indent', 0)
|
||||||
indent *= ' '
|
indent *= ' '
|
||||||
|
|
||||||
topo_order = self.topological_sort(reverse=True)
|
topo_order = self.topological_sort(reverse=True)
|
||||||
|
|
||||||
|
# Work on a clone so the spec is self contained (no incoming
|
||||||
|
# parent edges), and so we don't destroy this spec.
|
||||||
clone = self.copy()
|
clone = self.copy()
|
||||||
|
|
||||||
|
# Fast access to nodes in the spec.
|
||||||
nodes = clone.index()
|
nodes = clone.index()
|
||||||
|
frontier = []
|
||||||
|
|
||||||
def ordered_deps(node):
|
def ordered_deps(node):
|
||||||
deps = node.dependencies
|
deps = node.dependencies
|
||||||
return sorted((d for d in deps), reverse=True)
|
return sorted((d for d in deps), reverse=True)
|
||||||
|
|
||||||
frontier = []
|
|
||||||
|
|
||||||
debug = False
|
def back_edge(end, start, **kwargs):
|
||||||
|
|
||||||
def back_edge(end, start):
|
|
||||||
assert(end < start)
|
assert(end < start)
|
||||||
|
collapse = kwargs.get('collapse', True)
|
||||||
|
label = kwargs.get('label', '') # debug label
|
||||||
|
|
||||||
|
|
||||||
if (start - end) > 1:
|
if (start - end) > 1:
|
||||||
if debug:
|
# This part draws a long back edge.
|
||||||
out.write(" " * 80)
|
|
||||||
|
|
||||||
out.write(indent)
|
out.write(indent)
|
||||||
out.write("| " * (end + 1))
|
out.write("| " * (end + 1))
|
||||||
out.write("|_" * (start - end - 2))
|
out.write("|_" * (start - end - 2))
|
||||||
out.write("|/ ")
|
out.write("|/")
|
||||||
|
if collapse:
|
||||||
|
out.write(" ")
|
||||||
out.write("/ " * (len(frontier) - start))
|
out.write("/ " * (len(frontier) - start))
|
||||||
out.write("\n")
|
else:
|
||||||
|
out.write("| " * (len(frontier) - start))
|
||||||
|
|
||||||
if debug:
|
if debug:
|
||||||
out.write(" " * 80)
|
out.write(" " * 20)
|
||||||
|
out.write("%s %s" % (frontier, label))
|
||||||
|
|
||||||
|
out.write("\n")
|
||||||
|
|
||||||
|
# This part draws the final collapsing line
|
||||||
out.write(indent)
|
out.write(indent)
|
||||||
out.write("| " * end)
|
out.write("| " * end)
|
||||||
out.write("|/")
|
out.write("|/")
|
||||||
out.write("| " * (start - end - 1))
|
out.write("| " * (start - end - 1))
|
||||||
if (start - end) > 1:
|
if (start - end) > 1 or not collapse:
|
||||||
out.write("| " * (len(frontier) - start))
|
out.write("| " * (len(frontier) - start))
|
||||||
else:
|
else:
|
||||||
out.write(" /" * (len(frontier) - start))
|
out.write(" /" * (len(frontier) - start))
|
||||||
|
|
||||||
|
if debug:
|
||||||
|
out.write(" " * 20)
|
||||||
|
out.write("%s %s" % (frontier, label))
|
||||||
|
|
||||||
out.write("\n")
|
out.write("\n")
|
||||||
|
|
||||||
|
|
||||||
def connect_deps(i, deps):
|
def connect_deps(i, deps, **kwargs):
|
||||||
if len(deps) == 1 and deps in frontier:
|
if len(deps) == 1 and deps in frontier:
|
||||||
j = frontier.index(deps)
|
j = frontier.index(deps)
|
||||||
if j < i:
|
if j < i:
|
||||||
back_edge(j, i)
|
back_edge(j, i, **kwargs)
|
||||||
else:
|
else:
|
||||||
if i < j:
|
if i < j:
|
||||||
frontier.pop(j)
|
frontier.pop(j)
|
||||||
frontier.insert(i, deps)
|
frontier.insert(i, deps)
|
||||||
back_edge(i, j+1)
|
back_edge(i, j+1, **kwargs)
|
||||||
|
return True
|
||||||
|
|
||||||
elif deps:
|
elif deps:
|
||||||
frontier.insert(i, deps)
|
frontier.insert(i, deps)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def add_deps_to_frontier(node, i):
|
def add_deps_to_frontier(node, i):
|
||||||
deps = ordered_deps(node)
|
deps = ordered_deps(node)
|
||||||
connect_deps(i, deps)
|
connect_deps(i, deps, label="add_deps")
|
||||||
for d in deps:
|
for d in deps:
|
||||||
del nodes[d].dependents[node.name]
|
del nodes[d].dependents[node.name]
|
||||||
|
|
||||||
|
|
||||||
name = topo_order.pop()
|
def find(seq, predicate):
|
||||||
add_deps_to_frontier(nodes[name], 0)
|
for i, elt in enumerate(seq):
|
||||||
|
if predicate(elt):
|
||||||
|
return i
|
||||||
|
return -1
|
||||||
|
|
||||||
if debug:
|
|
||||||
out.write("%-80s" % frontier)
|
|
||||||
|
|
||||||
|
add_deps_to_frontier(self, 0)
|
||||||
out.write(indent)
|
out.write(indent)
|
||||||
out.write('%s %s\n' % (N, name))
|
out.write('%s %s\n' % (N, self.name))
|
||||||
|
topo_order.pop()
|
||||||
|
|
||||||
while topo_order:
|
while frontier:
|
||||||
if debug:
|
# Find an unexpanded part of frontier
|
||||||
out.write("%-80s" % frontier)
|
i = find(frontier, lambda f: len(f) > 1)
|
||||||
|
|
||||||
# Find last i, len(frontier[i]) > 1
|
|
||||||
i = len(frontier) - 1
|
|
||||||
for f in frontier[-1::-1]:
|
|
||||||
if len(f) > 1: break
|
|
||||||
i -= 1
|
|
||||||
|
|
||||||
# Expand frontier until there are enough columns for all children.
|
# Expand frontier until there are enough columns for all children.
|
||||||
if i >= 0:
|
if i >= 0:
|
||||||
|
# Do all back connections possible from this element
|
||||||
|
# before expanding.
|
||||||
|
back_connect = [d for d in frontier[i] if [d] in frontier[:i]]
|
||||||
|
for d in back_connect:
|
||||||
|
j = frontier.index([d])
|
||||||
|
frontier[i].remove(d)
|
||||||
|
connect_deps(i, [d], collapse=False, label="back_connect")
|
||||||
|
|
||||||
|
if not frontier[i]:
|
||||||
|
frontier.pop(i)
|
||||||
|
|
||||||
|
elif len(frontier[i]) > 1:
|
||||||
|
name = frontier[i].pop(0)
|
||||||
|
deps = [name]
|
||||||
|
|
||||||
out.write(indent)
|
out.write(indent)
|
||||||
out.write("| " * i)
|
out.write("| " * i)
|
||||||
out.write("|\ ")
|
out.write("|\ ")
|
||||||
out.write("\ " * (len(frontier) - i - 1))
|
out.write("\ " * (len(frontier) - i - 1))
|
||||||
out.write("\n")
|
out.write("\n")
|
||||||
|
|
||||||
name = frontier[i].pop(0)
|
connect_deps(i, deps, label="expansion")
|
||||||
deps = [name]
|
|
||||||
|
|
||||||
connect_deps(i, deps)
|
# Handle any back edges to the right
|
||||||
if i+1 < len(frontier) and len(frontier[i+1]) == 1:
|
j = i+1
|
||||||
deps = frontier.pop(i+1)
|
while j < len(frontier):
|
||||||
connect_deps(i+1, deps)
|
deps = frontier.pop(j)
|
||||||
|
# TODO: semantics of connect_deps are weird.
|
||||||
|
# TODO: false return means the popped item was put
|
||||||
|
# TODO: back & not connected.
|
||||||
|
if not connect_deps(j, deps, label="ending"):
|
||||||
|
j += 1
|
||||||
|
|
||||||
else:
|
else:
|
||||||
name = topo_order.pop()
|
name = topo_order.pop()
|
||||||
|
@ -1438,9 +1473,10 @@ def add_deps_to_frontier(node, i):
|
||||||
# the frontier. Since specs are single-rooted DAGs,
|
# the frontier. Since specs are single-rooted DAGs,
|
||||||
# the node is always there. If the graph had multiple
|
# the node is always there. If the graph had multiple
|
||||||
# roots, we'd need to handle that case case of a new root.
|
# roots, we'd need to handle that case case of a new root.
|
||||||
i, elt = next(f for f in enumerate(frontier) if name in f[1])
|
i = find(frontier, lambda f: name in f)
|
||||||
frontier.pop(i)
|
frontier.pop(i)
|
||||||
|
|
||||||
|
out.write(indent)
|
||||||
out.write("| " * i)
|
out.write("| " * i)
|
||||||
out.write("%s " % N)
|
out.write("%s " % N)
|
||||||
out.write("| " * (len(frontier) - i))
|
out.write("| " * (len(frontier) - i))
|
||||||
|
@ -1449,9 +1485,7 @@ def add_deps_to_frontier(node, i):
|
||||||
if node.dependencies:
|
if node.dependencies:
|
||||||
add_deps_to_frontier(node, i)
|
add_deps_to_frontier(node, i)
|
||||||
elif frontier:
|
elif frontier:
|
||||||
if debug:
|
out.write(indent)
|
||||||
out.write(" " * 80)
|
|
||||||
|
|
||||||
out.write("| " * i)
|
out.write("| " * i)
|
||||||
out.write(" /" * (len(frontier) - i))
|
out.write(" /" * (len(frontier) - i))
|
||||||
out.write("\n")
|
out.write("\n")
|
||||||
|
|
Loading…
Reference in a new issue