blob: 562d3964f267e7e057c690462842f63bf44da2e5 [file] [log] [blame]
"""
Tests for VF2 isomorphism algorithm.
"""
import os
import struct
import random
from nose.tools import assert_true, assert_equal
import networkx as nx
from networkx.algorithms import isomorphism as iso
class TestWikipediaExample(object):
# Source: http://en.wikipedia.org/wiki/Graph_isomorphism
# Nodes 'a', 'b', 'c' and 'd' form a column.
# Nodes 'g', 'h', 'i' and 'j' form a column.
g1edges = [['a','g'], ['a','h'], ['a','i'],
['b','g'], ['b','h'], ['b','j'],
['c','g'], ['c','i'], ['c','j'],
['d','h'], ['d','i'], ['d','j']]
# Nodes 1,2,3,4 form the clockwise corners of a large square.
# Nodes 5,6,7,8 form the clockwise corners of a small square
g2edges = [[1,2], [2,3], [3,4], [4,1],
[5,6], [6,7], [7,8], [8,5],
[1,5], [2,6], [3,7], [4,8]]
def test_graph(self):
g1 = nx.Graph()
g2 = nx.Graph()
g1.add_edges_from(self.g1edges)
g2.add_edges_from(self.g2edges)
gm = iso.GraphMatcher(g1,g2)
assert_true(gm.is_isomorphic())
mapping = sorted(gm.mapping.items())
# this mapping is only one of the possibilies
# so this test needs to be reconsidered
# isomap = [('a', 1), ('b', 6), ('c', 3), ('d', 8),
# ('g', 2), ('h', 5), ('i', 4), ('j', 7)]
# assert_equal(mapping, isomap)
def test_subgraph(self):
g1 = nx.Graph()
g2 = nx.Graph()
g1.add_edges_from(self.g1edges)
g2.add_edges_from(self.g2edges)
g3 = g2.subgraph([1,2,3,4])
gm = iso.GraphMatcher(g1,g3)
assert_true(gm.subgraph_is_isomorphic())
class TestVF2GraphDB(object):
# http://amalfi.dis.unina.it/graph/db/
@staticmethod
def create_graph(filename):
"""Creates a Graph instance from the filename."""
# The file is assumed to be in the format from the VF2 graph database.
# Each file is composed of 16-bit numbers (unsigned short int).
# So we will want to read 2 bytes at a time.
# We can read the number as follows:
# number = struct.unpack('<H', file.read(2))
# This says, expect the data in little-endian encoding
# as an unsigned short int and unpack 2 bytes from the file.
fh = open(filename, mode='rb')
# Grab the number of nodes.
# Node numeration is 0-based, so the first node has index 0.
nodes = struct.unpack('<H', fh.read(2))[0]
graph = nx.Graph()
for from_node in range(nodes):
# Get the number of edges.
edges = struct.unpack('<H', fh.read(2))[0]
for edge in range(edges):
# Get the terminal node.
to_node = struct.unpack('<H', fh.read(2))[0]
graph.add_edge(from_node, to_node)
fh.close()
return graph
def test_graph(self):
head,tail = os.path.split(__file__)
g1 = self.create_graph(os.path.join(head,'iso_r01_s80.A99'))
g2 = self.create_graph(os.path.join(head,'iso_r01_s80.B99'))
gm = iso.GraphMatcher(g1,g2)
assert_true(gm.is_isomorphic())
def test_subgraph(self):
# A is the subgraph
# B is the full graph
head,tail = os.path.split(__file__)
subgraph = self.create_graph(os.path.join(head,'si2_b06_m200.A99'))
graph = self.create_graph(os.path.join(head,'si2_b06_m200.B99'))
gm = iso.GraphMatcher(graph, subgraph)
assert_true(gm.subgraph_is_isomorphic())
def test_graph_atlas():
#Atlas = nx.graph_atlas_g()[0:208] # 208, 6 nodes or less
Atlas = nx.graph_atlas_g()[0:100]
alphabet = list(range(26))
for graph in Atlas:
nlist = graph.nodes()
labels = alphabet[:len(nlist)]
for s in range(10):
random.shuffle(labels)
d = dict(zip(nlist,labels))
relabel = nx.relabel_nodes(graph, d)
gm = iso.GraphMatcher(graph, relabel)
assert_true(gm.is_isomorphic())
def test_multiedge():
# Simple test for multigraphs
# Need something much more rigorous
edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5),
(5, 6), (6, 7), (7, 8), (8, 9), (9, 10),
(10, 11), (10, 11), (11, 12), (11, 12),
(12, 13), (12, 13), (13, 14), (13, 14),
(14, 15), (14, 15), (15, 16), (15, 16),
(16, 17), (16, 17), (17, 18), (17, 18),
(18, 19), (18, 19), (19, 0), (19, 0)]
nodes = list(range(20))
for g1 in [nx.MultiGraph(), nx.MultiDiGraph()]:
g1.add_edges_from(edges)
for _ in range(10):
new_nodes = list(nodes)
random.shuffle(new_nodes)
d = dict(zip(nodes, new_nodes))
g2 = nx.relabel_nodes(g1, d)
if not g1.is_directed():
gm = iso.GraphMatcher(g1,g2)
else:
gm = iso.DiGraphMatcher(g1,g2)
assert_true(gm.is_isomorphic())
def test_selfloop():
# Simple test for graphs with selfloops
edges = [(0, 1), (0, 2), (1, 2), (1, 3), (2, 2),
(2, 4), (3, 1), (3, 2), (4, 2), (4, 5), (5, 4)]
nodes = list(range(6))
for g1 in [nx.Graph(), nx.DiGraph()]:
g1.add_edges_from(edges)
for _ in range(100):
new_nodes = list(nodes)
random.shuffle(new_nodes)
d = dict(zip(nodes, new_nodes))
g2 = nx.relabel_nodes(g1, d)
if not g1.is_directed():
gm = iso.GraphMatcher(g1,g2)
else:
gm = iso.DiGraphMatcher(g1,g2)
assert_true(gm.is_isomorphic())
def test_isomorphism_iter1():
# As described in:
# http://groups.google.com/group/networkx-discuss/browse_thread/thread/2ff65c67f5e3b99f/d674544ebea359bb?fwc=1
g1 = nx.DiGraph()
g2 = nx.DiGraph()
g3 = nx.DiGraph()
g1.add_edge('A','B')
g1.add_edge('B','C')
g2.add_edge('Y','Z')
g3.add_edge('Z','Y')
gm12 = iso.DiGraphMatcher(g1,g2)
gm13 = iso.DiGraphMatcher(g1,g3)
x = list(gm12.subgraph_isomorphisms_iter())
y = list(gm13.subgraph_isomorphisms_iter())
assert_true({'A':'Y','B':'Z'} in x)
assert_true({'B':'Y','C':'Z'} in x)
assert_true({'A':'Z','B':'Y'} in y)
assert_true({'B':'Z','C':'Y'} in y)
assert_equal(len(x),len(y))
assert_equal(len(x),2)
def test_isomorphism_iter2():
# Path
for L in range(2,10):
g1 = nx.path_graph(L)
gm = iso.GraphMatcher(g1,g1)
s = len(list(gm.isomorphisms_iter()))
assert_equal(s,2)
# Cycle
for L in range(3,10):
g1 = nx.cycle_graph(L)
gm = iso.GraphMatcher(g1,g1)
s = len(list(gm.isomorphisms_iter()))
assert_equal(s, 2*L)
def test_multiple():
# Verify that we can use the graph matcher multiple times
edges = [('A','B'),('B','A'),('B','C')]
for g1,g2 in [(nx.Graph(),nx.Graph()), (nx.DiGraph(),nx.DiGraph())]:
g1.add_edges_from(edges)
g2.add_edges_from(edges)
g3 = nx.subgraph(g2, ['A','B'])
if not g1.is_directed():
gmA = iso.GraphMatcher(g1,g2)
gmB = iso.GraphMatcher(g1,g3)
else:
gmA = iso.DiGraphMatcher(g1,g2)
gmB = iso.DiGraphMatcher(g1,g3)
assert_true(gmA.is_isomorphic())
g2.remove_node('C')
assert_true(gmA.subgraph_is_isomorphic())
assert_true(gmB.subgraph_is_isomorphic())
# for m in [gmB.mapping, gmB.mapping]:
# assert_true(m['A'] == 'A')
# assert_true(m['B'] == 'B')
# assert_true('C' not in m)