def test_networkx_graph(self): G = nx.Graph() G.add_path([100, 200, 300]) G.add_node(1000) P = planarity.PGraph(G) H = planarity.networkx_graph(P) assert_equal(sorted(G.nodes()), sorted(H.nodes())) assert_equal(sorted(G.edges()), sorted(H.edges()))
def test_write_adjlist(self): e = ([1, 2], ) P = planarity.PGraph(e) fname = tempfile.mktemp() P.write(fname) d = open(fname).read() answer = 'N=2\n1: 2 0\n2: 1 0\n' assert_equal(d, answer) os.unlink(fname)
def kuratowski_subgraph(graph): """Return forbidden subgraph of nonplanar graph G.""" try: import networkx as nx except ImportError: raise ImportError("NetworkX required for kuratowski_subgraph()") pgraph = planarity.PGraph(graph) edges = pgraph.kuratowski_edges() return nx.Graph(edges)
def test_goldner_harary(self): # goldner-harary graph # http://en.wikipedia.org/wiki/Goldner%E2%80%93Harary_graph # a maximal planar graph e = [(1, 2), (1, 3), (1, 4), (1, 5), (1, 7), (1, 8), (1, 10), (1, 11), (2, 3), (2, 4), (2, 6), (2, 7), (2, 9), (2, 10), (2, 11), (3, 4), (4, 5), (4, 6), (4, 7), (5, 7), (6, 7), (7, 8), (7, 9), (7, 10), (8, 10), (9, 10), (10, 11)] P = planarity.PGraph(e) assert_true(P.is_planar())
def draw(graph, labels=True): """Draw planar graph with Matplotlib.""" try: import matplotlib.pyplot as plt from matplotlib.patches import Circle from matplotlib.collections import PatchCollection except ImportError: raise ImportError("Matplotlib is required for draw()") pgraph = planarity.PGraph(graph) pgraph.embed_drawplanar() hgraph = networkx_graph(pgraph) patches = [] node_labels = {} xs = [] ys = [] for node, data in hgraph.nodes(data=True): y = data['pos'] xb = data['start'] xe = data['end'] x = int((xe + xb) / 2) node_labels[node] = (x, y) patches += [Circle((x, y), 0.25)] #,0.5,fc='w')] xs.extend([xb, xe]) ys.append(y) plt.hlines([y], [xb], [xe]) for (_, _, data) in hgraph.edges(data=True): x = data['pos'] yb = data['start'] ye = data['end'] ys.extend([yb, ye]) xs.append(x) plt.vlines([x], [yb], [ye]) # labels if labels: for n, (x, y) in node_labels.items(): plt.text(x, y, n, horizontalalignment='center', verticalalignment='center', bbox=dict( boxstyle='round', ec=(0.0, 0.0, 0.0), fc=(1.0, 1.0, 1.0), )) p = PatchCollection(patches) ax = plt.gca() ax.add_collection(p) plt.axis('equal') plt.xlim(min(xs) - 1, max(xs) + 1) plt.ylim(min(ys) - 1, max(ys) + 1)
def test_is_planar_edgelist_input(self): P = planarity.PGraph(self.p4_edgelist) assert_true(P.is_planar()) P = planarity.PGraph(self.k5_edgelist) assert_false(P.is_planar())
def test_draw_text(self): e = ([1, 2], ) P = planarity.PGraph(e) s = P.ascii() #.decode() assert_equal(s, '1\n|\n2\n \n')
def test_no_kuratowski_k5m(self): edges = self.k5_edgelist[:] edges.remove((0, 1)) P = planarity.PGraph(edges) edges = P.kuratowski_edges() assert_equal(edges, [])
def test_kuratowski_k5(self): P = planarity.PGraph(self.k5_edgelist) edges = P.kuratowski_edges() assert_equal(sorted(edges), sorted(self.k5_edgelist))
def test_is_planar_adj_list(self): P = planarity.PGraph(self.k5_adj_list) assert_false(P.is_planar())
def test_is_planar_adj_symmetric(self): P = planarity.PGraph(self.k5_adj_symmetric) assert_false(P.is_planar())
def write(graph, path='stdout'): """Write an adjacency list representation of graph to path.""" planarity.PGraph(graph).write(path)
import planarity # Example of the complete graph of 5 nodes, K5 # K5 is not planar # any of the following formats can bed used for representing the graph edgelist = [(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)] P = planarity.PGraph(edgelist) print(P.nodes()) # indexed from 1..n print(P.mapping()) # the node mapping print(P.edges()) # edges print(P.is_planar()) # False print(P.kuratowski_edges()) edgelist.remove((0, 1)) P = planarity.PGraph(edgelist) print(P.ascii())
def _construct_planar_graph(self): pd = self.planar_diagram() g, duplicates, heights, first_edge = pd.as_networkx_extended() import planarity pg = planarity.PGraph(g) pg.embed_drawplanar() g = planarity.networkx_graph(pg) node_labels = {} xs = [] ys = [] nodes_by_height = {} node_xs_by_y = {} node_xs_ys = {} node_lefts_rights = {} for node, data in g.nodes(data=True): y = data['pos'] xb = data['start'] xe = data['end'] x = int((xe + xb) / 2.) node_labels[node] = (x, y) xs.extend([xb, xe]) ys.append(y) nodes_by_height[data['pos']] = node node_xs_by_y[data['pos']] = x node_xs_ys[node] = (x, y) node_lefts_rights[node] = (xb, xe) lines = [] rightmost_x = n.max(xs) leftmost_x = n.min(xs) x_span = rightmost_x - leftmost_x safe_yshift = 0.5 / x_span extra_x_shifts = [] for n1, n2, data in g.edges(data=True): x = data['pos'] yb = data['start'] ye = data['end'] start_node = nodes_by_height[yb] end_node = nodes_by_height[ye] if start_node >= len(self) and end_node >= len(self): continue start_left, start_right = node_lefts_rights[start_node] end_left, end_right = node_lefts_rights[end_node] start_frac = n.abs((x - start_left) / (start_right - start_left) - 0.5) start_frac = 0.5 - start_frac if True: # ye < ys: # This always evaluated to True - a bug? start_frac *= -1 start_shift = start_frac end_frac = n.abs((x - end_left) / (end_right - end_left) - 0.5) end_frac = 0.5 - end_frac if False: # ye > ys: # This always evaluated to False - a bug? end_frac *= -1 end_shift = end_frac start_node_x = node_xs_by_y[yb] start_node_y = yb end_node_x = node_xs_by_y[ye] end_node_y = ye line = n.array([[start_node_x, start_node_y], [x, start_node_y - start_shift], [x, end_node_y - end_shift], [end_node_x, end_node_y]]) if x == start_node_x: line = line[1:] line[0, 1] = start_node_y if x == end_node_x: line = line[:-1] line[-1, 1] = end_node_y lines.append(line) if sorted((n1, n2)) in duplicates: line = line.copy() n1x, n1y = node_xs_ys[n1] n2x, n2y = node_xs_ys[n2] lx, hx = sorted([n1x, n2x]) ly, hy = sorted([n1y, n2y]) if len(line) == 4: join_1 = n.array([line[2, 0], line[2, 1], 0]) - n.array( [line[0, 0], line[0, 1], 0]) normal_1 = n.cross(join_1, [0, 0, 1])[:2] normal_1 /= n.linalg.norm(normal_1) join_2 = n.array([line[3, 0], line[3, 1], 0]) - n.array( [line[1, 0], line[1, 1], 0]) normal_2 = n.cross(join_2, [0, 0, 1])[:2] normal_2 /= n.linalg.norm(normal_2) extra_x_shifts.append(line[1][0] + 0.005 * normal_1[0]) line[1] += 0.01 * normal_1 line[2] += 0.01 * normal_2 elif len(line) == 3: join_1 = n.array([line[2, 0], line[2, 1], 0]) - n.array( [line[0, 0], line[0, 1], 0]) normal_1 = n.cross(join_1, [0, 0, 1])[:2] normal_1 /= n.linalg.norm(normal_1) extra_x_shifts.append(line[1][0] + 0.005 * normal_1[0]) line[1] += 0.01 * normal_1 elif len(line) == 2: join_1 = n.array([line[1, 0], line[1, 1], 0]) - n.array( [line[0, 0], line[0, 1], 0]) normal_1 = n.cross(join_1, [0, 0, 1])[:2] normal_1 /= n.linalg.norm(normal_1) line = n.vstack([ line[0], line[0] + 0.5 * (line[1] - line[0]) + 0.01 * normal_1, line[1] ]) extra_x_shifts.append(line[1][0] - 0.005 * normal_1[0]) lines.append(line) extra_x_shifts = sorted(extra_x_shifts)[::-1] return g, lines, node_labels, nodes_by_height, ( leftmost_x, rightmost_x), first_edge, heights, extra_x_shifts
def pgraph_graph(graph): """Return pgraph graph built from NetworkX graph.""" return planarity.PGraph(graph)
def kuratowski_edges(graph): """Return edges of forbidden subgraph of non-planar graph.""" return planarity.PGraph(graph).kuratowski_edges()
def is_planar(graph): """Test planarity of graph.""" return planarity.PGraph(graph).is_planar()
def mapping(graph): """Return dictionary of internal mapping of nodes to integers.""" return planarity.PGraph(graph).mapping()
def test_is_planar_adj_input(self): P = planarity.PGraph(self.p4_adj) assert_true(P.is_planar()) P = planarity.PGraph(self.k5_adj) assert_false(P.is_planar())
def ascii(graph): """Draw text representation of a planar graph.""" return planarity.PGraph(graph).ascii()