Edit on GitHub

pytact.scripts.web

Generate a bird-eye-view visualization of (part of) a dataset with Graphviz.

  1"""Generate a bird-eye-view visualization of (part of) a dataset with
  2Graphviz."""
  3
  4import graphviz
  5from pytact.data_reader import lowlevel_data_reader
  6import sys
  7import json
  8from pathlib import Path
  9from collections import Counter
 10import cmath
 11import colorsys
 12import math
 13
 14def get_node_id(gid, nid):
 15    prefix = f"{gid}-{nid}"
 16    return prefix
 17
 18def init_graph():
 19    g = graphviz.Graph(engine='sfdp')
 20    g.attr('node', shape='point', width='0.01', height='0.01')
 21    g.attr('edge', color="#0000ff10")
 22    g.attr(overlap='prism', outputorder='edgesfirst')
 23    g.attr(ratio='fill', size="20,10")
 24    return g
 25
 26def make_color(n, alpha):
 27    rgb = colorsys.hsv_to_rgb(n+(1/2), 1, 1)
 28    hexrgb = ["%0.2X"%int(v * 255) for v in list(rgb)+[alpha]]
 29    return f"#{hexrgb[0]}{hexrgb[1]}{hexrgb[2]}{hexrgb[3]}"
 30
 31def tooltip(n):
 32    if n.label.is_definition:
 33        return n.label.definition.name
 34    else:
 35        return n.label.which.name
 36
 37def main():
 38    with lowlevel_data_reader(Path(sys.argv[1])) as data:
 39
 40        subset = { p: gid for p, gid in data.graphid_by_filename.items()
 41                if Path('coq-tactician-stdlib.8.11.dev/theories/Init/') in p.parents }
 42
 43        # subset = { p: gid for p, gid in data.graphid_by_filename.items()
 44        #            if Path('coq-tactician-stdlib.8.11.dev/theories/Init/Logic.bin') == p }
 45
 46        print('Calculating node incoming count')
 47        incoming = Counter()
 48        for p, gid in subset.items():
 49            d = data[gid]
 50            for i, n in enumerate(d.graph.nodes):
 51                for ci in range(n.children_index, n.children_index + n.children_count):
 52                    target = d.graph.edges[ci].target
 53                    target_graph = data.local_to_global(gid, target.dep_index)
 54                    targetid = get_node_id(target_graph, target.node_index)
 55                    incoming[targetid] += 1
 56        incoming_max = incoming.most_common(1)[0][1]
 57
 58        print('Calculating node positions')
 59        g = init_graph()
 60        for p, gid in subset.items():
 61            d = data[gid]
 62            for i, n in enumerate(d.graph.nodes):
 63                sourceid = get_node_id(gid, i)
 64                g.node(sourceid, width=str(0.001*incoming[sourceid]), height=str(0.001*incoming[sourceid]))
 65                for ci in range(n.children_index, n.children_index + n.children_count):
 66                    target = d.graph.edges[ci].target
 67                    target_graph = data.local_to_global(gid, target.dep_index)
 68                    targetid = get_node_id(target_graph, target.node_index)
 69                    g.edge(sourceid, targetid)
 70                    # print(f"{sourceid},{targetid}")
 71        g.render(filename='web', format='json', view=False, cleanup=False)
 72
 73        print('Reading node positions')
 74        positions = dict()
 75        with open('web.json') as f:
 76            jsongraph = json.load(f)
 77            for node in jsongraph['objects']:
 78                position = node['pos'].split(',')
 79                position = complex(float(position[0]), float(position[1]))
 80                positions[node['name']] = position
 81
 82        print('Calculating largest distance')
 83        maxdist = 0
 84        for p, gid in subset.items():
 85            d = data[gid]
 86            for i, n in enumerate(d.graph.nodes):
 87                sourceid = get_node_id(gid, i)
 88                for ci in range(n.children_index, n.children_index + n.children_count):
 89                    target = d.graph.edges[ci].target
 90                    target_graph = data.local_to_global(gid, target.dep_index)
 91                    targetid = get_node_id(target_graph, target.node_index)
 92                    maxdist = max(maxdist, abs(positions[sourceid]-positions[targetid]))
 93        print(f'Max distance: {maxdist}')
 94
 95        print('Drawing final graph')
 96        g = init_graph()
 97        for p, gid in subset.items():
 98            d = data[gid]
 99            # print(p)
100            for i, n in enumerate(d.graph.nodes):
101                sourceid = get_node_id(gid, i)
102                g.node(sourceid, width=str(0.001*incoming[sourceid]), height=str(0.001*incoming[sourceid]),
103                    color=make_color((1/2)+(math.log(incoming[sourceid]+1)/math.log(incoming_max+1)), 1),
104                    tooltip=tooltip(n))
105                for ci in range(n.children_index, n.children_index + n.children_count):
106                    target = d.graph.edges[ci].target
107                    target_graph = data.local_to_global(gid, target.dep_index)
108                    targetid = get_node_id(target_graph, target.node_index)
109                    distance = abs(positions[sourceid]-positions[targetid])
110                    distance_norm = distance / maxdist
111                    g.edge(sourceid, targetid, color=make_color(distance_norm, 0.05))
112                    # print(f"{sourceid},{targetid}")
113        g.render(filename='web', format='svg', view=False, cleanup=False)
114
115# with data_reader(Path(sys.argv[1])) as data:
116
117#     g = graphviz.Graph(engine='sfdp')
118#     g.attr('node', shape='point', width='.1', height='.1')
119#     g.attr('edge', penwidth="0.1")
120#     g.attr(overlap='false')
121
122#     # print("source,target")
123
124#     for p, f in data.items():
125#         if Path('coq-tactician-stdlib.dev/theories/Init/') not in p.parents:
126#             continue
127#         for d in f:
128#             print(d.name)
129
130
131#     # g.render(filename='web', view=False, cleanup=False)
132
133if __name__ == "__main__":
134    exit(main())
def get_node_id(gid, nid):
15def get_node_id(gid, nid):
16    prefix = f"{gid}-{nid}"
17    return prefix
def init_graph():
19def init_graph():
20    g = graphviz.Graph(engine='sfdp')
21    g.attr('node', shape='point', width='0.01', height='0.01')
22    g.attr('edge', color="#0000ff10")
23    g.attr(overlap='prism', outputorder='edgesfirst')
24    g.attr(ratio='fill', size="20,10")
25    return g
def make_color(n, alpha):
27def make_color(n, alpha):
28    rgb = colorsys.hsv_to_rgb(n+(1/2), 1, 1)
29    hexrgb = ["%0.2X"%int(v * 255) for v in list(rgb)+[alpha]]
30    return f"#{hexrgb[0]}{hexrgb[1]}{hexrgb[2]}{hexrgb[3]}"
def tooltip(n):
32def tooltip(n):
33    if n.label.is_definition:
34        return n.label.definition.name
35    else:
36        return n.label.which.name
def main():
 38def main():
 39    with lowlevel_data_reader(Path(sys.argv[1])) as data:
 40
 41        subset = { p: gid for p, gid in data.graphid_by_filename.items()
 42                if Path('coq-tactician-stdlib.8.11.dev/theories/Init/') in p.parents }
 43
 44        # subset = { p: gid for p, gid in data.graphid_by_filename.items()
 45        #            if Path('coq-tactician-stdlib.8.11.dev/theories/Init/Logic.bin') == p }
 46
 47        print('Calculating node incoming count')
 48        incoming = Counter()
 49        for p, gid in subset.items():
 50            d = data[gid]
 51            for i, n in enumerate(d.graph.nodes):
 52                for ci in range(n.children_index, n.children_index + n.children_count):
 53                    target = d.graph.edges[ci].target
 54                    target_graph = data.local_to_global(gid, target.dep_index)
 55                    targetid = get_node_id(target_graph, target.node_index)
 56                    incoming[targetid] += 1
 57        incoming_max = incoming.most_common(1)[0][1]
 58
 59        print('Calculating node positions')
 60        g = init_graph()
 61        for p, gid in subset.items():
 62            d = data[gid]
 63            for i, n in enumerate(d.graph.nodes):
 64                sourceid = get_node_id(gid, i)
 65                g.node(sourceid, width=str(0.001*incoming[sourceid]), height=str(0.001*incoming[sourceid]))
 66                for ci in range(n.children_index, n.children_index + n.children_count):
 67                    target = d.graph.edges[ci].target
 68                    target_graph = data.local_to_global(gid, target.dep_index)
 69                    targetid = get_node_id(target_graph, target.node_index)
 70                    g.edge(sourceid, targetid)
 71                    # print(f"{sourceid},{targetid}")
 72        g.render(filename='web', format='json', view=False, cleanup=False)
 73
 74        print('Reading node positions')
 75        positions = dict()
 76        with open('web.json') as f:
 77            jsongraph = json.load(f)
 78            for node in jsongraph['objects']:
 79                position = node['pos'].split(',')
 80                position = complex(float(position[0]), float(position[1]))
 81                positions[node['name']] = position
 82
 83        print('Calculating largest distance')
 84        maxdist = 0
 85        for p, gid in subset.items():
 86            d = data[gid]
 87            for i, n in enumerate(d.graph.nodes):
 88                sourceid = get_node_id(gid, i)
 89                for ci in range(n.children_index, n.children_index + n.children_count):
 90                    target = d.graph.edges[ci].target
 91                    target_graph = data.local_to_global(gid, target.dep_index)
 92                    targetid = get_node_id(target_graph, target.node_index)
 93                    maxdist = max(maxdist, abs(positions[sourceid]-positions[targetid]))
 94        print(f'Max distance: {maxdist}')
 95
 96        print('Drawing final graph')
 97        g = init_graph()
 98        for p, gid in subset.items():
 99            d = data[gid]
100            # print(p)
101            for i, n in enumerate(d.graph.nodes):
102                sourceid = get_node_id(gid, i)
103                g.node(sourceid, width=str(0.001*incoming[sourceid]), height=str(0.001*incoming[sourceid]),
104                    color=make_color((1/2)+(math.log(incoming[sourceid]+1)/math.log(incoming_max+1)), 1),
105                    tooltip=tooltip(n))
106                for ci in range(n.children_index, n.children_index + n.children_count):
107                    target = d.graph.edges[ci].target
108                    target_graph = data.local_to_global(gid, target.dep_index)
109                    targetid = get_node_id(target_graph, target.node_index)
110                    distance = abs(positions[sourceid]-positions[targetid])
111                    distance_norm = distance / maxdist
112                    g.edge(sourceid, targetid, color=make_color(distance_norm, 0.05))
113                    # print(f"{sourceid},{targetid}")
114        g.render(filename='web', format='svg', view=False, cleanup=False)