1 """ 2 Tests for VF2 isomorphism algorithm. 3 """ 4 5 import os 6 import struct 7 import random 8 9 from nose.tools import assert_true, assert_equal 10 import networkx as nx 11 from networkx.algorithms import isomorphism as iso 12 13 class TestWikipediaExample(object): 14 # Source: http://en.wikipedia.org/wiki/Graph_isomorphism 15 16 # Nodes 'a', 'b', 'c' and 'd' form a column. 17 # Nodes 'g', 'h', 'i' and 'j' form a column. 18 g1edges = [['a','g'], ['a','h'], ['a','i'], 19 ['b','g'], ['b','h'], ['b','j'], 20 ['c','g'], ['c','i'], ['c','j'], 21 ['d','h'], ['d','i'], ['d','j']] 22 23 # Nodes 1,2,3,4 form the clockwise corners of a large square. 24 # Nodes 5,6,7,8 form the clockwise corners of a small square 25 g2edges = [[1,2], [2,3], [3,4], [4,1], 26 [5,6], [6,7], [7,8], [8,5], 27 [1,5], [2,6], [3,7], [4,8]] 28 29 def test_graph(self): 30 g1 = nx.Graph() 31 g2 = nx.Graph() 32 g1.add_edges_from(self.g1edges) 33 g2.add_edges_from(self.g2edges) 34 gm = iso.GraphMatcher(g1,g2) 35 assert_true(gm.is_isomorphic()) 36 37 mapping = sorted(gm.mapping.items()) 38 # this mapping is only one of the possibilies 39 # so this test needs to be reconsidered 40 # isomap = [('a', 1), ('b', 6), ('c', 3), ('d', 8), 41 # ('g', 2), ('h', 5), ('i', 4), ('j', 7)] 42 # assert_equal(mapping, isomap) 43 44 def test_subgraph(self): 45 g1 = nx.Graph() 46 g2 = nx.Graph() 47 g1.add_edges_from(self.g1edges) 48 g2.add_edges_from(self.g2edges) 49 g3 = g2.subgraph([1,2,3,4]) 50 gm = iso.GraphMatcher(g1,g3) 51 assert_true(gm.subgraph_is_isomorphic()) 52 53 class TestVF2GraphDB(object): 54 # http://amalfi.dis.unina.it/graph/db/ 55 56 @staticmethod 57 def create_graph(filename): 58 """Creates a Graph instance from the filename.""" 59 60 # The file is assumed to be in the format from the VF2 graph database. 61 # Each file is composed of 16-bit numbers (unsigned short int). 62 # So we will want to read 2 bytes at a time. 63 64 # We can read the number as follows: 65 # number = struct.unpack('<H', file.read(2)) 66 # This says, expect the data in little-endian encoding 67 # as an unsigned short int and unpack 2 bytes from the file. 68 69 fh = open(filename, mode='rb') 70 71 # Grab the number of nodes. 72 # Node numeration is 0-based, so the first node has index 0. 73 nodes = struct.unpack('<H', fh.read(2))[0] 74 75 graph = nx.Graph() 76 for from_node in range(nodes): 77 # Get the number of edges. 78 edges = struct.unpack('<H', fh.read(2))[0] 79 for edge in range(edges): 80 # Get the terminal node. 81 to_node = struct.unpack('<H', fh.read(2))[0] 82 graph.add_edge(from_node, to_node) 83 84 fh.close() 85 return graph 86 87 def test_graph(self): 88 head,tail = os.path.split(__file__) 89 g1 = self.create_graph(os.path.join(head,'iso_r01_s80.A99')) 90 g2 = self.create_graph(os.path.join(head,'iso_r01_s80.B99')) 91 gm = iso.GraphMatcher(g1,g2) 92 assert_true(gm.is_isomorphic()) 93 94 def test_subgraph(self): 95 # A is the subgraph 96 # B is the full graph 97 head,tail = os.path.split(__file__) 98 subgraph = self.create_graph(os.path.join(head,'si2_b06_m200.A99')) 99 graph = self.create_graph(os.path.join(head,'si2_b06_m200.B99')) 100 gm = iso.GraphMatcher(graph, subgraph) 101 assert_true(gm.subgraph_is_isomorphic()) 102 103 def test_graph_atlas(): 104 #Atlas = nx.graph_atlas_g()[0:208] # 208, 6 nodes or less 105 Atlas = nx.graph_atlas_g()[0:100] 106 alphabet = list(range(26)) 107 for graph in Atlas: 108 nlist = graph.nodes() 109 labels = alphabet[:len(nlist)] 110 for s in range(10): 111 random.shuffle(labels) 112 d = dict(zip(nlist,labels)) 113 relabel = nx.relabel_nodes(graph, d) 114 gm = iso.GraphMatcher(graph, relabel) 115 assert_true(gm.is_isomorphic()) 116 117 def test_multiedge(): 118 # Simple test for multigraphs 119 # Need something much more rigorous 120 edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), 121 (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), 122 (10, 11), (10, 11), (11, 12), (11, 12), 123 (12, 13), (12, 13), (13, 14), (13, 14), 124 (14, 15), (14, 15), (15, 16), (15, 16), 125 (16, 17), (16, 17), (17, 18), (17, 18), 126 (18, 19), (18, 19), (19, 0), (19, 0)] 127 nodes = list(range(20)) 128 129 for g1 in [nx.MultiGraph(), nx.MultiDiGraph()]: 130 g1.add_edges_from(edges) 131 for _ in range(10): 132 new_nodes = list(nodes) 133 random.shuffle(new_nodes) 134 d = dict(zip(nodes, new_nodes)) 135 g2 = nx.relabel_nodes(g1, d) 136 if not g1.is_directed(): 137 gm = iso.GraphMatcher(g1,g2) 138 else: 139 gm = iso.DiGraphMatcher(g1,g2) 140 assert_true(gm.is_isomorphic()) 141 142 def test_selfloop(): 143 # Simple test for graphs with selfloops 144 edges = [(0, 1), (0, 2), (1, 2), (1, 3), (2, 2), 145 (2, 4), (3, 1), (3, 2), (4, 2), (4, 5), (5, 4)] 146 nodes = list(range(6)) 147 148 for g1 in [nx.Graph(), nx.DiGraph()]: 149 g1.add_edges_from(edges) 150 for _ in range(100): 151 new_nodes = list(nodes) 152 random.shuffle(new_nodes) 153 d = dict(zip(nodes, new_nodes)) 154 g2 = nx.relabel_nodes(g1, d) 155 if not g1.is_directed(): 156 gm = iso.GraphMatcher(g1,g2) 157 else: 158 gm = iso.DiGraphMatcher(g1,g2) 159 assert_true(gm.is_isomorphic()) 160 161 def test_isomorphism_iter1(): 162 # As described in: 163 # http://groups.google.com/group/networkx-discuss/browse_thread/thread/2ff65c67f5e3b99f/d674544ebea359bb?fwc=1 164 g1 = nx.DiGraph() 165 g2 = nx.DiGraph() 166 g3 = nx.DiGraph() 167 g1.add_edge('A','B') 168 g1.add_edge('B','C') 169 g2.add_edge('Y','Z') 170 g3.add_edge('Z','Y') 171 gm12 = iso.DiGraphMatcher(g1,g2) 172 gm13 = iso.DiGraphMatcher(g1,g3) 173 x = list(gm12.subgraph_isomorphisms_iter()) 174 y = list(gm13.subgraph_isomorphisms_iter()) 175 assert_true({'A':'Y','B':'Z'} in x) 176 assert_true({'B':'Y','C':'Z'} in x) 177 assert_true({'A':'Z','B':'Y'} in y) 178 assert_true({'B':'Z','C':'Y'} in y) 179 assert_equal(len(x),len(y)) 180 assert_equal(len(x),2) 181 182 def test_isomorphism_iter2(): 183 # Path 184 for L in range(2,10): 185 g1 = nx.path_graph(L) 186 gm = iso.GraphMatcher(g1,g1) 187 s = len(list(gm.isomorphisms_iter())) 188 assert_equal(s,2) 189 # Cycle 190 for L in range(3,10): 191 g1 = nx.cycle_graph(L) 192 gm = iso.GraphMatcher(g1,g1) 193 s = len(list(gm.isomorphisms_iter())) 194 assert_equal(s, 2*L) 195 196 def test_multiple(): 197 # Verify that we can use the graph matcher multiple times 198 edges = [('A','B'),('B','A'),('B','C')] 199 for g1,g2 in [(nx.Graph(),nx.Graph()), (nx.DiGraph(),nx.DiGraph())]: 200 g1.add_edges_from(edges) 201 g2.add_edges_from(edges) 202 g3 = nx.subgraph(g2, ['A','B']) 203 if not g1.is_directed(): 204 gmA = iso.GraphMatcher(g1,g2) 205 gmB = iso.GraphMatcher(g1,g3) 206 else: 207 gmA = iso.DiGraphMatcher(g1,g2) 208 gmB = iso.DiGraphMatcher(g1,g3) 209 assert_true(gmA.is_isomorphic()) 210 g2.remove_node('C') 211 assert_true(gmA.subgraph_is_isomorphic()) 212 assert_true(gmB.subgraph_is_isomorphic()) 213 # for m in [gmB.mapping, gmB.mapping]: 214 # assert_true(m['A'] == 'A') 215 # assert_true(m['B'] == 'B') 216 # assert_true('C' not in m) 217 218