rtt-f030/components/rtgui/utils/perfect_hash/graph/py2dot

217 lines
7.3 KiB
Python

#!/usr/bin/env python
class Graph:
def __init__(self, N):
self.N = N # number of vertices
# maps a vertex number to the list of (vertices, edge value)
# to which it is connected by edges.
self.adjacent = [[] for n in xrange(N)]
def connect(self, vertex1, vertex2, edge_value):
"""
Connect 'vertex1' and 'vertex2' with an edge, with associated
value 'value'
"""
# Add vertices to each other's adjacent list
self.adjacent[vertex1].append( (vertex2, edge_value) )
self.adjacent[vertex2].append( (vertex1, edge_value) )
def check(self):
"""
See if vertex values add up to edge values (mod N).
"""
for vertex in xrange(self.N):
for neighbor, edge_value in self.adjacent[vertex]:
assert (self.vertex_values[vertex] +
self.vertex_values[neighbor]) % self.N == edge_value
def calc_tree_sizes(self):
"""
After running this method, the attribute size will contain a list,
which maps the vertices to the size of the tree that vertex belongs
to.
"""
visited = self.N * [-1] # -1 unvisited, otherwise the number of tree
treenum = 0
# Loop over all vertices, taking unvisited ones as roots.
for root in xrange(self.N):
if visited[root] >= 0:
continue
# explore tree starting at 'root'
# Stack of vertices to visit, a list of tuples (parent, vertex)
tovisit = [ (None, root) ]
while tovisit:
parent, vertex = tovisit.pop()
visited[vertex] = treenum
# Loop over adjacent vertices, but skip the vertex we arrived
# here from the first time it is encountered.
skip = True
for neighbor, edge_value in self.adjacent[vertex]:
if skip and neighbor == parent:
skip = False
continue
if visited[neighbor] >= 0:
# We visited here before, so the graph is cyclic.
exit('Hmm, graph is cyclic.')
tovisit.append( (vertex, neighbor) )
treenum += 1
# maps the tree number to number of vertices within that tree
treesizes = treenum * [0]
for tree in visited:
treesizes[tree] += 1
self.size = [treesizes[visited[v]] for v in xrange(self.N)]
if verbose:
freq = (self.N+1) * [0]
for size in treesizes:
freq[size] += 1
sys.stderr.write(' Size Trees\n')
for i, f in enumerate(freq):
if f:
sys.stderr.write('%5i %5i\n' % (i, f))
if i == minsize-1:
sys.stderr.write('--------------\n')
def write(self, fo, labels = False):
self.calc_tree_sizes()
fo.write('graph G {\n'
' size = "8,8";\n'
' edge [color="#ff0000"]\n')
if labels:
fo.write(' node [color="#a0e0ee", style=filled];\n')
for vertex, value in enumerate(self.vertex_values):
if self.size[vertex] < minsize: continue
fo.write(' { node [label="%i: %i"] v%i }\n' % (
vertex, value, vertex))
else:
fo.write(' node [color="#3377a0", label="",\n'
' style=filled, shape=circle]\n')
for vertex in xrange(self.N): # edges
if self.size[vertex] < minsize: continue
for neighbor, edge_value in self.adjacent[vertex]:
if neighbor > vertex: continue
fo.write(' v%i -- v%i%s;\n' %
(vertex, neighbor,
(' [label="%s: %i"]' % (K[edge_value], edge_value))
if labels else ''))
fo.write('}\n')
fo.close()
if __name__ == '__main__':
import sys
from optparse import OptionParser
usage = "usage: %prog [options] [PYCODE]"
description = """\
Given the python code for a perfect hash function which was generated by
perfect_hash.py, e.g. by '$ ../perfect_hash.py animals.txt >animals.py',
this program will create the graph which was used in determining the
perfect hash function. The input python code may also be given to stdin.
The output is saved as in the .dot format which is used by the Graphviz
tools (see http://www.graphviz.org/) to generate a picture of the graph.
"""
parser = OptionParser(usage = usage,
description = description,
prog = sys.argv[0])
parser.add_option("-l", "--labels",
action = "store_true",
help = "Be verbose")
parser.add_option("-m", "--minsize",
action = "store",
default = 1,
type = "int",
help = "Include only trees in the output which "
"have at least INT vertices. "
"Default is %default, i.e. all trees are "
"included within the output.",
metavar = "INT")
parser.add_option("-o", "--output",
action = "store",
help = "Specify output FILE explicitly. "
"Default, is stdout. ",
metavar = "FILE")
parser.add_option("-v", "--verbose",
action = "store_true",
help = "Be verbose")
options, args = parser.parse_args()
if options.minsize > 0:
minsize = options.minsize
else:
parser.error("minimal size of trees has to be larger than zero")
verbose = options.verbose
if len(args) > 1:
parser.error("incorrect number of arguments")
# --------------------- end parsing and checking -----------------------
if verbose:
sys.stderr.write('minsize (of trees): %i\n' % minsize)
sys.stderr.write('labels (in output): %s\n' % options.labels)
# ------------ input filehandle
if len(args)==1:
try:
fi = file(args[0])
except IOError :
exit("Error: Can't open `%s' for reading." % args[0])
else:
fi = sys.stdin
# ------------ read input, i.e. execute code
exec(fi.read())
# ------------ make graph
g = Graph(len(G))
g.vertex_values = G
for key, hashval in zip(K, H):
g.connect(hash_f(key, S1),
hash_f(key, S2),
hashval)
g.check()
# ------------ output filehandle
if options.output:
try:
fo = file(options.output, 'w')
except IOError :
exit("Error: Can't open `%s' for writing." % options.output)
else:
fo = sys.stdout
# ------------ write output, i.e. generate .dot output
g.write(fo, options.labels)
# Local Variables:
# mode: python
# End: