Example #1
0
 def make_graph(edge_inds):
     nvert = max(max(inds) for inds in edge_inds) + 1
     vertices = [Vertex() for _ in range(nvert)]
     graph = Graph(vertices)
     for idx1, idx2 in edge_inds:
         graph.add_edge(Edge(vertices[idx1], vertices[idx2]))
     return graph
 def make_graph(edge_inds):
     nvert = max(max(inds) for inds in edge_inds) + 1
     vertices = [Vertex() for _ in range(nvert)]
     graph = Graph(vertices)
     for idx1, idx2 in edge_inds:
         graph.addEdge(Edge(vertices[idx1], vertices[idx2]))
     return graph
    def __init__(self,
                 label='',
                 species_repr=None,
                 vertices=None,
                 symmetry=-1,
                 multiplicity=-187,
                 reactive=True,
                 props=None,
                 InChI='',
                 SMILES=''):
        self.index = -1
        self.label = label
        self.species_repr = species_repr
        Graph.__init__(self, vertices)
        self.symmetryNumber = symmetry
        self.fingerprint = None
        self.inchi = None
        self.smiles = None
        self.props = props or {}
        self.multiplicity = multiplicity
        self.reactive = reactive

        if InChI and SMILES:
            logging.warning(
                'Both InChI and SMILES provided for Fragment instantiation, using InChI and ignoring SMILES.'
            )
        if InChI:
            self.fromInChI(InChI)
            self.inchi = InChI
        elif SMILES:
            self.from_SMILES_like_string(SMILES)
            self.smiles = SMILES
    def test_getLargestRing(self):
        """
        Test that the Graph.getPolycyclicRings() method returns only polycyclic rings.
        """
        vertices = [Vertex() for i in range(27)]
        bonds = [
                 (0,1),
                 (1,2),
                 (2,3),
                 (3,4),
                 (4,5),
                 (5,6),
                 (6,7),
                 (9,10),
                 (10,11),
                 (11,12),
                 (12,13),
                 (13,14),
                 (14,15),
                 (12,16),
                 (10,17),
                 (17,18),
                 (18,19),
                 (9,20),
                 (20,21),
                 (6,22),
                 (22,23),
                 (22,8),
                 (8,4),
                 (23,3),
                 (23,24),
                 (24,25),
                 (25,1)
                 ]
        edges = []
        for bond in bonds:
            edges.append(Edge(vertices[bond[0]], vertices[bond[1]]))

        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)
        graph.updateConnectivityValues()
        
        rings = graph.getPolycyclicRings()
        self.assertEqual(len(rings), 1)
        
        #ensure the last ring doesn't include vertex 8, since it isn't in the
        #longest ring. Try two different items since one might contain the vertex 8
        long_ring = graph.getLargestRing(rings[0][0])
        long_ring2 = graph.getLargestRing(rings[0][1])
        
        if len(long_ring) > len(long_ring2):
            longest_ring = long_ring
        else:
            longest_ring = long_ring2
        
        self.assertEqual(len(longest_ring), len(rings[0]) - 1)
Example #5
0
    def test_pickle(self):
        """
        Test that a Graph object can be successfully pickled and unpickled
        with no loss of information.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph0 = Graph()
        for vertex in vertices:
            graph0.addVertex(vertex)
        for edge in edges:
            graph0.addEdge(edge)
        graph0.updateConnectivityValues()

        import cPickle
        graph = cPickle.loads(cPickle.dumps(graph0))

        self.assertEqual(len(graph0.vertices), len(graph.vertices))
        for v1, v2 in zip(graph0.vertices, graph.vertices):
            self.assertEqual(v1.connectivity1, v2.connectivity1)
            self.assertEqual(v1.connectivity2, v2.connectivity2)
            self.assertEqual(v1.connectivity3, v2.connectivity3)
            self.assertEqual(v1.sortingLabel, v2.sortingLabel)
            self.assertEqual(len(v1.edges), len(v2.edges))
        self.assertTrue(graph0.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph0))
Example #6
0
    def test_pickle(self):
        """
        Test that a Graph object can be successfully pickled and unpickled
        with no loss of information.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph0 = Graph()
        for vertex in vertices: graph0.addVertex(vertex)
        for edge in edges: graph0.addEdge(edge)
        graph0.updateConnectivityValues()
        
        import cPickle
        graph = cPickle.loads(cPickle.dumps(graph0))

        self.assertEqual(len(graph0.vertices), len(graph.vertices))
        for v1, v2 in zip(graph0.vertices, graph.vertices):
            self.assertEqual(v1.connectivity1, v2.connectivity1)
            self.assertEqual(v1.connectivity2, v2.connectivity2)
            self.assertEqual(v1.connectivity3, v2.connectivity3)
            self.assertEqual(v1.sortingLabel, v2.sortingLabel)
            self.assertEqual(len(v1.edges), len(v2.edges))
        self.assertTrue(graph0.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph0))
Example #7
0
    def test_copy(self):
        """
        Test the graph copy function to ensure a complete copy of the graph is
        made while preserving vertices and edges.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)

        graph2 = graph.copy()
        for vertex in graph.vertices:
            self.assertTrue(graph2.hasVertex(vertex))
        for v1 in graph.vertices:
            for v2 in v1.edges:
                self.assertTrue(graph2.hasEdge(v1, v2))
                self.assertTrue(graph2.hasEdge(v2, v1))
        self.assertTrue(graph2.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph2))
Example #8
0
    def setUp(self):
        """
        A function run before each unit test in this class.
        """
        vertices = [Vertex() for _ in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        self.graph = Graph(vertices)
        for edge in edges:
            self.graph.add_edge(edge)
 def merge(self, other):
     """
     Merge two fragments so as to store them in a single :class:`Fragment`
     object. The merged :class:`Fragment` object is returned.
     """
     g = Graph.merge(self, other)
     fragment = Fragment(vertices=g.vertices)
     return fragment
Example #10
0
    def test_split(self):
        """
        Test the graph split function to ensure a proper splitting of the graph
        is being done.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)

        graphs = graph.split()

        self.assertTrue(len(graphs) == 2)
        self.assertTrue(
            len(graphs[0].vertices) == 4 or len(graphs[0].vertices) == 2)
        self.assertTrue(
            len(graphs[0].vertices) +
            len(graphs[1].vertices) == len(graph.vertices))
Example #11
0
    def test_copy(self):
        """
        Test the graph copy function to ensure a complete copy of the graph is
        made while preserving vertices and edges.
        """
        
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)
        
        graph2 = graph.copy()
        for vertex in graph.vertices:
            self.assertTrue(graph2.hasVertex(vertex))
        for v1 in graph.vertices:
            for v2 in v1.edges:
                self.assertTrue(graph2.hasEdge(v1, v2))
                self.assertTrue(graph2.hasEdge(v2, v1))
        self.assertTrue(graph2.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph2))
Example #12
0
    def test_vertex_connectivity_values(self):
        """
        Tests the vertex connectivity values as introduced by Morgan (1965).
        
        Graph:     Expected (and tested) values:
        
        0-1-2-3-4            1-3-2-2-1   3-4-5-3-2    4-11-7-7-3
        |                    |           |             |
        5                    1           3             4
        
                                # = 3     # = 4         # = 4
                                         *selected*
        """
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[1], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)

        graph.updateConnectivityValues()

        for i, cv_ in enumerate([11, 15, 18, 10, 7, 11]):
            cv = vertices[i].connectivity
            self.assertEqual(cv, cv_, "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".format(i, cv, cv_))
 def copy(self, deep=False):
     """
     Create a copy of the current graph. If `deep` is ``True``, a deep copy
     is made: copies of the vertices and edges are used in the new graph.
     If `deep` is ``False`` or not specified, a shallow copy is made: the
     original vertices and edges are used in the new graph.
     """
     g = Graph.copy(self, deep)
     other = Fragment(vertices=g.vertices)
     return other
    def isIsomorphic(self,
                     other,
                     initialMap=None,
                     generateInitialMap=False,
                     saveOrder=False,
                     strict=True):
        """
        Returns :data:`True` if two graphs are isomorphic and :data:`False`
        otherwise. The `initialMap` attribute can be used to specify a required
        mapping from `self` to `other` (i.e. the atoms of `self` are the keys,
        while the atoms of `other` are the values). The `other` parameter must
        be a :class:`Graph` object, or a :class:`TypeError` is raised.
        Also ensures multiplicities are also equal.

        Args:
            initialMap (dict, optional):         initial atom mapping to use
            generateInitialMap (bool, optional): if ``True``, initialize map by pairing atoms with same labels
            saveOrder (bool, optional):          if ``True``, reset atom order after performing atom isomorphism
            strict (bool, optional):             if ``False``, perform isomorphism ignoring electrons
        """
        # It only makes sense to compare a Molecule to a Molecule for full
        # isomorphism, so raise an exception if this is not what was requested
        if not isinstance(other, Graph):
            raise TypeError(
                'Got a {0} object for parameter "other", when a Molecule object is required.'
                .format(other.__class__))
        # Do the quick isomorphism comparison using the fingerprint
        # Two fingerprint strings matching is a necessary (but not
        # sufficient!) condition for the associated molecules to be isomorphic
        if self.fingerprint != other.fingerprint:
            return False
        # check multiplicity
        if self.multiplicity != other.multiplicity:
            return False

        if generateInitialMap:
            initialMap = dict()
            for atom in self.vertices:
                if atom.label and atom.label != '':
                    for a in other.vertices:
                        if a.label == atom.label:
                            initialMap[atom] = a
                            break
                    else:
                        return False
            if not self.isMappingValid(other, initialMap, equivalent=True):
                return False

        # Do the full isomorphism comparison
        result = Graph.isIsomorphic(self,
                                    other,
                                    initialMap,
                                    saveOrder=saveOrder,
                                    strict=strict)
        return result
 def split(self):
     """
     Convert a single :class:`Fragment` object containing two or more
     unconnected fragment into separate class:`Fragment` objects.
     """
     graphs = Graph.split(self)
     fragments = []
     for g in graphs:
         fragment = Fragment(vertices=g.vertices)
         fragments.append(fragment)
     return fragments
Example #16
0
    def test_vertex_connectivity_values(self):
        """
        Tests the vertex connectivity values as introduced by Morgan (1965).
        
        First CV1 is the number of neighbours
        CV2 is the sum of neighbouring CV1 values
        CV3 is the sum of neighbouring CV2 values
        
        Graph:     Expected (and tested) values:
        
        0-1-2-3-4            1-3-2-2-1   3-4-5-3-2    4-11-7-7-3
        |                    |           |             |
        5                    1           3             4
        
        """
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[1], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)

        graph.updateConnectivityValues()

        for i, cv_ in enumerate([1, 3, 2, 2, 1, 1]):
            cv = vertices[i].connectivity1
            self.assertEqual(
                cv, cv_,
                "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".
                format(i, cv, cv_))
        for i, cv_ in enumerate([3, 4, 5, 3, 2, 3]):
            cv = vertices[i].connectivity2
            self.assertEqual(
                cv, cv_,
                "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".
                format(i, cv, cv_))
        for i, cv_ in enumerate([4, 11, 7, 7, 3, 4]):
            cv = vertices[i].connectivity3
            self.assertEqual(
                cv, cv_,
                "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".
                format(i, cv, cv_))
Example #17
0
    def test_split(self):
        """
        Test the graph split function to ensure a proper splitting of the graph
        is being done.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)

        graphs = graph.split()

        self.assertTrue(len(graphs) == 2)
        self.assertTrue(len(graphs[0].vertices) == 4 or len(graphs[0].vertices) == 2)
        self.assertTrue(len(graphs[0].vertices) + len(graphs[1].vertices) == len(graph.vertices))
 def setUp(self):
     """
     A function run before each unit test in this class.
     """
     vertices = [Vertex() for i in range(6)]
     edges = [
         Edge(vertices[0], vertices[1]),
         Edge(vertices[1], vertices[2]),
         Edge(vertices[2], vertices[3]),
         Edge(vertices[3], vertices[4]),
         Edge(vertices[4], vertices[5]),
     ]
     
     self.graph = Graph(vertices)
     for edge in edges: self.graph.addEdge(edge)
    def isSubgraphIsomorphic(self, other, initialMap=None):
        """
        Fragment's subgraph isomorphism check is done by first creating 
        a representative molecule of fragment, and then following same procedure
        of subgraph isomorphism check of `Molecule` object aganist `Group` object
        """
        if not isinstance(other, gr.Group):
            raise TypeError(
                'Got a {0} object for parameter "other", when a Molecule object is required.'
                .format(other.__class__))
        group = other

        mapping = self.assign_representative_molecule()

        # Check multiplicity
        if group.multiplicity:
            if self.mol_repr.multiplicity not in group.multiplicity:
                return False

        # Compare radical counts
        if self.mol_repr.getRadicalCount() < group.radicalCount:
            return False

        # Compare element counts
        element_count = self.mol_repr.get_element_count()
        for element, count in group.elementCount.iteritems():
            if element not in element_count:
                return False
            elif element_count[element] < count:
                return False

        # Do the isomorphism comparison
        new_initial_map = None
        if initialMap:
            new_initial_map = {}
            for fragment_vertex in initialMap:
                repr_mol_vertex = mapping[fragment_vertex]
                new_initial_map[repr_mol_vertex] = initialMap[fragment_vertex]

        result = Graph.isSubgraphIsomorphic(self.mol_repr, other,
                                            new_initial_map)
        return result
Example #20
0
    def test_vertex_connectivity_values(self):
        """
        Tests the vertex connectivity values as introduced by Morgan (1965).
        
        First CV1 is the number of neighbours
        CV2 is the sum of neighbouring CV1 values
        CV3 is the sum of neighbouring CV2 values
        
        Graph:     Expected (and tested) values:
        
        0-1-2-3-4            1-3-2-2-1   3-4-5-3-2    4-11-7-7-3
        |                    |           |             |
        5                    1           3             4
        
        """
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[1], vertices[5]),
        ]
        
        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)
    
        graph.updateConnectivityValues()

        for i,cv_ in enumerate([1,3,2,2,1,1]):
            cv = vertices[i].connectivity1
            self.assertEqual(cv, cv_, "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".format(i,cv,cv_))
        for i,cv_ in enumerate([3,4,5,3,2,3]):
            cv = vertices[i].connectivity2
            self.assertEqual(cv, cv_, "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".format(i,cv,cv_))
        for i,cv_ in enumerate([4,11,7,7,3,4]):
            cv = vertices[i].connectivity3
            self.assertEqual(cv, cv_, "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".format(i,cv,cv_))
    def test_isomorphism_disconnected(self):
        """
        Check the graph isomorphism for broken graphs.
        
        This tries to match graphs with a missing bond, 
        eg. [ 0-1-2-3-4  5 ] should match [ 0-1-2-3-4  5 ]
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            #Edge(vertices1[4], vertices1[5]),
        ]

        vertices2 = [Vertex() for i in range(6)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
            Edge(vertices2[2], vertices2[3]),
            Edge(vertices2[3], vertices2[4]),
            #Edge(vertices2[4], vertices2[5]),
        ]

        graph1 = Graph()
        for vertex in vertices1: graph1.addVertex(vertex)
        for edge in edges1: graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2: graph2.addVertex(vertex)
        for edge in edges2: graph2.addEdge(edge)

        self.assertTrue(graph1.isIsomorphic(graph2))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))
        self.assertTrue(graph2.isIsomorphic(graph1))
        self.assertTrue(graph2.isSubgraphIsomorphic(graph1))
        self.assertTrue(len(graph1.findSubgraphIsomorphisms(graph2)) > 0)
Example #22
0
    def test_subgraphIsomorphism(self):
        """
        Check the subgraph isomorphism functions.
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]
        vertices2 = [Vertex() for i in range(2)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
        ]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.addVertex(vertex)
        for edge in edges1:
            graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.addVertex(vertex)
        for edge in edges2:
            graph2.addEdge(edge)

        self.assertFalse(graph1.isIsomorphic(graph2))
        self.assertFalse(graph2.isIsomorphic(graph1))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))

        mapList = graph1.findSubgraphIsomorphisms(graph2)
        self.assertTrue(len(mapList) == 10)

        for mapping in mapList:
            self.assertTrue(graph1.isMappingValid(graph2, mapping))
            self.assertTrue(graph1.isMappingValid(graph2, mapping))
Example #23
0
    def test_isomorphism(self):
        """
        Check the graph isomorphism functions.
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]

        vertices2 = [Vertex() for i in range(6)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
            Edge(vertices2[2], vertices2[3]),
            Edge(vertices2[3], vertices2[4]),
            Edge(vertices2[4], vertices2[5]),
        ]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.addVertex(vertex)
        for edge in edges1:
            graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.addVertex(vertex)
        for edge in edges2:
            graph2.addEdge(edge)

        self.assertTrue(graph1.isIsomorphic(graph2))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))
        self.assertTrue(graph2.isIsomorphic(graph1))
        self.assertTrue(graph2.isSubgraphIsomorphic(graph1))
Example #24
0
    def test_merge(self):
        """
        Test the graph merge function to ensure a proper merging of the graph
        is being done.
        """

        vertices1 = [Vertex() for i in range(4)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
        ]

        vertices2 = [Vertex() for i in range(3)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
        ]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.addVertex(vertex)
        for edge in edges1:
            graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.addVertex(vertex)
        for edge in edges2:
            graph2.addEdge(edge)

        graph = graph1.merge(graph2)

        self.assertTrue(
            len(graph1.vertices) + len(graph2.vertices) == len(graph.vertices))
        for vertex1 in vertices1:
            self.assertTrue(vertex1 in graph.vertices)
            for vertex2 in vertex1.edges:
                self.assertTrue(vertex2 in graph.vertices)
        for vertex2 in vertices2:
            self.assertTrue(vertex2 in graph.vertices)
            for vertex1 in vertex2.edges:
                self.assertTrue(vertex1 in vertex2.edges)
Example #25
0
class TestGraph(unittest.TestCase):
    """
    Contains unit tests of the Vertex, Edge, and Graph classes. Most of the
    functionality of Vertex and Edge is only meaningful when part of a graph,
    so we test them all together instead of having separate unit test classes
    for each.
    """
    def setUp(self):
        """
        A function run before each unit test in this class.
        """
        vertices = [Vertex() for _ in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        self.graph = Graph(vertices)
        for edge in edges:
            self.graph.add_edge(edge)

    def test_vertices(self):
        """
        Test that the vertices attribute can be accessed.
        """
        vertices = self.graph.vertices
        self.assertTrue(isinstance(vertices, list))
        self.assertEqual(len(vertices), 6)

    def test_add_vertex(self):
        """
        Test the Graph.add_vertex() method.
        """
        vertex = Vertex()
        self.graph.add_vertex(vertex)
        self.assertTrue(vertex in self.graph.vertices)
        self.assertTrue(vertex.edges == {})

    def test_add_edge(self):
        """
        Test the Graph.add_edge() method.
        """
        vertex1 = Vertex()
        vertex2 = Vertex()
        edge = Edge(vertex1, vertex2)
        try:
            self.graph.add_edge(edge)
            self.fail('Added edge between vertices not in graph to graph.')
        except ValueError:
            pass
        self.graph.add_vertex(vertex1)
        self.graph.add_vertex(vertex2)
        self.graph.add_edge(edge)
        self.assertTrue(vertex1 in self.graph.vertices)
        self.assertTrue(vertex1 in vertex2.edges)
        self.assertTrue(vertex2 in self.graph.vertices)
        self.assertTrue(vertex2 in vertex1.edges)
        self.assertTrue(vertex1.edges[vertex2] is edge)
        self.assertTrue(vertex2.edges[vertex1] is edge)

    def test_get_edge(self):
        """
        Test the Graph.get_edge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[4]
        try:
            self.graph.get_edge(vertex1, vertex2)
            self.fail(
                'Returned an edge between vertices that should not be connected in graph.'
            )
        except ValueError:
            pass
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        edge = self.graph.get_edge(vertex1, vertex2)
        self.assertNotEqual(edge, None)
        self.assertTrue(isinstance(edge, Edge))
        self.assertTrue(vertex1.edges[vertex2] is edge)
        self.assertTrue(vertex2.edges[vertex1] is edge)

    def test_get_edges(self):
        """
        Test the Graph.get_edges() method.
        """
        vertex1 = self.graph.vertices[2]
        edges = self.graph.get_edges(vertex1)
        self.assertTrue(isinstance(edges, dict))
        self.assertEqual(len(edges), 2)
        self.assertTrue(self.graph.vertices[1] in edges)
        self.assertTrue(self.graph.vertices[3] in edges)

    def test_get_all_edges(self):
        """
        Test the Graph.get_all_edges() method.
        """
        edges = self.graph.get_all_edges()
        self.assertTrue(isinstance(edges, list))
        self.assertEqual(len(edges), 5)

    def test_has_vertex(self):
        """
        Test the Graph.has_vertex() method.
        """
        vertex = Vertex()
        self.assertFalse(self.graph.has_vertex(vertex))
        for v in self.graph.vertices:
            self.assertTrue(self.graph.has_vertex(v))

    def test_has_edge(self):
        """
        Test the Graph.has_edge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[4]
        self.assertFalse(self.graph.has_edge(vertex1, vertex2))
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        self.assertTrue(self.graph.has_edge(vertex1, vertex2))

    def test_remove_vertex(self):
        """
        Test the Graph.remove_vertex() method.
        """
        vertex = self.graph.vertices[2]
        self.assertTrue(self.graph.has_vertex(vertex))
        self.graph.remove_vertex(vertex)
        self.assertFalse(self.graph.has_vertex(vertex))
        for v in self.graph.vertices:
            self.assertFalse(vertex in v.edges)

    def test_remove_edge(self):
        """
        Test the Graph.remove_edge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        self.assertTrue(self.graph.has_edge(vertex1, vertex2))
        edge = self.graph.get_edge(vertex1, vertex2)
        self.graph.remove_edge(edge)
        self.assertFalse(vertex1 in vertex2.edges)
        self.assertFalse(vertex2 in vertex1.edges)

    def test_copy(self):
        """
        Test the graph copy function to ensure a complete copy of the graph is
        made while preserving vertices and edges.
        """

        vertices = [Vertex() for _ in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.add_vertex(vertex)
        for edge in edges:
            graph.add_edge(edge)

        graph2 = graph.copy()
        for vertex in graph.vertices:
            self.assertTrue(graph2.has_vertex(vertex))
        for v1 in graph.vertices:
            for v2 in v1.edges:
                self.assertTrue(graph2.has_edge(v1, v2))
                self.assertTrue(graph2.has_edge(v2, v1))
        self.assertTrue(graph2.is_isomorphic(graph))
        self.assertTrue(graph.is_isomorphic(graph2))

    def test_copy_and_map(self):
        """
        Test the returned dictionary points toward equivaalent vertices and edges
        """

        vertices = [Vertex() for _ in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.add_vertex(vertex)
        for edge in edges:
            graph.add_edge(edge)

        graph_dict = graph.copy_and_map()
        graph2 = Graph(vertices=list(graph_dict.values()))

        for vertex in graph.vertices:
            self.assertTrue(graph2.has_vertex(graph_dict[vertex]))
        for v1 in graph.vertices:
            for v2 in v1.edges:
                self.assertTrue(graph2.has_edge(graph_dict[v1],
                                                graph_dict[v2]))
                self.assertTrue(graph2.has_edge(graph_dict[v2],
                                                graph_dict[v1]))
        self.assertTrue(graph2.is_isomorphic(graph))
        self.assertTrue(graph.is_isomorphic(graph2))

    def test_split(self):
        """
        Test the graph split function to ensure a proper splitting of the graph
        is being done.
        """

        vertices = [Vertex() for _ in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.add_vertex(vertex)
        for edge in edges:
            graph.add_edge(edge)

        graphs = graph.split()

        self.assertTrue(len(graphs) == 2)
        self.assertTrue(
            len(graphs[0].vertices) == 4 or len(graphs[0].vertices) == 2)
        self.assertTrue(
            len(graphs[0].vertices) +
            len(graphs[1].vertices) == len(graph.vertices))

    def test_merge(self):
        """
        Test the graph merge function to ensure a proper merging of the graph
        is being done.
        """

        vertices1 = [Vertex() for _ in range(4)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
        ]

        vertices2 = [Vertex() for _ in range(3)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
        ]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.add_vertex(vertex)
        for edge in edges1:
            graph1.add_edge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.add_vertex(vertex)
        for edge in edges2:
            graph2.add_edge(edge)

        graph = graph1.merge(graph2)

        self.assertTrue(
            len(graph1.vertices) + len(graph2.vertices) == len(graph.vertices))
        for vertex1 in vertices1:
            self.assertTrue(vertex1 in graph.vertices)
            for vertex2 in vertex1.edges:
                self.assertTrue(vertex2 in graph.vertices)
        for vertex2 in vertices2:
            self.assertTrue(vertex2 in graph.vertices)
            for vertex1 in vertex2.edges:
                self.assertTrue(vertex1 in vertex2.edges)

    def test_reset_connectivity_values(self):
        """
        Test the Graph.reset_connectivity_values() method.
        """
        self.graph.reset_connectivity_values()
        for vertex in self.graph.vertices:
            self.assertEqual(vertex.connectivity1, -1)
            self.assertEqual(vertex.connectivity2, -1)
            self.assertEqual(vertex.connectivity3, -1)
            self.assertEqual(vertex.sorting_label, -1)

    def test_update_connectivity_values(self):
        """
        Test the Graph.update_connectivity_values() method.
        """
        self.graph.update_connectivity_values()
        self.assertEqual(self.graph.vertices[0].connectivity1, 1)
        self.assertEqual(self.graph.vertices[0].connectivity2, 2)
        self.assertEqual(self.graph.vertices[0].connectivity3, 3)
        self.assertEqual(self.graph.vertices[0].sorting_label, -1)
        self.assertEqual(self.graph.vertices[1].connectivity1, 2)
        self.assertEqual(self.graph.vertices[1].connectivity2, 3)
        self.assertEqual(self.graph.vertices[1].connectivity3, 6)
        self.assertEqual(self.graph.vertices[1].sorting_label, -1)
        self.assertEqual(self.graph.vertices[2].connectivity1, 2)
        self.assertEqual(self.graph.vertices[2].connectivity2, 4)
        self.assertEqual(self.graph.vertices[2].connectivity3, 7)
        self.assertEqual(self.graph.vertices[2].sorting_label, -1)
        self.assertEqual(self.graph.vertices[3].connectivity1, 2)
        self.assertEqual(self.graph.vertices[3].connectivity2, 4)
        self.assertEqual(self.graph.vertices[3].connectivity3, 7)
        self.assertEqual(self.graph.vertices[3].sorting_label, -1)
        self.assertEqual(self.graph.vertices[4].connectivity1, 2)
        self.assertEqual(self.graph.vertices[4].connectivity2, 3)
        self.assertEqual(self.graph.vertices[4].connectivity3, 6)
        self.assertEqual(self.graph.vertices[4].sorting_label, -1)
        self.assertEqual(self.graph.vertices[5].connectivity1, 1)
        self.assertEqual(self.graph.vertices[5].connectivity2, 2)
        self.assertEqual(self.graph.vertices[5].connectivity3, 3)
        self.assertEqual(self.graph.vertices[5].sorting_label, -1)

    def test_sort_vertices(self):
        """
        Test the Graph.sort_vertices() method.
        """
        self.graph.update_connectivity_values()
        self.graph.sort_vertices()
        for vertex1, vertex2 in zip(self.graph.vertices[:-1],
                                    self.graph.vertices[1:]):
            self.assertTrue(vertex1.sorting_label < vertex2.sorting_label)
            self.assertTrue(vertex1.connectivity3 >= vertex2.connectivity3)
            self.assertTrue(vertex1.connectivity2 >= vertex2.connectivity2)
            self.assertTrue(vertex1.connectivity1 >= vertex2.connectivity1)

    def test_vertex_connectivity_values(self):
        """
        Tests the vertex connectivity values as introduced by Morgan (1965).
        
        First CV1 is the number of neighbours
        CV2 is the sum of neighbouring CV1 values
        CV3 is the sum of neighbouring CV2 values
        
        Graph:     Expected (and tested) values:
        
        0-1-2-3-4            1-3-2-2-1   3-4-5-3-2    4-11-7-7-3
        |                    |           |             |
        5                    1           3             4
        
        """
        vertices = [Vertex() for _ in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[1], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.add_vertex(vertex)
        for edge in edges:
            graph.add_edge(edge)

        graph.update_connectivity_values()

        for i, cv_ in enumerate([1, 3, 2, 2, 1, 1]):
            cv = vertices[i].connectivity1
            self.assertEqual(
                cv, cv_,
                "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".
                format(i, cv, cv_))
        for i, cv_ in enumerate([3, 4, 5, 3, 2, 3]):
            cv = vertices[i].connectivity2
            self.assertEqual(
                cv, cv_,
                "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".
                format(i, cv, cv_))
        for i, cv_ in enumerate([4, 11, 7, 7, 3, 4]):
            cv = vertices[i].connectivity3
            self.assertEqual(
                cv, cv_,
                "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".
                format(i, cv, cv_))

    def test_isomorphism(self):
        """
        Check the graph isomorphism functions.
        """

        vertices1 = [Vertex() for _ in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]

        vertices2 = [Vertex() for _ in range(6)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
            Edge(vertices2[2], vertices2[3]),
            Edge(vertices2[3], vertices2[4]),
            Edge(vertices2[4], vertices2[5]),
        ]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.add_vertex(vertex)
        for edge in edges1:
            graph1.add_edge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.add_vertex(vertex)
        for edge in edges2:
            graph2.add_edge(edge)

        self.assertTrue(graph1.is_isomorphic(graph2))
        self.assertTrue(graph1.is_subgraph_isomorphic(graph2))
        self.assertTrue(graph2.is_isomorphic(graph1))
        self.assertTrue(graph2.is_subgraph_isomorphic(graph1))

    def test_isomorphism_disconnected(self):
        """
        Check the graph isomorphism for broken graphs.
        
        This tries to match graphs with a missing bond, 
        eg. [ 0-1-2-3-4  5 ] should match [ 0-1-2-3-4  5 ]
        """

        vertices1 = [Vertex() for _ in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            # Edge(vertices1[4], vertices1[5]),
        ]

        vertices2 = [Vertex() for _ in range(6)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
            Edge(vertices2[2], vertices2[3]),
            Edge(vertices2[3], vertices2[4]),
            # Edge(vertices2[4], vertices2[5]),
        ]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.add_vertex(vertex)
        for edge in edges1:
            graph1.add_edge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.add_vertex(vertex)
        for edge in edges2:
            graph2.add_edge(edge)

        self.assertTrue(graph1.is_isomorphic(graph2))
        self.assertTrue(graph1.is_subgraph_isomorphic(graph2))
        self.assertTrue(graph2.is_isomorphic(graph1))
        self.assertTrue(graph2.is_subgraph_isomorphic(graph1))
        self.assertTrue(len(graph1.find_subgraph_isomorphisms(graph2)) > 0)

    def test_subgraph_isomorphism(self):
        """
        Check the subgraph isomorphism functions.
        """

        vertices1 = [Vertex() for _ in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]
        vertices2 = [Vertex() for _ in range(2)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
        ]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.add_vertex(vertex)
        for edge in edges1:
            graph1.add_edge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.add_vertex(vertex)
        for edge in edges2:
            graph2.add_edge(edge)

        self.assertFalse(graph1.is_isomorphic(graph2))
        self.assertFalse(graph2.is_isomorphic(graph1))
        self.assertTrue(graph1.is_subgraph_isomorphic(graph2))

        map_list = graph1.find_subgraph_isomorphisms(graph2)
        self.assertTrue(len(map_list) == 10)

        for mapping in map_list:
            self.assertTrue(graph1.is_mapping_valid(graph2, mapping))
            self.assertTrue(graph1.is_mapping_valid(graph2, mapping))

    def test_pickle(self):
        """
        Test that a Graph object can be successfully pickled and unpickled
        with no loss of information.
        """

        vertices = [Vertex() for _ in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph0 = Graph()
        for vertex in vertices:
            graph0.add_vertex(vertex)
        for edge in edges:
            graph0.add_edge(edge)
        graph0.update_connectivity_values()

        import pickle
        graph = pickle.loads(pickle.dumps(graph0))

        self.assertEqual(len(graph0.vertices), len(graph.vertices))
        for v1, v2 in zip(graph0.vertices, graph.vertices):
            self.assertEqual(v1.connectivity1, v2.connectivity1)
            self.assertEqual(v1.connectivity2, v2.connectivity2)
            self.assertEqual(v1.connectivity3, v2.connectivity3)
            self.assertEqual(v1.sorting_label, v2.sorting_label)
            self.assertEqual(len(v1.edges), len(v2.edges))
        self.assertTrue(graph0.is_isomorphic(graph))
        self.assertTrue(graph.is_isomorphic(graph0))

    def test_is_cyclic(self):
        """
        Test the Graph.is_cyclic() method.
        """
        self.assertFalse(self.graph.is_cyclic())
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.add_edge(edge)  # To create a cycle
        self.assertTrue(self.graph.is_cyclic())

    def test_is_vertex_in_cycle(self):
        """
        Test the Graph.is_vertex_in_cycle() method.
        """
        for vertex in self.graph.vertices:
            self.assertFalse(self.graph.is_vertex_in_cycle(vertex))
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.add_edge(edge)  # To create a cycle
        for vertex in self.graph.vertices[0:4]:
            self.assertTrue(self.graph.is_vertex_in_cycle(vertex))
        for vertex in self.graph.vertices[4:]:
            self.assertFalse(self.graph.is_vertex_in_cycle(vertex))

    def test_is_edge_in_cycle(self):
        """
        Test the Graph.is_edge_in_cycle() method.
        """
        for vertex1 in self.graph.vertices:
            for vertex2, edge in vertex1.edges.items():
                self.assertFalse(self.graph.is_edge_in_cycle(edge))
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.add_edge(edge)  # To create a cycle
        for vertex1 in self.graph.vertices:
            for vertex2, edge in vertex1.edges.items():
                if self.graph.vertices.index(
                        vertex1) < 4 and self.graph.vertices.index(
                            vertex2) < 4:
                    self.assertTrue(self.graph.is_edge_in_cycle(edge))
                else:
                    self.assertFalse(self.graph.is_edge_in_cycle(edge))

    def test_get_all_cyclic_vertices(self):
        self.assertListEqual(self.graph.get_all_cyclic_vertices(), [])
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.add_edge(edge)  # To create a cycle
        self.assertEqual(len(self.graph.get_all_cyclic_vertices()), 4)

    def test_get_all_polycylic_vertices(self):
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.add_edge(edge)  # To create a cycle
        self.assertListEqual(self.graph.get_all_polycyclic_vertices(), [])
        edge2 = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.add_edge(
            edge2)  # Create another cycle to generate two fused cycles
        self.assertEqual(len(self.graph.get_all_polycyclic_vertices()), 2)
        # Add new vertices and edges to generate a spirocyclic cycle
        vertices = [Vertex() for _ in range(2)]
        for vertex in vertices:
            self.graph.add_vertex(vertex)
        edges = [
            Edge(self.graph.vertices[5], self.graph.vertices[6]),
            Edge(self.graph.vertices[6], self.graph.vertices[7]),
            Edge(self.graph.vertices[5], self.graph.vertices[7]),
        ]
        for edge in edges:
            self.graph.add_edge(edge)
        self.assertEqual(len(self.graph.get_all_polycyclic_vertices()), 3)

    def test_get_all_cycles(self):
        """
        Test the Graph.get_all_cycles() method.
        """
        cycle_list = self.graph.get_all_cycles(self.graph.vertices[0])
        self.assertEqual(len(cycle_list), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.add_edge(edge)  # To create a cycle
        cycle_list = self.graph.get_all_cycles(self.graph.vertices[0])
        self.assertEqual(len(cycle_list), 2)
        self.assertEqual(len(cycle_list[0]), 4)
        self.assertEqual(len(cycle_list[1]), 4)

    def test_get_all_cycles_of_size(self):
        """
        Test the Graph.getRingsOfSize() method
        """
        cycle_list = self.graph.get_all_cycles_of_size(6)
        self.assertEqual(len(cycle_list), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.add_edge(edge)  # To create a cycle of length 4
        edge = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.add_edge(
            edge
        )  # To create a cycle of length 6 and another cycle of length 4
        cycle_list = self.graph.get_all_cycles_of_size(4)
        self.assertEqual(len(cycle_list), 2)
        self.assertEqual(len(cycle_list[0]), 4)
        self.assertEqual(len(cycle_list[1]), 4)

    def test_get_all_simple_cycles_of_size(self):
        """
        Test the Graph.get_all_simple_cycles_of_size() method.
        """
        cycle_list = self.graph.get_all_cycles_of_size(6)
        self.assertEqual(len(cycle_list), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.add_edge(edge)  # To create a cycle of length 4
        edge = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.add_edge(
            edge
        )  # To create a cycle of length 6 and another cycle of length 4
        cycle_list = self.graph.get_all_simple_cycles_of_size(4)
        self.assertEqual(len(cycle_list), 2)
        self.assertEqual(len(cycle_list[0]), 4)
        self.assertEqual(len(cycle_list[1]), 4)
        cycle_list = self.graph.get_all_simple_cycles_of_size(6)
        self.assertEqual(len(cycle_list), 0)

    def test_get_smallest_set_of_smallest_rings(self):
        """
        Test the Graph.get_smallest_set_of_smallest_rings() method.
        """
        cycle_list = self.graph.get_smallest_set_of_smallest_rings()
        self.assertEqual(len(cycle_list), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.add_edge(edge)  # To create a cycle
        cycle_list = self.graph.get_smallest_set_of_smallest_rings()
        self.assertEqual(len(cycle_list), 1)
        self.assertEqual(len(cycle_list[0]), 4)

    def test_get_relevant_cycles(self):
        """
        Test the Graph.get_relevant_cycles() method.
        """
        cycle_list = self.graph.get_relevant_cycles()
        self.assertEqual(len(cycle_list), 0)
        # Create a cycle of length 4
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.add_edge(edge)
        # Create a second cycle of length 4
        edge = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.add_edge(edge)
        # Create a bridge forming multiple cycles of length 4
        edge = Edge(self.graph.vertices[1], self.graph.vertices[4])
        self.graph.add_edge(edge)

        # SSSR should be 3 cycles of length 4
        cycle_list = self.graph.get_smallest_set_of_smallest_rings()
        self.assertEqual(len(cycle_list), 3)
        size_list = sorted([len(cycle) for cycle in cycle_list])
        self.assertEqual(size_list, [4, 4, 4])

        # RC should be 5 cycles of length 4
        cycle_list = self.graph.get_relevant_cycles()
        self.assertEqual(len(cycle_list), 5)
        size_list = sorted([len(cycle) for cycle in cycle_list])
        self.assertEqual(size_list, [4, 4, 4, 4, 4])

    def test_cycle_list_order_sssr(self):
        """
        Test that get_smallest_set_of_smallest_rings return vertices in the proper order.

        There are methods such as symmetry and molecule drawing which rely
        on the fact that subsequent list entries are connected.
        """
        # Create a cycle of length 5
        edge = Edge(self.graph.vertices[0], self.graph.vertices[4])
        self.graph.add_edge(edge)
        # Test SSSR
        sssr = self.graph.get_smallest_set_of_smallest_rings()
        self.assertEqual(len(sssr), 1)
        self.assertEqual(len(sssr[0]), 5)
        for i in range(5):
            self.assertTrue(self.graph.has_edge(sssr[0][i], sssr[0][i - 1]))

    def test_cycle_list_order_relevant_cycles(self):
        """
        Test that get_relevant_cycles return vertices in the proper order.

        There are methods such as symmetry and molecule drawing which rely
        on the fact that subsequent list entries are connected.
        """
        # Create a cycle of length 5
        edge = Edge(self.graph.vertices[0], self.graph.vertices[4])
        self.graph.add_edge(edge)
        # Test RC
        rc = self.graph.get_relevant_cycles()
        self.assertEqual(len(rc), 1)
        self.assertEqual(len(rc[0]), 5)
        for i in range(5):
            self.assertTrue(self.graph.has_edge(rc[0][i], rc[0][i - 1]))

    def test_get_polycyclic_rings(self):
        """
        Test that the Graph.get_polycycles() method returns only polycyclic rings.
        """
        vertices = [Vertex() for _ in range(27)]
        bonds = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6),
                 (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13),
                 (13, 14), (14, 15), (14, 12), (12, 16), (16, 10), (10, 17),
                 (17, 18), (18, 19), (9, 20), (20, 21), (21, 7), (6, 22),
                 (22, 23), (22, 4), (23, 3), (23, 24), (24, 25), (25, 1)]
        edges = []
        for bond in bonds:
            edges.append(Edge(vertices[bond[0]], vertices[bond[1]]))

        graph = Graph()
        for vertex in vertices:
            graph.add_vertex(vertex)
        for edge in edges:
            graph.add_edge(edge)
        graph.update_connectivity_values()

        sssr = graph.get_smallest_set_of_smallest_rings()
        self.assertEqual(len(sssr), 6)
        polycyclic_vertices = set(graph.get_all_polycyclic_vertices())
        expected_polycyclic_vertices = set(
            [vertices[index] for index in [3, 23, 4, 22, 12]])

        self.assertEqual(polycyclic_vertices, expected_polycyclic_vertices)

        continuous_rings = graph.get_polycycles()
        expected_continuous_rings = [
            [vertices[index] for index in [1, 2, 3, 4, 5, 6, 22, 23, 24, 25]],
            # [vertices[index] for index in [7,8,9,21,20]], # This is a nonpolycyclic ring
            [vertices[index] for index in [10, 11, 12, 13, 14, 16]],
        ]

        # Convert to sets for comparison purposes
        continuous_rings = [set(ring) for ring in continuous_rings]
        expected_continuous_rings = [
            set(ring) for ring in expected_continuous_rings
        ]
        for ring in expected_continuous_rings:
            self.assertTrue(ring in continuous_rings)

    def test_get_max_cycle_overlap(self):
        """
        Test that get_max_cycle_overlap returns the correct overlap numbers
        for different graphs.
        """
        def make_graph(edge_inds):
            nvert = max(max(inds) for inds in edge_inds) + 1
            vertices = [Vertex() for _ in range(nvert)]
            graph = Graph(vertices)
            for idx1, idx2 in edge_inds:
                graph.add_edge(Edge(vertices[idx1], vertices[idx2]))
            return graph

        linear = make_graph([(0, 1), (1, 2)])
        mono = make_graph([(0, 1), (0, 2), (1, 2), (2, 3), (3, 4), (3, 5),
                           (4, 5)])
        spiro = make_graph([(0, 1), (0, 2), (1, 2), (2, 3), (2, 4), (3, 4)])
        fused = make_graph([(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)])
        bridged = make_graph([(0, 1), (0, 2), (1, 3), (1, 4), (2, 3), (2, 5),
                              (4, 5)])
        cube = make_graph([(0, 1), (0, 2), (0, 4), (1, 3), (1, 5), (2, 3),
                           (2, 6), (3, 7), (4, 5), (4, 6), (5, 7), (6, 7)])

        self.assertEqual(linear.get_max_cycle_overlap(), 0)
        self.assertEqual(mono.get_max_cycle_overlap(), 0)
        self.assertEqual(spiro.get_max_cycle_overlap(), 1)
        self.assertEqual(fused.get_max_cycle_overlap(), 2)
        self.assertEqual(bridged.get_max_cycle_overlap(), 3)
        # With the current algorithm for maximum overlap determination, a cube
        # only has an overlap of 2, because the set of relevant cycles
        # contains the six four-membered faces. This could be changed in the
        # future.
        self.assertEqual(cube.get_max_cycle_overlap(), 2)

    def test_get_largest_ring(self):
        """
        Test that the Graph.get_polycycles() method returns only polycyclic rings.
        """
        vertices = [Vertex() for _ in range(27)]
        bonds = [(0, 1), (1, 2),
                 (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (9, 10), (10, 11),
                 (11, 12), (12, 13), (13, 14), (14, 15), (12, 16), (10, 17),
                 (17, 18), (18, 19), (9, 20), (20, 21), (6, 22), (22, 23),
                 (22, 8), (8, 4), (23, 3), (23, 24), (24, 25), (25, 1)]
        edges = []
        for bond in bonds:
            edges.append(Edge(vertices[bond[0]], vertices[bond[1]]))

        graph = Graph()
        for vertex in vertices:
            graph.add_vertex(vertex)
        for edge in edges:
            graph.add_edge(edge)
        graph.update_connectivity_values()

        rings = graph.get_polycycles()
        self.assertEqual(len(rings), 1)

        # ensure the last ring doesn't include vertex 8, since it isn't in the
        # longest ring. Try two different items since one might contain the vertex 8
        long_ring = graph.get_largest_ring(rings[0][0])
        long_ring2 = graph.get_largest_ring(rings[0][1])

        if len(long_ring) > len(long_ring2):
            longest_ring = long_ring
        else:
            longest_ring = long_ring2

        self.assertEqual(len(longest_ring), len(rings[0]) - 1)

    def test_sort_cyclic_vertices(self):
        """Test that sort_cyclic_vertices works properly for a valid input."""
        edge = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.add_edge(edge)  # To create a cycle

        # Sort the vertices
        original = list(self.graph.vertices)
        ordered = self.graph.sort_cyclic_vertices(original)

        # Check that we didn't lose any vertices
        self.assertEqual(len(self.graph.vertices), len(ordered),
                         'Sorting changed the number of vertices.')

        # Check that the order is different
        self.assertNotEqual(self.graph.vertices, ordered,
                            'Sorting did not change the order of vertices.')

        # Check that subsequent vertices are connected
        for i in range(5):
            self.assertTrue(self.graph.has_edge(ordered[i], ordered[i - 1]))

    def test_sort_cyclic_vertices_invalid(self):
        """Test that sort_cyclic_vertices raises an error for an invalid input."""
        edge = Edge(self.graph.vertices[0], self.graph.vertices[4])
        self.graph.add_edge(edge)  # To create a cycle

        original = list(self.graph.vertices)

        with self.assertRaisesRegexp(RuntimeError,
                                     'do not comprise a single cycle'):
            self.graph.sort_cyclic_vertices(original)

    def test_sort_cyclic_vertices_noncyclic(self):
        """Test that sort_cyclic_vertices raises an error for a noncyclic input."""
        original = list(self.graph.vertices)
        with self.assertRaisesRegexp(RuntimeError,
                                     'do not comprise a single cycle'):
            self.graph.sort_cyclic_vertices(original)

    def test_sort_cyclic_vertices_unconnected(self):
        """Test that sort_cyclic_vertices raises an error for an unconnected input."""
        self.graph.add_vertex(Vertex())
        original = list(self.graph.vertices)
        with self.assertRaisesRegexp(RuntimeError,
                                     'not all vertices are connected'):
            self.graph.sort_cyclic_vertices(original)
Example #26
0
class TestGraph(unittest.TestCase):
    """
    Contains unit tests of the Vertex, Edge, and Graph classes. Most of the
    functionality of Vertex and Edge is only meaningful when part of a graph,
    so we test them all together instead of having separate unit test classes
    for each.
    """

    def setUp(self):
        """
        A function run before each unit test in this class.
        """
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        self.graph = Graph()
        for vertex in vertices:
            self.graph.addVertex(vertex)
        for edge in edges:
            self.graph.addEdge(edge)

    def test_addVertex(self):
        """
        Test the Graph.addVertex() method.
        """
        vertex = Vertex()
        self.graph.addVertex(vertex)
        self.assertTrue(vertex in self.graph.vertices)
        self.assertTrue(vertex.edges == {})

    def test_addEdge(self):
        """
        Test the Graph.addEdge() method.
        """
        vertex1 = Vertex()
        vertex2 = Vertex()
        edge = Edge(vertex1, vertex2)
        try:
            self.graph.addEdge(edge)
            self.fail("Added edge between vertices not in graph to graph.")
        except ValueError:
            pass
        self.graph.addVertex(vertex1)
        self.graph.addVertex(vertex2)
        self.graph.addEdge(edge)
        self.assertTrue(vertex1 in self.graph.vertices)
        self.assertTrue(vertex1 in vertex2.edges)
        self.assertTrue(vertex2 in self.graph.vertices)
        self.assertTrue(vertex2 in vertex1.edges)
        self.assertTrue(vertex1.edges[vertex2] is edge)
        self.assertTrue(vertex2.edges[vertex1] is edge)

    def test_getEdge(self):
        """
        Test the Graph.getEdge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[4]
        try:
            edge = self.graph.getEdge(vertex1, vertex2)
            self.fail("Returned an edge between vertices that should not be connected in graph.")
        except ValueError:
            pass
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        edge = self.graph.getEdge(vertex1, vertex2)
        self.assertNotEqual(edge, None)
        self.assertTrue(isinstance(edge, Edge))
        self.assertTrue(vertex1.edges[vertex2] is edge)
        self.assertTrue(vertex2.edges[vertex1] is edge)

    def test_getEdges(self):
        """
        Test the Graph.getEdges() method.
        """
        vertex1 = self.graph.vertices[2]
        edges = self.graph.getEdges(vertex1)
        self.assertTrue(isinstance(edges, dict))
        self.assertEqual(len(edges), 2)
        self.assertTrue(self.graph.vertices[1] in edges)
        self.assertTrue(self.graph.vertices[3] in edges)

    def test_hasVertex(self):
        """
        Test the Graph.hasVertex() method.
        """
        vertex = Vertex()
        self.assertFalse(self.graph.hasVertex(vertex))
        for v in self.graph.vertices:
            self.assertTrue(self.graph.hasVertex(v))

    def test_hasEdge(self):
        """
        Test the Graph.hasEdge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[4]
        self.assertFalse(self.graph.hasEdge(vertex1, vertex2))
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        self.assertTrue(self.graph.hasEdge(vertex1, vertex2))

    def test_removeVertex(self):
        """
        Test the Graph.removeVertex() method.
        """
        vertex = self.graph.vertices[2]
        self.assertTrue(self.graph.hasVertex(vertex))
        self.graph.removeVertex(vertex)
        self.assertFalse(self.graph.hasVertex(vertex))
        for v in self.graph.vertices:
            self.assertFalse(vertex in v.edges)

    def test_removeEdge(self):
        """
        Test the Graph.removeEdge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        self.assertTrue(self.graph.hasEdge(vertex1, vertex2))
        edge = self.graph.getEdge(vertex1, vertex2)
        self.graph.removeEdge(edge)
        self.assertFalse(vertex1 in vertex2.edges)
        self.assertFalse(vertex2 in vertex1.edges)

    def test_copy(self):
        """
        Test the graph copy function to ensure a complete copy of the graph is
        made while preserving vertices and edges.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)

        graph2 = graph.copy()
        for vertex in graph.vertices:
            self.assertTrue(graph2.hasVertex(vertex))
        for v1 in graph.vertices:
            for v2 in v1.edges:
                self.assertTrue(graph2.hasEdge(v1, v2))
                self.assertTrue(graph2.hasEdge(v2, v1))
        self.assertTrue(graph2.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph2))

    def test_split(self):
        """
        Test the graph split function to ensure a proper splitting of the graph
        is being done.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)

        graphs = graph.split()

        self.assertTrue(len(graphs) == 2)
        self.assertTrue(len(graphs[0].vertices) == 4 or len(graphs[0].vertices) == 2)
        self.assertTrue(len(graphs[0].vertices) + len(graphs[1].vertices) == len(graph.vertices))

    def test_merge(self):
        """
        Test the graph merge function to ensure a proper merging of the graph
        is being done.
        """

        vertices1 = [Vertex() for i in range(4)]
        edges1 = [Edge(vertices1[0], vertices1[1]), Edge(vertices1[1], vertices1[2]), Edge(vertices1[2], vertices1[3])]

        vertices2 = [Vertex() for i in range(3)]
        edges2 = [Edge(vertices2[0], vertices2[1]), Edge(vertices2[1], vertices2[2])]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.addVertex(vertex)
        for edge in edges1:
            graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.addVertex(vertex)
        for edge in edges2:
            graph2.addEdge(edge)

        graph = graph1.merge(graph2)

        self.assertTrue(len(graph1.vertices) + len(graph2.vertices) == len(graph.vertices))
        for vertex1 in vertices1:
            self.assertTrue(vertex1 in graph.vertices)
            for vertex2 in vertex1.edges:
                self.assertTrue(vertex2 in graph.vertices)
        for vertex2 in vertices2:
            self.assertTrue(vertex2 in graph.vertices)
            for vertex1 in vertex2.edges:
                self.assertTrue(vertex1 in vertex2.edges)

    def test_resetConnectivityValues(self):
        """
        Test the Graph.resetConnectivityValues() method.
        """
        self.graph.resetConnectivityValues()
        for vertex in self.graph.vertices:
            self.assertEqual(vertex.connectivity, -1)
            self.assertEqual(vertex.sortingLabel, -1)

    def test_updateConnectivityValues(self):
        """
        Test the Graph.updateConnectivityValues() method.
        """
        self.graph.updateConnectivityValues()
        self.assertEqual(self.graph.vertices[0].connectivity, 10)
        self.assertEqual(self.graph.vertices[0].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[1].connectivity, 19)
        self.assertEqual(self.graph.vertices[1].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[2].connectivity, 23)
        self.assertEqual(self.graph.vertices[2].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[3].connectivity, 23)
        self.assertEqual(self.graph.vertices[3].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[4].connectivity, 19)
        self.assertEqual(self.graph.vertices[4].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[5].connectivity, 10)
        self.assertEqual(self.graph.vertices[5].sortingLabel, -1)

    def test_sortVertices(self):
        """
        Test the Graph.sortVertices() method.
        """
        self.graph.updateConnectivityValues()
        self.graph.sortVertices()
        for vertex1, vertex2 in zip(self.graph.vertices[:-1], self.graph.vertices[1:]):
            self.assertTrue(vertex1.sortingLabel < vertex2.sortingLabel)
            self.assertTrue(vertex1.connectivity >= vertex2.connectivity)

    def test_vertex_connectivity_values(self):
        """
        Tests the vertex connectivity values as introduced by Morgan (1965).
        
        Graph:     Expected (and tested) values:
        
        0-1-2-3-4            1-3-2-2-1   3-4-5-3-2    4-11-7-7-3
        |                    |           |             |
        5                    1           3             4
        
                                # = 3     # = 4         # = 4
                                         *selected*
        """
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[1], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)

        graph.updateConnectivityValues()

        for i, cv_ in enumerate([11, 15, 18, 10, 7, 11]):
            cv = vertices[i].connectivity
            self.assertEqual(cv, cv_, "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".format(i, cv, cv_))

    def test_isomorphism(self):
        """
        Check the graph isomorphism functions.
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]

        vertices2 = [Vertex() for i in range(6)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
            Edge(vertices2[2], vertices2[3]),
            Edge(vertices2[3], vertices2[4]),
            Edge(vertices2[4], vertices2[5]),
        ]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.addVertex(vertex)
        for edge in edges1:
            graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.addVertex(vertex)
        for edge in edges2:
            graph2.addEdge(edge)

        self.assertTrue(graph1.isIsomorphic(graph2))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))
        self.assertTrue(graph2.isIsomorphic(graph1))
        self.assertTrue(graph2.isSubgraphIsomorphic(graph1))

    def test_subgraphIsomorphism(self):
        """
        Check the subgraph isomorphism functions.
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]
        vertices2 = [Vertex() for i in range(2)]
        edges2 = [Edge(vertices2[0], vertices2[1])]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.addVertex(vertex)
        for edge in edges1:
            graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.addVertex(vertex)
        for edge in edges2:
            graph2.addEdge(edge)

        self.assertFalse(graph1.isIsomorphic(graph2))
        self.assertFalse(graph2.isIsomorphic(graph1))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))

        mapList = graph1.findSubgraphIsomorphisms(graph2)
        self.assertTrue(len(mapList) == 10)

        for mapping in mapList:
            self.assertTrue(graph1.isMappingValid(graph2, mapping))
            self.assertTrue(graph1.isMappingValid(graph2, mapping))

    def test_pickle(self):
        """
        Test that a Graph object can be successfully pickled and unpickled
        with no loss of information.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph0 = Graph()
        for vertex in vertices:
            graph0.addVertex(vertex)
        for edge in edges:
            graph0.addEdge(edge)
        graph0.updateConnectivityValues()

        import cPickle

        graph = cPickle.loads(cPickle.dumps(graph0))

        self.assertEqual(len(graph0.vertices), len(graph.vertices))
        for v1, v2 in zip(graph0.vertices, graph.vertices):
            self.assertEqual(v1.connectivity, v2.connectivity)
            self.assertEqual(v1.sortingLabel, v2.sortingLabel)
            self.assertEqual(len(v1.edges), len(v2.edges))
        self.assertTrue(graph0.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph0))

    def test_isCyclic(self):
        """
        Test the Graph.isCyclic() method.
        """
        self.assertFalse(self.graph.isCyclic())
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        self.assertTrue(self.graph.isCyclic())

    def test_isVertexInCycle(self):
        """
        Test the Graph.isVertexInCycle() method.
        """
        for vertex in self.graph.vertices:
            self.assertFalse(self.graph.isVertexInCycle(vertex))
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        for vertex in self.graph.vertices[0:4]:
            self.assertTrue(self.graph.isVertexInCycle(vertex))
        for vertex in self.graph.vertices[4:]:
            self.assertFalse(self.graph.isVertexInCycle(vertex))

    def test_isEdgeInCycle(self):
        """
        Test the Graph.isEdgeInCycle() method.
        """
        for vertex1 in self.graph.vertices:
            for vertex2, edge in vertex1.edges.items():
                self.assertFalse(self.graph.isEdgeInCycle(edge))
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        for vertex1 in self.graph.vertices:
            for vertex2, edge in vertex1.edges.items():
                if self.graph.vertices.index(vertex1) < 4 and self.graph.vertices.index(vertex2) < 4:
                    self.assertTrue(self.graph.isEdgeInCycle(edge))
                else:
                    self.assertFalse(self.graph.isEdgeInCycle(edge))

    def test_getAllCyclicVertices(self):
        self.assertListEqual(self.graph.getAllCyclicVertices(), [])
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        self.assertEqual(len(self.graph.getAllCyclicVertices()), 4)

    def test_getAllPolycylicVertices(self):
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        self.assertListEqual(self.graph.getAllPolycyclicVertices(), [])
        edge2 = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.addEdge(edge2)  # Create another cycle to generate two fused cycles
        self.assertEqual(len(self.graph.getAllPolycyclicVertices()), 2)
        # Add new vertices and edges to generate a spirocyclic cycle
        vertices = [Vertex() for i in range(2)]
        for vertex in vertices:
            self.graph.addVertex(vertex)
        edges = [
            Edge(self.graph.vertices[5], self.graph.vertices[6]),
            Edge(self.graph.vertices[6], self.graph.vertices[7]),
            Edge(self.graph.vertices[5], self.graph.vertices[7]),
        ]
        for edge in edges:
            self.graph.addEdge(edge)
        self.assertEqual(len(self.graph.getAllPolycyclicVertices()), 3)

    def test_getAllCycles(self):
        """
        Test the Graph.getAllCycles() method.
        """
        cycleList = self.graph.getAllCycles(self.graph.vertices[0])
        self.assertEqual(len(cycleList), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        cycleList = self.graph.getAllCycles(self.graph.vertices[0])
        self.assertEqual(len(cycleList), 2)
        self.assertEqual(len(cycleList[0]), 4)
        self.assertEqual(len(cycleList[1]), 4)

    def test_getSmallestSetOfSmallestRings(self):
        """
        Test the Graph.getSmallestSetOfSmallestRings() method.
        """
        cycleList = self.graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(cycleList), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        cycleList = self.graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(cycleList), 1)
        self.assertEqual(len(cycleList[0]), 4)
Example #27
0
class TestGraph(unittest.TestCase):
    """
    Contains unit tests of the Vertex, Edge, and Graph classes. Most of the
    functionality of Vertex and Edge is only meaningful when part of a graph,
    so we test them all together instead of having separate unit test classes
    for each.
    """
    def setUp(self):
        """
        A function run before each unit test in this class.
        """
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        self.graph = Graph()
        for vertex in vertices:
            self.graph.addVertex(vertex)
        for edge in edges:
            self.graph.addEdge(edge)

    def test_addVertex(self):
        """
        Test the Graph.addVertex() method.
        """
        vertex = Vertex()
        self.graph.addVertex(vertex)
        self.assertTrue(vertex in self.graph.vertices)
        self.assertTrue(vertex.edges == {})

    def test_addEdge(self):
        """
        Test the Graph.addEdge() method.
        """
        vertex1 = Vertex()
        vertex2 = Vertex()
        edge = Edge(vertex1, vertex2)
        try:
            self.graph.addEdge(edge)
            self.fail('Added edge between vertices not in graph to graph.')
        except ValueError:
            pass
        self.graph.addVertex(vertex1)
        self.graph.addVertex(vertex2)
        self.graph.addEdge(edge)
        self.assertTrue(vertex1 in self.graph.vertices)
        self.assertTrue(vertex1 in vertex2.edges)
        self.assertTrue(vertex2 in self.graph.vertices)
        self.assertTrue(vertex2 in vertex1.edges)
        self.assertTrue(vertex1.edges[vertex2] is edge)
        self.assertTrue(vertex2.edges[vertex1] is edge)

    def test_getEdge(self):
        """
        Test the Graph.getEdge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[4]
        try:
            edge = self.graph.getEdge(vertex1, vertex2)
            self.fail(
                'Returned an edge between vertices that should not be connected in graph.'
            )
        except ValueError:
            pass
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        edge = self.graph.getEdge(vertex1, vertex2)
        self.assertNotEqual(edge, None)
        self.assertTrue(isinstance(edge, Edge))
        self.assertTrue(vertex1.edges[vertex2] is edge)
        self.assertTrue(vertex2.edges[vertex1] is edge)

    def test_getEdges(self):
        """
        Test the Graph.getEdges() method.
        """
        vertex1 = self.graph.vertices[2]
        edges = self.graph.getEdges(vertex1)
        self.assertTrue(isinstance(edges, dict))
        self.assertEqual(len(edges), 2)
        self.assertTrue(self.graph.vertices[1] in edges)
        self.assertTrue(self.graph.vertices[3] in edges)

    def test_hasVertex(self):
        """
        Test the Graph.hasVertex() method.
        """
        vertex = Vertex()
        self.assertFalse(self.graph.hasVertex(vertex))
        for v in self.graph.vertices:
            self.assertTrue(self.graph.hasVertex(v))

    def test_hasEdge(self):
        """
        Test the Graph.hasEdge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[4]
        self.assertFalse(self.graph.hasEdge(vertex1, vertex2))
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        self.assertTrue(self.graph.hasEdge(vertex1, vertex2))

    def test_removeVertex(self):
        """
        Test the Graph.removeVertex() method.
        """
        vertex = self.graph.vertices[2]
        self.assertTrue(self.graph.hasVertex(vertex))
        self.graph.removeVertex(vertex)
        self.assertFalse(self.graph.hasVertex(vertex))
        for v in self.graph.vertices:
            self.assertFalse(vertex in v.edges)

    def test_removeEdge(self):
        """
        Test the Graph.removeEdge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        self.assertTrue(self.graph.hasEdge(vertex1, vertex2))
        edge = self.graph.getEdge(vertex1, vertex2)
        self.graph.removeEdge(edge)
        self.assertFalse(vertex1 in vertex2.edges)
        self.assertFalse(vertex2 in vertex1.edges)

    def test_copy(self):
        """
        Test the graph copy function to ensure a complete copy of the graph is
        made while preserving vertices and edges.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)

        graph2 = graph.copy()
        for vertex in graph.vertices:
            self.assertTrue(graph2.hasVertex(vertex))
        for v1 in graph.vertices:
            for v2 in v1.edges:
                self.assertTrue(graph2.hasEdge(v1, v2))
                self.assertTrue(graph2.hasEdge(v2, v1))
        self.assertTrue(graph2.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph2))

    def test_split(self):
        """
        Test the graph split function to ensure a proper splitting of the graph
        is being done.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)

        graphs = graph.split()

        self.assertTrue(len(graphs) == 2)
        self.assertTrue(
            len(graphs[0].vertices) == 4 or len(graphs[0].vertices) == 2)
        self.assertTrue(
            len(graphs[0].vertices) +
            len(graphs[1].vertices) == len(graph.vertices))

    def test_merge(self):
        """
        Test the graph merge function to ensure a proper merging of the graph
        is being done.
        """

        vertices1 = [Vertex() for i in range(4)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
        ]

        vertices2 = [Vertex() for i in range(3)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
        ]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.addVertex(vertex)
        for edge in edges1:
            graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.addVertex(vertex)
        for edge in edges2:
            graph2.addEdge(edge)

        graph = graph1.merge(graph2)

        self.assertTrue(
            len(graph1.vertices) + len(graph2.vertices) == len(graph.vertices))
        for vertex1 in vertices1:
            self.assertTrue(vertex1 in graph.vertices)
            for vertex2 in vertex1.edges:
                self.assertTrue(vertex2 in graph.vertices)
        for vertex2 in vertices2:
            self.assertTrue(vertex2 in graph.vertices)
            for vertex1 in vertex2.edges:
                self.assertTrue(vertex1 in vertex2.edges)

    def test_resetConnectivityValues(self):
        """
        Test the Graph.resetConnectivityValues() method.
        """
        self.graph.resetConnectivityValues()
        for vertex in self.graph.vertices:
            self.assertEqual(vertex.connectivity1, -1)
            self.assertEqual(vertex.connectivity2, -1)
            self.assertEqual(vertex.connectivity3, -1)
            self.assertEqual(vertex.sortingLabel, -1)

    def test_updateConnectivityValues(self):
        """
        Test the Graph.updateConnectivityValues() method.
        """
        self.graph.updateConnectivityValues()
        self.assertEqual(self.graph.vertices[0].connectivity1, 1)
        self.assertEqual(self.graph.vertices[0].connectivity2, 2)
        self.assertEqual(self.graph.vertices[0].connectivity3, 3)
        self.assertEqual(self.graph.vertices[0].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[1].connectivity1, 2)
        self.assertEqual(self.graph.vertices[1].connectivity2, 3)
        self.assertEqual(self.graph.vertices[1].connectivity3, 6)
        self.assertEqual(self.graph.vertices[1].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[2].connectivity1, 2)
        self.assertEqual(self.graph.vertices[2].connectivity2, 4)
        self.assertEqual(self.graph.vertices[2].connectivity3, 7)
        self.assertEqual(self.graph.vertices[2].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[3].connectivity1, 2)
        self.assertEqual(self.graph.vertices[3].connectivity2, 4)
        self.assertEqual(self.graph.vertices[3].connectivity3, 7)
        self.assertEqual(self.graph.vertices[3].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[4].connectivity1, 2)
        self.assertEqual(self.graph.vertices[4].connectivity2, 3)
        self.assertEqual(self.graph.vertices[4].connectivity3, 6)
        self.assertEqual(self.graph.vertices[4].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[5].connectivity1, 1)
        self.assertEqual(self.graph.vertices[5].connectivity2, 2)
        self.assertEqual(self.graph.vertices[5].connectivity3, 3)
        self.assertEqual(self.graph.vertices[5].sortingLabel, -1)

    def test_sortVertices(self):
        """
        Test the Graph.sortVertices() method.
        """
        self.graph.updateConnectivityValues()
        self.graph.sortVertices()
        for vertex1, vertex2 in zip(self.graph.vertices[:-1],
                                    self.graph.vertices[1:]):
            self.assertTrue(vertex1.sortingLabel < vertex2.sortingLabel)
            self.assertTrue(vertex1.connectivity3 >= vertex2.connectivity3)
            self.assertTrue(vertex1.connectivity2 >= vertex2.connectivity2)
            self.assertTrue(vertex1.connectivity1 >= vertex2.connectivity1)

    def test_vertex_connectivity_values(self):
        """
        Tests the vertex connectivity values as introduced by Morgan (1965).
        
        First CV1 is the number of neighbours
        CV2 is the sum of neighbouring CV1 values
        CV3 is the sum of neighbouring CV2 values
        
        Graph:     Expected (and tested) values:
        
        0-1-2-3-4            1-3-2-2-1   3-4-5-3-2    4-11-7-7-3
        |                    |           |             |
        5                    1           3             4
        
        """
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[1], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)

        graph.updateConnectivityValues()

        for i, cv_ in enumerate([1, 3, 2, 2, 1, 1]):
            cv = vertices[i].connectivity1
            self.assertEqual(
                cv, cv_,
                "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".
                format(i, cv, cv_))
        for i, cv_ in enumerate([3, 4, 5, 3, 2, 3]):
            cv = vertices[i].connectivity2
            self.assertEqual(
                cv, cv_,
                "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".
                format(i, cv, cv_))
        for i, cv_ in enumerate([4, 11, 7, 7, 3, 4]):
            cv = vertices[i].connectivity3
            self.assertEqual(
                cv, cv_,
                "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".
                format(i, cv, cv_))

    def test_isomorphism(self):
        """
        Check the graph isomorphism functions.
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]

        vertices2 = [Vertex() for i in range(6)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
            Edge(vertices2[2], vertices2[3]),
            Edge(vertices2[3], vertices2[4]),
            Edge(vertices2[4], vertices2[5]),
        ]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.addVertex(vertex)
        for edge in edges1:
            graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.addVertex(vertex)
        for edge in edges2:
            graph2.addEdge(edge)

        self.assertTrue(graph1.isIsomorphic(graph2))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))
        self.assertTrue(graph2.isIsomorphic(graph1))
        self.assertTrue(graph2.isSubgraphIsomorphic(graph1))

    def test_subgraphIsomorphism(self):
        """
        Check the subgraph isomorphism functions.
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]
        vertices2 = [Vertex() for i in range(2)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
        ]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.addVertex(vertex)
        for edge in edges1:
            graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.addVertex(vertex)
        for edge in edges2:
            graph2.addEdge(edge)

        self.assertFalse(graph1.isIsomorphic(graph2))
        self.assertFalse(graph2.isIsomorphic(graph1))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))

        mapList = graph1.findSubgraphIsomorphisms(graph2)
        self.assertTrue(len(mapList) == 10)

        for mapping in mapList:
            self.assertTrue(graph1.isMappingValid(graph2, mapping))
            self.assertTrue(graph1.isMappingValid(graph2, mapping))

    def test_pickle(self):
        """
        Test that a Graph object can be successfully pickled and unpickled
        with no loss of information.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph0 = Graph()
        for vertex in vertices:
            graph0.addVertex(vertex)
        for edge in edges:
            graph0.addEdge(edge)
        graph0.updateConnectivityValues()

        import cPickle
        graph = cPickle.loads(cPickle.dumps(graph0))

        self.assertEqual(len(graph0.vertices), len(graph.vertices))
        for v1, v2 in zip(graph0.vertices, graph.vertices):
            self.assertEqual(v1.connectivity1, v2.connectivity1)
            self.assertEqual(v1.connectivity2, v2.connectivity2)
            self.assertEqual(v1.connectivity3, v2.connectivity3)
            self.assertEqual(v1.sortingLabel, v2.sortingLabel)
            self.assertEqual(len(v1.edges), len(v2.edges))
        self.assertTrue(graph0.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph0))

    def test_isCyclic(self):
        """
        Test the Graph.isCyclic() method.
        """
        self.assertFalse(self.graph.isCyclic())
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        self.assertTrue(self.graph.isCyclic())

    def test_isVertexInCycle(self):
        """
        Test the Graph.isVertexInCycle() method.
        """
        for vertex in self.graph.vertices:
            self.assertFalse(self.graph.isVertexInCycle(vertex))
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        for vertex in self.graph.vertices[0:4]:
            self.assertTrue(self.graph.isVertexInCycle(vertex))
        for vertex in self.graph.vertices[4:]:
            self.assertFalse(self.graph.isVertexInCycle(vertex))

    def test_isEdgeInCycle(self):
        """
        Test the Graph.isEdgeInCycle() method.
        """
        for vertex1 in self.graph.vertices:
            for vertex2, edge in vertex1.edges.items():
                self.assertFalse(self.graph.isEdgeInCycle(edge))
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        for vertex1 in self.graph.vertices:
            for vertex2, edge in vertex1.edges.items():
                if self.graph.vertices.index(
                        vertex1) < 4 and self.graph.vertices.index(
                            vertex2) < 4:
                    self.assertTrue(self.graph.isEdgeInCycle(edge))
                else:
                    self.assertFalse(self.graph.isEdgeInCycle(edge))

    def test_getAllCyclicVertices(self):
        self.assertListEqual(self.graph.getAllCyclicVertices(), [])
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        self.assertEqual(len(self.graph.getAllCyclicVertices()), 4)

    def test_getAllPolycylicVertices(self):
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        self.assertListEqual(self.graph.getAllPolycyclicVertices(), [])
        edge2 = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.addEdge(
            edge2)  # Create another cycle to generate two fused cycles
        self.assertEqual(len(self.graph.getAllPolycyclicVertices()), 2)
        # Add new vertices and edges to generate a spirocyclic cycle
        vertices = [Vertex() for i in range(2)]
        for vertex in vertices:
            self.graph.addVertex(vertex)
        edges = [
            Edge(self.graph.vertices[5], self.graph.vertices[6]),
            Edge(self.graph.vertices[6], self.graph.vertices[7]),
            Edge(self.graph.vertices[5], self.graph.vertices[7]),
        ]
        for edge in edges:
            self.graph.addEdge(edge)
        self.assertEqual(len(self.graph.getAllPolycyclicVertices()), 3)

    def test_getAllCycles(self):
        """
        Test the Graph.getAllCycles() method.
        """
        cycleList = self.graph.getAllCycles(self.graph.vertices[0])
        self.assertEqual(len(cycleList), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        cycleList = self.graph.getAllCycles(self.graph.vertices[0])
        self.assertEqual(len(cycleList), 2)
        self.assertEqual(len(cycleList[0]), 4)
        self.assertEqual(len(cycleList[1]), 4)

    def test_getSmallestSetOfSmallestRings(self):
        """
        Test the Graph.getSmallestSetOfSmallestRings() method.
        """
        cycleList = self.graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(cycleList), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        cycleList = self.graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(cycleList), 1)
        self.assertEqual(len(cycleList[0]), 4)
class TestGraph(unittest.TestCase):
    """
    Contains unit tests of the Vertex, Edge, and Graph classes. Most of the
    functionality of Vertex and Edge is only meaningful when part of a graph,
    so we test them all together instead of having separate unit test classes
    for each.
    """

    def setUp(self):
        """
        A function run before each unit test in this class.
        """
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]
        
        self.graph = Graph(vertices)
        for edge in edges: self.graph.addEdge(edge)

    def test_vertices(self):
        """
        Test that the vertices attribute can be accessed.
        """
        vertices = self.graph.vertices
        self.assertTrue(isinstance(vertices, list))
        self.assertEqual(len(vertices), 6)

    def test_addVertex(self):
        """
        Test the Graph.addVertex() method.
        """
        vertex = Vertex()
        self.graph.addVertex(vertex)
        self.assertTrue(vertex in self.graph.vertices)
        self.assertTrue(vertex.edges == {})
        
    def test_addEdge(self):
        """
        Test the Graph.addEdge() method.
        """
        vertex1 = Vertex(); vertex2 = Vertex(); edge = Edge(vertex1, vertex2)
        try:
            self.graph.addEdge(edge)
            self.fail('Added edge between vertices not in graph to graph.')
        except ValueError:
            pass
        self.graph.addVertex(vertex1)
        self.graph.addVertex(vertex2)
        self.graph.addEdge(edge)
        self.assertTrue(vertex1 in self.graph.vertices)
        self.assertTrue(vertex1 in vertex2.edges)
        self.assertTrue(vertex2 in self.graph.vertices)
        self.assertTrue(vertex2 in vertex1.edges)
        self.assertTrue(vertex1.edges[vertex2] is edge)
        self.assertTrue(vertex2.edges[vertex1] is edge)
        
    def test_getEdge(self):
        """
        Test the Graph.getEdge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[4]
        try:
            edge = self.graph.getEdge(vertex1, vertex2)
            self.fail('Returned an edge between vertices that should not be connected in graph.')
        except ValueError:
            pass
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        edge = self.graph.getEdge(vertex1, vertex2)
        self.assertNotEqual(edge, None)
        self.assertTrue(isinstance(edge, Edge))
        self.assertTrue(vertex1.edges[vertex2] is edge)
        self.assertTrue(vertex2.edges[vertex1] is edge)

    def test_getEdges(self):
        """
        Test the Graph.getEdges() method.
        """
        vertex1 = self.graph.vertices[2]
        edges = self.graph.getEdges(vertex1)
        self.assertTrue(isinstance(edges, dict))
        self.assertEqual(len(edges), 2)
        self.assertTrue(self.graph.vertices[1] in edges)
        self.assertTrue(self.graph.vertices[3] in edges)

    def test_getAllEdges(self):
        """
        Test the Graph.getAllEdges() method.
        """
        edges = self.graph.getAllEdges()
        self.assertTrue(isinstance(edges, list))
        self.assertEqual(len(edges), 5)

    def test_hasVertex(self):
        """
        Test the Graph.hasVertex() method.
        """
        vertex = Vertex()
        self.assertFalse(self.graph.hasVertex(vertex))
        for v in self.graph.vertices:
            self.assertTrue(self.graph.hasVertex(v))

    def test_hasEdge(self):
        """
        Test the Graph.hasEdge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[4]
        self.assertFalse(self.graph.hasEdge(vertex1, vertex2))
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        self.assertTrue(self.graph.hasEdge(vertex1, vertex2))

    def test_removeVertex(self):
        """
        Test the Graph.removeVertex() method.
        """
        vertex = self.graph.vertices[2]
        self.assertTrue(self.graph.hasVertex(vertex))
        self.graph.removeVertex(vertex)
        self.assertFalse(self.graph.hasVertex(vertex))
        for v in self.graph.vertices:
            self.assertFalse(vertex in v.edges)

    def test_removeEdge(self):
        """
        Test the Graph.removeEdge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        self.assertTrue(self.graph.hasEdge(vertex1, vertex2))
        edge = self.graph.getEdge(vertex1, vertex2)
        self.graph.removeEdge(edge)
        self.assertFalse(vertex1 in vertex2.edges)
        self.assertFalse(vertex2 in vertex1.edges)
    
    def test_copy(self):
        """
        Test the graph copy function to ensure a complete copy of the graph is
        made while preserving vertices and edges.
        """
        
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)
        
        graph2 = graph.copy()
        for vertex in graph.vertices:
            self.assertTrue(graph2.hasVertex(vertex))
        for v1 in graph.vertices:
            for v2 in v1.edges:
                self.assertTrue(graph2.hasEdge(v1, v2))
                self.assertTrue(graph2.hasEdge(v2, v1))
        self.assertTrue(graph2.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph2))

    def test_copyAndMap(self):
        """
        Test the returned dictionary points toward equivaalent vertices and edges
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)

        graphDict = graph.copyAndMap()
        graph2 = Graph(vertices = graphDict.values())

        for vertex in graph.vertices:
            self.assertTrue(graph2.hasVertex(graphDict[vertex]))
        for v1 in graph.vertices:
            for v2 in v1.edges:
                self.assertTrue(graph2.hasEdge(graphDict[v1], graphDict[v2]))
                self.assertTrue(graph2.hasEdge(graphDict[v2], graphDict[v1]))
        self.assertTrue(graph2.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph2))

    def test_split(self):
        """
        Test the graph split function to ensure a proper splitting of the graph
        is being done.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)
        
        graphs = graph.split()
        
        self.assertTrue(len(graphs) == 2)
        self.assertTrue(len(graphs[0].vertices) == 4 or len(graphs[0].vertices) == 2)
        self.assertTrue(len(graphs[0].vertices) + len(graphs[1].vertices) == len(graph.vertices))
    
    def test_merge(self):
        """
        Test the graph merge function to ensure a proper merging of the graph
        is being done.
        """

        vertices1 = [Vertex() for i in range(4)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
        ]

        vertices2 = [Vertex() for i in range(3)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
        ]

        graph1 = Graph()
        for vertex in vertices1: graph1.addVertex(vertex)
        for edge in edges1: graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2: graph2.addVertex(vertex)
        for edge in edges2: graph2.addEdge(edge)

        graph = graph1.merge(graph2)

        self.assertTrue(len(graph1.vertices) + len(graph2.vertices) == len(graph.vertices))
        for vertex1 in vertices1:
            self.assertTrue(vertex1 in graph.vertices)
            for vertex2 in vertex1.edges:
                self.assertTrue(vertex2 in graph.vertices)
        for vertex2 in vertices2:
            self.assertTrue(vertex2 in graph.vertices)
            for vertex1 in vertex2.edges:
                self.assertTrue(vertex1 in vertex2.edges)
    
    def test_resetConnectivityValues(self):
        """
        Test the Graph.resetConnectivityValues() method.
        """
        self.graph.resetConnectivityValues()
        for vertex in self.graph.vertices:
            self.assertEqual(vertex.connectivity1, -1)
            self.assertEqual(vertex.connectivity2, -1)
            self.assertEqual(vertex.connectivity3, -1)
            self.assertEqual(vertex.sortingLabel, -1)
    
    def test_updateConnectivityValues(self):
        """
        Test the Graph.updateConnectivityValues() method.
        """
        self.graph.updateConnectivityValues()
        self.assertEqual(self.graph.vertices[0].connectivity1, 1)
        self.assertEqual(self.graph.vertices[0].connectivity2, 2)
        self.assertEqual(self.graph.vertices[0].connectivity3, 3)
        self.assertEqual(self.graph.vertices[0].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[1].connectivity1, 2)
        self.assertEqual(self.graph.vertices[1].connectivity2, 3)
        self.assertEqual(self.graph.vertices[1].connectivity3, 6)
        self.assertEqual(self.graph.vertices[1].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[2].connectivity1, 2)
        self.assertEqual(self.graph.vertices[2].connectivity2, 4)
        self.assertEqual(self.graph.vertices[2].connectivity3, 7)
        self.assertEqual(self.graph.vertices[2].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[3].connectivity1, 2)
        self.assertEqual(self.graph.vertices[3].connectivity2, 4)
        self.assertEqual(self.graph.vertices[3].connectivity3, 7)
        self.assertEqual(self.graph.vertices[3].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[4].connectivity1, 2)
        self.assertEqual(self.graph.vertices[4].connectivity2, 3)
        self.assertEqual(self.graph.vertices[4].connectivity3, 6)
        self.assertEqual(self.graph.vertices[4].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[5].connectivity1, 1)
        self.assertEqual(self.graph.vertices[5].connectivity2, 2)
        self.assertEqual(self.graph.vertices[5].connectivity3, 3)
        self.assertEqual(self.graph.vertices[5].sortingLabel, -1)
    
    def test_sortVertices(self):
        """
        Test the Graph.sortVertices() method.
        """
        self.graph.updateConnectivityValues()
        self.graph.sortVertices()
        for vertex1, vertex2 in zip(self.graph.vertices[:-1], self.graph.vertices[1:]):
            self.assertTrue(vertex1.sortingLabel < vertex2.sortingLabel)
            self.assertTrue(vertex1.connectivity3 >= vertex2.connectivity3)
            self.assertTrue(vertex1.connectivity2 >= vertex2.connectivity2)
            self.assertTrue(vertex1.connectivity1 >= vertex2.connectivity1)
    
    def test_vertex_connectivity_values(self):
        """
        Tests the vertex connectivity values as introduced by Morgan (1965).
        
        First CV1 is the number of neighbours
        CV2 is the sum of neighbouring CV1 values
        CV3 is the sum of neighbouring CV2 values
        
        Graph:     Expected (and tested) values:
        
        0-1-2-3-4            1-3-2-2-1   3-4-5-3-2    4-11-7-7-3
        |                    |           |             |
        5                    1           3             4
        
        """
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[1], vertices[5]),
        ]
        
        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)
    
        graph.updateConnectivityValues()

        for i,cv_ in enumerate([1,3,2,2,1,1]):
            cv = vertices[i].connectivity1
            self.assertEqual(cv, cv_, "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".format(i,cv,cv_))
        for i,cv_ in enumerate([3,4,5,3,2,3]):
            cv = vertices[i].connectivity2
            self.assertEqual(cv, cv_, "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".format(i,cv,cv_))
        for i,cv_ in enumerate([4,11,7,7,3,4]):
            cv = vertices[i].connectivity3
            self.assertEqual(cv, cv_, "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".format(i,cv,cv_))

    def test_isomorphism(self):
        """
        Check the graph isomorphism functions.
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]

        vertices2 = [Vertex() for i in range(6)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
            Edge(vertices2[2], vertices2[3]),
            Edge(vertices2[3], vertices2[4]),
            Edge(vertices2[4], vertices2[5]),
        ]

        graph1 = Graph()
        for vertex in vertices1: graph1.addVertex(vertex)
        for edge in edges1: graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2: graph2.addVertex(vertex)
        for edge in edges2: graph2.addEdge(edge)

        self.assertTrue(graph1.isIsomorphic(graph2))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))
        self.assertTrue(graph2.isIsomorphic(graph1))
        self.assertTrue(graph2.isSubgraphIsomorphic(graph1))


    def test_isomorphism_disconnected(self):
        """
        Check the graph isomorphism for broken graphs.
        
        This tries to match graphs with a missing bond, 
        eg. [ 0-1-2-3-4  5 ] should match [ 0-1-2-3-4  5 ]
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            #Edge(vertices1[4], vertices1[5]),
        ]

        vertices2 = [Vertex() for i in range(6)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
            Edge(vertices2[2], vertices2[3]),
            Edge(vertices2[3], vertices2[4]),
            #Edge(vertices2[4], vertices2[5]),
        ]

        graph1 = Graph()
        for vertex in vertices1: graph1.addVertex(vertex)
        for edge in edges1: graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2: graph2.addVertex(vertex)
        for edge in edges2: graph2.addEdge(edge)

        self.assertTrue(graph1.isIsomorphic(graph2))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))
        self.assertTrue(graph2.isIsomorphic(graph1))
        self.assertTrue(graph2.isSubgraphIsomorphic(graph1))
        self.assertTrue(len(graph1.findSubgraphIsomorphisms(graph2)) > 0)

    def test_subgraphIsomorphism(self):
        """
        Check the subgraph isomorphism functions.
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]
        vertices2 = [Vertex() for i in range(2)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
        ]

        graph1 = Graph()
        for vertex in vertices1: graph1.addVertex(vertex)
        for edge in edges1: graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2: graph2.addVertex(vertex)
        for edge in edges2: graph2.addEdge(edge)

        self.assertFalse(graph1.isIsomorphic(graph2))
        self.assertFalse(graph2.isIsomorphic(graph1))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))

        mapList = graph1.findSubgraphIsomorphisms(graph2)
        self.assertTrue(len(mapList) == 10)
        
        for mapping in mapList:
            self.assertTrue( graph1.isMappingValid(graph2,mapping) )
            self.assertTrue( graph1.isMappingValid(graph2,mapping) )
    
    def test_pickle(self):
        """
        Test that a Graph object can be successfully pickled and unpickled
        with no loss of information.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph0 = Graph()
        for vertex in vertices: graph0.addVertex(vertex)
        for edge in edges: graph0.addEdge(edge)
        graph0.updateConnectivityValues()
        
        import cPickle
        graph = cPickle.loads(cPickle.dumps(graph0))

        self.assertEqual(len(graph0.vertices), len(graph.vertices))
        for v1, v2 in zip(graph0.vertices, graph.vertices):
            self.assertEqual(v1.connectivity1, v2.connectivity1)
            self.assertEqual(v1.connectivity2, v2.connectivity2)
            self.assertEqual(v1.connectivity3, v2.connectivity3)
            self.assertEqual(v1.sortingLabel, v2.sortingLabel)
            self.assertEqual(len(v1.edges), len(v2.edges))
        self.assertTrue(graph0.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph0))

    def test_isCyclic(self):
        """
        Test the Graph.isCyclic() method.
        """
        self.assertFalse(self.graph.isCyclic())
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle
        self.assertTrue(self.graph.isCyclic())
        
    def test_isVertexInCycle(self):
        """
        Test the Graph.isVertexInCycle() method.
        """
        for vertex in self.graph.vertices:
            self.assertFalse(self.graph.isVertexInCycle(vertex))
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle
        for vertex in self.graph.vertices[0:4]:
            self.assertTrue(self.graph.isVertexInCycle(vertex))
        for vertex in self.graph.vertices[4:]:
            self.assertFalse(self.graph.isVertexInCycle(vertex))
        
    def test_isEdgeInCycle(self):
        """
        Test the Graph.isEdgeInCycle() method.
        """
        for vertex1 in self.graph.vertices:
            for vertex2, edge in vertex1.edges.items():
                self.assertFalse(self.graph.isEdgeInCycle(edge))
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle
        for vertex1 in self.graph.vertices:
            for vertex2, edge in vertex1.edges.items():
                if self.graph.vertices.index(vertex1) < 4 and self.graph.vertices.index(vertex2) < 4:
                    self.assertTrue(self.graph.isEdgeInCycle(edge))
                else:
                    self.assertFalse(self.graph.isEdgeInCycle(edge))
                    
    def test_getAllCyclicVertices(self):
        self.assertListEqual(self.graph.getAllCyclicVertices(), [])
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle
        self.assertEqual(len(self.graph.getAllCyclicVertices()), 4)
        
    def test_getAllPolycylicVertices(self):        
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle        
        self.assertListEqual(self.graph.getAllPolycyclicVertices(), []) 
        edge2 = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.addEdge(edge2) # Create another cycle to generate two fused cycles 
        self.assertEqual(len(self.graph.getAllPolycyclicVertices()), 2)
        # Add new vertices and edges to generate a spirocyclic cycle      
        vertices = [Vertex() for i in range(2)]        
        for vertex in vertices: self.graph.addVertex(vertex)
        edges = [
                 Edge(self.graph.vertices[5], self.graph.vertices[6]),
                 Edge(self.graph.vertices[6], self.graph.vertices[7]),
                 Edge(self.graph.vertices[5], self.graph.vertices[7]),
                 ]
        for edge in edges: self.graph.addEdge(edge)        
        self.assertEqual(len(self.graph.getAllPolycyclicVertices()), 3)
                      
    def test_getAllCycles(self):
        """
        Test the Graph.getAllCycles() method.
        """
        cycleList = self.graph.getAllCycles(self.graph.vertices[0])
        self.assertEqual(len(cycleList), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle
        cycleList = self.graph.getAllCycles(self.graph.vertices[0])
        self.assertEqual(len(cycleList), 2)
        self.assertEqual(len(cycleList[0]), 4)
        self.assertEqual(len(cycleList[1]), 4)

    def test_getAllCyclesOfSize(self):
        """
        Test the Graph.getRingsOfSize() method
        """
        cycleList = self.graph.getAllCyclesOfSize(6)
        self.assertEqual(len(cycleList), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle of length 4
        edge = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.addEdge(edge) # To create a cycle of length 6 and another cycle of length 4
        cycleList = self.graph.getAllCyclesOfSize(4)
        self.assertEqual(len(cycleList), 2)
        self.assertEqual(len(cycleList[0]), 4)
        self.assertEqual(len(cycleList[1]), 4)
        
    def test_getAllSimpleCyclesOfSize(self):
        """
        Test the Graph.getAllSimpleCyclesOfSize() method.
        """
        cycleList = self.graph.getAllCyclesOfSize(6)
        self.assertEqual(len(cycleList), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle of length 4
        edge = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.addEdge(edge)  # To create a cycle of length 6 and another cycle of length 4
        cycleList = self.graph.getAllSimpleCyclesOfSize(4)
        self.assertEqual(len(cycleList), 2)
        self.assertEqual(len(cycleList[0]), 4)
        self.assertEqual(len(cycleList[1]), 4)
        cycleList = self.graph.getAllSimpleCyclesOfSize(6)
        self.assertEqual(len(cycleList), 0)

    def test_getSmallestSetOfSmallestRings(self):
        """
        Test the Graph.getSmallestSetOfSmallestRings() method.
        """
        cycleList = self.graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(cycleList), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle
        cycleList = self.graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(cycleList), 1)
        self.assertEqual(len(cycleList[0]), 4)

    def test_getRelevantCycles(self):
        """
        Test the Graph.getRelevantCycles() method.
        """
        cycleList = self.graph.getRelevantCycles()
        self.assertEqual(len(cycleList), 0)
        # Create a cycle of length 4
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)
        # Create a second cycle of length 4
        edge = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.addEdge(edge)
        # Create a bridge forming multiple cycles of length 4
        edge = Edge(self.graph.vertices[1], self.graph.vertices[4])
        self.graph.addEdge(edge)

        # SSSR should be 3 cycles of length 4
        cycleList = self.graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(cycleList), 3)
        sizeList = sorted([len(cycle) for cycle in cycleList])
        self.assertEqual(sizeList, [4, 4, 4])

        # RC should be 5 cycles of length 4
        cycleList = self.graph.getRelevantCycles()
        self.assertEqual(len(cycleList), 5)
        sizeList = sorted([len(cycle) for cycle in cycleList])
        self.assertEqual(sizeList, [4, 4, 4, 4, 4])

    def test_cycleListOrderSSSR(self):
        """
        Test that getSmallestSetOfSmallestRings return vertices in the proper order.

        There are methods such as symmetry and molecule drawing which rely
        on the fact that subsequent list entries are connected.
        """
        # Create a cycle of length 5
        edge = Edge(self.graph.vertices[0], self.graph.vertices[4])
        self.graph.addEdge(edge)
        # Test SSSR
        sssr = self.graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(sssr), 1)
        self.assertEqual(len(sssr[0]), 5)
        for i in range(5):
            self.assertTrue(self.graph.hasEdge(sssr[0][i], sssr[0][i - 1]))

    def test_cycleListOrderRC(self):
        """
        Test that getRelevantCycles return vertices in the proper order.

        There are methods such as symmetry and molecule drawing which rely
        on the fact that subsequent list entries are connected.
        """
        # Create a cycle of length 5
        edge = Edge(self.graph.vertices[0], self.graph.vertices[4])
        self.graph.addEdge(edge)
        # Test RC
        rc = self.graph.getRelevantCycles()
        self.assertEqual(len(rc), 1)
        self.assertEqual(len(rc[0]), 5)
        for i in range(5):
            self.assertTrue(self.graph.hasEdge(rc[0][i], rc[0][i - 1]))

    def test_getPolycyclicRings(self):
        """
        Test that the Graph.getPolycyclicRings() method returns only polycyclic rings.
        """
        vertices = [Vertex() for i in range(27)]
        bonds = [
                 (0,1),
                 (1,2),
                 (2,3),
                 (3,4),
                 (4,5),
                 (5,6),
                 (6,7),
                 (7,8),
                 (8,9),
                 (9,10),
                 (10,11),
                 (11,12),
                 (12,13),
                 (13,14),
                 (14,15),
                 (14,12),
                 (12,16),
                 (16,10),
                 (10,17),
                 (17,18),
                 (18,19),
                 (9,20),
                 (20,21),
                 (21,7),
                 (6,22),
                 (22,23),
                 (22,4),
                 (23,3),
                 (23,24),
                 (24,25),
                 (25,1)
                 ]
        edges = []
        for bond in bonds:
            edges.append(Edge(vertices[bond[0]], vertices[bond[1]]))

        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)
        graph.updateConnectivityValues()
        
        SSSR = graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(SSSR),6)
        polycyclicVertices = set(graph.getAllPolycyclicVertices())
        expectedPolycyclicVertices = set([vertices[index] for index in [3,23,4,22,12]])
        
        self.assertEqual(polycyclicVertices, expectedPolycyclicVertices)
        
        continuousRings = graph.getPolycyclicRings()
        expectedContinuousRings = [[vertices[index] for index in [1,2,3,4,5,6,22,23,24,25]],
                                   #[vertices[index] for index in [7,8,9,21,20]], # This is a nonpolycyclic ring
                                   [vertices[index] for index in [10,11,12,13,14,16]],
                                   ]
        
        # Convert to sets for comparison purposes
        continuousRings = [set(ring) for ring in continuousRings]
        expectedContinuousRings = [set(ring) for ring in expectedContinuousRings]
        for ring in expectedContinuousRings:
            self.assertTrue(ring in continuousRings)

    def test_getMaxCycleOverlap(self):
        """
        Test that getMaxCycleOverlap returns the correct overlap numbers
        for different graphs.
        """
        def make_graph(edge_inds):
            nvert = max(max(inds) for inds in edge_inds) + 1
            vertices = [Vertex() for _ in range(nvert)]
            graph = Graph(vertices)
            for idx1, idx2 in edge_inds:
                graph.addEdge(Edge(vertices[idx1], vertices[idx2]))
            return graph

        linear = make_graph([(0, 1), (1, 2)])
        mono = make_graph([(0, 1), (0, 2), (1, 2), (2, 3), (3, 4), (3, 5), (4, 5)])
        spiro = make_graph([(0, 1), (0, 2), (1, 2), (2, 3), (2, 4), (3, 4)])
        fused = make_graph([(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)])
        bridged = make_graph([(0, 1), (0, 2), (1, 3), (1, 4), (2, 3), (2, 5), (4, 5)])
        cube = make_graph([(0, 1), (0, 2), (0, 4), (1, 3), (1, 5), (2, 3),
                           (2, 6), (3, 7), (4, 5), (4, 6), (5, 7), (6, 7)])

        self.assertEqual(linear.getMaxCycleOverlap(), 0)
        self.assertEqual(mono.getMaxCycleOverlap(), 0)
        self.assertEqual(spiro.getMaxCycleOverlap(), 1)
        self.assertEqual(fused.getMaxCycleOverlap(), 2)
        self.assertEqual(bridged.getMaxCycleOverlap(), 3)
        # With the current algorithm for maximum overlap determination, a cube
        # only has an overlap of 2, because the set of relevant cycles
        # contains the six four-membered faces. This could be changed in the
        # future.
        self.assertEqual(cube.getMaxCycleOverlap(), 2)

    def test_getLargestRing(self):
        """
        Test that the Graph.getPolycyclicRings() method returns only polycyclic rings.
        """
        vertices = [Vertex() for i in range(27)]
        bonds = [
                 (0,1),
                 (1,2),
                 (2,3),
                 (3,4),
                 (4,5),
                 (5,6),
                 (6,7),
                 (9,10),
                 (10,11),
                 (11,12),
                 (12,13),
                 (13,14),
                 (14,15),
                 (12,16),
                 (10,17),
                 (17,18),
                 (18,19),
                 (9,20),
                 (20,21),
                 (6,22),
                 (22,23),
                 (22,8),
                 (8,4),
                 (23,3),
                 (23,24),
                 (24,25),
                 (25,1)
                 ]
        edges = []
        for bond in bonds:
            edges.append(Edge(vertices[bond[0]], vertices[bond[1]]))

        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)
        graph.updateConnectivityValues()
        
        rings = graph.getPolycyclicRings()
        self.assertEqual(len(rings), 1)
        
        #ensure the last ring doesn't include vertex 8, since it isn't in the
        #longest ring. Try two different items since one might contain the vertex 8
        long_ring = graph.getLargestRing(rings[0][0])
        long_ring2 = graph.getLargestRing(rings[0][1])
        
        if len(long_ring) > len(long_ring2):
            longest_ring = long_ring
        else:
            longest_ring = long_ring2
        
        self.assertEqual(len(longest_ring), len(rings[0]) - 1)

    def testSortCyclicVertices(self):
        """Test that _sortCyclicVertices works properly for a valid input."""
        edge = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.addEdge(edge)  # To create a cycle


        # Sort the vertices
        original = list(self.graph.vertices)
        ordered = self.graph._sortCyclicVertices(original)

        # Check that we didn't lose any vertices
        self.assertEqual(len(self.graph.vertices), len(ordered), 'Sorting changed the number of vertices.')

        # Check that the order is different
        self.assertNotEqual(self.graph.vertices, ordered, 'Sorting did not change the order of vertices.')

        # Check that subsequent vertices are connected
        for i in range(5):
            self.assertTrue(self.graph.hasEdge(ordered[i], ordered[i - 1]))

    def testSortCyclicVerticesInvalid(self):
        """Test that _sortCyclicVertices raises an error for an invalid input."""
        edge = Edge(self.graph.vertices[0], self.graph.vertices[4])
        self.graph.addEdge(edge)  # To create a cycle

        original = list(self.graph.vertices)

        with self.assertRaisesRegexp(RuntimeError, 'do not comprise a single cycle'):
            self.graph._sortCyclicVertices(original)

    def testSortCyclicVerticesNoncyclic(self):
        """Test that _sortCyclicVertices raises an error for a noncyclic input."""
        original = list(self.graph.vertices)
        with self.assertRaisesRegexp(RuntimeError, 'do not comprise a single cycle'):
            self.graph._sortCyclicVertices(original)

    def testSortCyclicVerticesUnconnected(self):
        """Test that _sortCyclicVertices raises an error for an unconnected input."""
        self.graph.addVertex(Vertex())
        original = list(self.graph.vertices)
        with self.assertRaisesRegexp(RuntimeError, 'not all vertices are connected'):
            self.graph._sortCyclicVertices(original)
Example #29
0
    def test_subgraphIsomorphism(self):
        """
        Check the subgraph isomorphism functions.
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]
        vertices2 = [Vertex() for i in range(2)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
        ]

        graph1 = Graph()
        for vertex in vertices1: graph1.addVertex(vertex)
        for edge in edges1: graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2: graph2.addVertex(vertex)
        for edge in edges2: graph2.addEdge(edge)

        self.assertFalse(graph1.isIsomorphic(graph2))
        self.assertFalse(graph2.isIsomorphic(graph1))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))

        mapList = graph1.findSubgraphIsomorphisms(graph2)
        self.assertTrue(len(mapList) == 10)
        
        for mapping in mapList:
            self.assertTrue( graph1.isMappingValid(graph2,mapping) )
            self.assertTrue( graph1.isMappingValid(graph2,mapping) )
Example #30
0
    def test_getPolycyclicRings(self):
        """
        Test that the Graph.getPolycyclicRings() method returns only polycyclic rings.
        """
        vertices = [Vertex() for i in range(27)]
        bonds = [
                 (0,1),
                 (1,2),
                 (2,3),
                 (3,4),
                 (4,5),
                 (5,6),
                 (6,7),
                 (7,8),
                 (8,9),
                 (9,10),
                 (10,11),
                 (11,12),
                 (12,13),
                 (13,14),
                 (14,15),
                 (14,12),
                 (12,16),
                 (16,10),
                 (10,17),
                 (17,18),
                 (18,19),
                 (9,20),
                 (20,21),
                 (21,7),
                 (6,22),
                 (22,23),
                 (22,4),
                 (23,3),
                 (23,24),
                 (24,25),
                 (25,1)
                 ]
        edges = []
        for bond in bonds:
            edges.append(Edge(vertices[bond[0]], vertices[bond[1]]))

        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)
        graph.updateConnectivityValues()
        
        SSSR = graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(SSSR),6)
        polycyclicVertices = set(graph.getAllPolycyclicVertices())
        expectedPolycyclicVertices = set([vertices[index] for index in [3,23,4,22,12]])
        
        self.assertEqual(polycyclicVertices, expectedPolycyclicVertices)
        
        continuousRings = graph.getPolycyclicRings()
        expectedContinuousRings = [[vertices[index] for index in [1,2,3,4,5,6,22,23,24,25]],
                                   #[vertices[index] for index in [7,8,9,21,20]], # This is a nonpolycyclic ring
                                   [vertices[index] for index in [10,11,12,13,14,16]],
                                   ]
        
        # Convert to sets for comparison purposes
        continuousRings = [set(ring) for ring in continuousRings]
        expectedContinuousRings = [set(ring) for ring in expectedContinuousRings]
        for ring in expectedContinuousRings:
            self.assertTrue(ring in continuousRings)
Example #31
0
    def test_copyAndMap(self):
        """
        Test the returned dictionary points toward equivaalent vertices and edges
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)

        graphDict = graph.copyAndMap()
        graph2 = Graph(vertices = graphDict.values())

        for vertex in graph.vertices:
            self.assertTrue(graph2.hasVertex(graphDict[vertex]))
        for v1 in graph.vertices:
            for v2 in v1.edges:
                self.assertTrue(graph2.hasEdge(graphDict[v1], graphDict[v2]))
                self.assertTrue(graph2.hasEdge(graphDict[v2], graphDict[v1]))
        self.assertTrue(graph2.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph2))
Example #32
0
    def test_merge(self):
        """
        Test the graph merge function to ensure a proper merging of the graph
        is being done.
        """

        vertices1 = [Vertex() for i in range(4)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
        ]

        vertices2 = [Vertex() for i in range(3)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
        ]

        graph1 = Graph()
        for vertex in vertices1: graph1.addVertex(vertex)
        for edge in edges1: graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2: graph2.addVertex(vertex)
        for edge in edges2: graph2.addEdge(edge)

        graph = graph1.merge(graph2)

        self.assertTrue(len(graph1.vertices) + len(graph2.vertices) == len(graph.vertices))
        for vertex1 in vertices1:
            self.assertTrue(vertex1 in graph.vertices)
            for vertex2 in vertex1.edges:
                self.assertTrue(vertex2 in graph.vertices)
        for vertex2 in vertices2:
            self.assertTrue(vertex2 in graph.vertices)
            for vertex1 in vertex2.edges:
                self.assertTrue(vertex1 in vertex2.edges)
Example #33
0
    def test_copy_and_map(self):
        """
        Test the returned dictionary points toward equivaalent vertices and edges
        """

        vertices = [Vertex() for _ in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.add_vertex(vertex)
        for edge in edges:
            graph.add_edge(edge)

        graph_dict = graph.copy_and_map()
        graph2 = Graph(vertices=list(graph_dict.values()))

        for vertex in graph.vertices:
            self.assertTrue(graph2.has_vertex(graph_dict[vertex]))
        for v1 in graph.vertices:
            for v2 in v1.edges:
                self.assertTrue(graph2.has_edge(graph_dict[v1],
                                                graph_dict[v2]))
                self.assertTrue(graph2.has_edge(graph_dict[v2],
                                                graph_dict[v1]))
        self.assertTrue(graph2.is_isomorphic(graph))
        self.assertTrue(graph.is_isomorphic(graph2))
Example #34
0
    def test_isomorphism(self):
        """
        Check the graph isomorphism functions.
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]

        vertices2 = [Vertex() for i in range(6)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
            Edge(vertices2[2], vertices2[3]),
            Edge(vertices2[3], vertices2[4]),
            Edge(vertices2[4], vertices2[5]),
        ]

        graph1 = Graph()
        for vertex in vertices1: graph1.addVertex(vertex)
        for edge in edges1: graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2: graph2.addVertex(vertex)
        for edge in edges2: graph2.addEdge(edge)

        self.assertTrue(graph1.isIsomorphic(graph2))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))
        self.assertTrue(graph2.isIsomorphic(graph1))
        self.assertTrue(graph2.isSubgraphIsomorphic(graph1))
Example #35
0
    def test_get_largest_ring(self):
        """
        Test that the Graph.get_polycycles() method returns only polycyclic rings.
        """
        vertices = [Vertex() for _ in range(27)]
        bonds = [(0, 1), (1, 2),
                 (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (9, 10), (10, 11),
                 (11, 12), (12, 13), (13, 14), (14, 15), (12, 16), (10, 17),
                 (17, 18), (18, 19), (9, 20), (20, 21), (6, 22), (22, 23),
                 (22, 8), (8, 4), (23, 3), (23, 24), (24, 25), (25, 1)]
        edges = []
        for bond in bonds:
            edges.append(Edge(vertices[bond[0]], vertices[bond[1]]))

        graph = Graph()
        for vertex in vertices:
            graph.add_vertex(vertex)
        for edge in edges:
            graph.add_edge(edge)
        graph.update_connectivity_values()

        rings = graph.get_polycycles()
        self.assertEqual(len(rings), 1)

        # ensure the last ring doesn't include vertex 8, since it isn't in the
        # longest ring. Try two different items since one might contain the vertex 8
        long_ring = graph.get_largest_ring(rings[0][0])
        long_ring2 = graph.get_largest_ring(rings[0][1])

        if len(long_ring) > len(long_ring2):
            longest_ring = long_ring
        else:
            longest_ring = long_ring2

        self.assertEqual(len(longest_ring), len(rings[0]) - 1)
Example #36
0
    def test_isomorphism_disconnected(self):
        """
        Check the graph isomorphism for broken graphs.
        
        This tries to match graphs with a missing bond, 
        eg. [ 0-1-2-3-4  5 ] should match [ 0-1-2-3-4  5 ]
        """

        vertices1 = [Vertex() for _ in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            # Edge(vertices1[4], vertices1[5]),
        ]

        vertices2 = [Vertex() for _ in range(6)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
            Edge(vertices2[2], vertices2[3]),
            Edge(vertices2[3], vertices2[4]),
            # Edge(vertices2[4], vertices2[5]),
        ]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.add_vertex(vertex)
        for edge in edges1:
            graph1.add_edge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.add_vertex(vertex)
        for edge in edges2:
            graph2.add_edge(edge)

        self.assertTrue(graph1.is_isomorphic(graph2))
        self.assertTrue(graph1.is_subgraph_isomorphic(graph2))
        self.assertTrue(graph2.is_isomorphic(graph1))
        self.assertTrue(graph2.is_subgraph_isomorphic(graph1))
        self.assertTrue(len(graph1.find_subgraph_isomorphisms(graph2)) > 0)
class TestGraph(unittest.TestCase):
    """
    Contains unit tests of the Vertex, Edge, and Graph classes. Most of the
    functionality of Vertex and Edge is only meaningful when part of a graph,
    so we test them all together instead of having separate unit test classes
    for each.
    """
    def setUp(self):
        """
        A function run before each unit test in this class.
        """
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        self.graph = Graph()
        for vertex in vertices:
            self.graph.addVertex(vertex)
        for edge in edges:
            self.graph.addEdge(edge)

    def test_addVertex(self):
        """
        Test the Graph.addVertex() method.
        """
        vertex = Vertex()
        self.graph.addVertex(vertex)
        self.assertTrue(vertex in self.graph.vertices)
        self.assertTrue(vertex.edges == {})

    def test_addEdge(self):
        """
        Test the Graph.addEdge() method.
        """
        vertex1 = Vertex()
        vertex2 = Vertex()
        edge = Edge(vertex1, vertex2)
        try:
            self.graph.addEdge(edge)
            self.fail('Added edge between vertices not in graph to graph.')
        except ValueError:
            pass
        self.graph.addVertex(vertex1)
        self.graph.addVertex(vertex2)
        self.graph.addEdge(edge)
        self.assertTrue(vertex1 in self.graph.vertices)
        self.assertTrue(vertex1 in vertex2.edges)
        self.assertTrue(vertex2 in self.graph.vertices)
        self.assertTrue(vertex2 in vertex1.edges)
        self.assertTrue(vertex1.edges[vertex2] is edge)
        self.assertTrue(vertex2.edges[vertex1] is edge)

    def test_getEdge(self):
        """
        Test the Graph.getEdge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[4]
        try:
            edge = self.graph.getEdge(vertex1, vertex2)
            self.fail(
                'Returned an edge between vertices that should not be connected in graph.'
            )
        except ValueError:
            pass
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        edge = self.graph.getEdge(vertex1, vertex2)
        self.assertNotEqual(edge, None)
        self.assertTrue(isinstance(edge, Edge))
        self.assertTrue(vertex1.edges[vertex2] is edge)
        self.assertTrue(vertex2.edges[vertex1] is edge)

    def test_getEdges(self):
        """
        Test the Graph.getEdges() method.
        """
        vertex1 = self.graph.vertices[2]
        edges = self.graph.getEdges(vertex1)
        self.assertTrue(isinstance(edges, dict))
        self.assertEqual(len(edges), 2)
        self.assertTrue(self.graph.vertices[1] in edges)
        self.assertTrue(self.graph.vertices[3] in edges)

    def test_hasVertex(self):
        """
        Test the Graph.hasVertex() method.
        """
        vertex = Vertex()
        self.assertFalse(self.graph.hasVertex(vertex))
        for v in self.graph.vertices:
            self.assertTrue(self.graph.hasVertex(v))

    def test_hasEdge(self):
        """
        Test the Graph.hasEdge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[4]
        self.assertFalse(self.graph.hasEdge(vertex1, vertex2))
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        self.assertTrue(self.graph.hasEdge(vertex1, vertex2))

    def test_removeVertex(self):
        """
        Test the Graph.removeVertex() method.
        """
        vertex = self.graph.vertices[2]
        self.assertTrue(self.graph.hasVertex(vertex))
        self.graph.removeVertex(vertex)
        self.assertFalse(self.graph.hasVertex(vertex))
        for v in self.graph.vertices:
            self.assertFalse(vertex in v.edges)

    def test_removeEdge(self):
        """
        Test the Graph.removeEdge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        self.assertTrue(self.graph.hasEdge(vertex1, vertex2))
        edge = self.graph.getEdge(vertex1, vertex2)
        self.graph.removeEdge(edge)
        self.assertFalse(vertex1 in vertex2.edges)
        self.assertFalse(vertex2 in vertex1.edges)

    def test_copy(self):
        """
        Test the graph copy function to ensure a complete copy of the graph is
        made while preserving vertices and edges.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)

        graph2 = graph.copy()
        for vertex in graph.vertices:
            self.assertTrue(graph2.hasVertex(vertex))
        for v1 in graph.vertices:
            for v2 in v1.edges:
                self.assertTrue(graph2.hasEdge(v1, v2))
                self.assertTrue(graph2.hasEdge(v2, v1))
        self.assertTrue(graph2.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph2))

    def test_copyAndMap(self):
        """
        Test the returned dictionary points toward equivaalent vertices and edges
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)

        graphDict = graph.copyAndMap()
        graph2 = Graph(vertices=graphDict.values())

        for vertex in graph.vertices:
            self.assertTrue(graph2.hasVertex(graphDict[vertex]))
        for v1 in graph.vertices:
            for v2 in v1.edges:
                self.assertTrue(graph2.hasEdge(graphDict[v1], graphDict[v2]))
                self.assertTrue(graph2.hasEdge(graphDict[v2], graphDict[v1]))
        self.assertTrue(graph2.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph2))

    def test_split(self):
        """
        Test the graph split function to ensure a proper splitting of the graph
        is being done.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)

        graphs = graph.split()

        self.assertTrue(len(graphs) == 2)
        self.assertTrue(
            len(graphs[0].vertices) == 4 or len(graphs[0].vertices) == 2)
        self.assertTrue(
            len(graphs[0].vertices) +
            len(graphs[1].vertices) == len(graph.vertices))

    def test_merge(self):
        """
        Test the graph merge function to ensure a proper merging of the graph
        is being done.
        """

        vertices1 = [Vertex() for i in range(4)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
        ]

        vertices2 = [Vertex() for i in range(3)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
        ]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.addVertex(vertex)
        for edge in edges1:
            graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.addVertex(vertex)
        for edge in edges2:
            graph2.addEdge(edge)

        graph = graph1.merge(graph2)

        self.assertTrue(
            len(graph1.vertices) + len(graph2.vertices) == len(graph.vertices))
        for vertex1 in vertices1:
            self.assertTrue(vertex1 in graph.vertices)
            for vertex2 in vertex1.edges:
                self.assertTrue(vertex2 in graph.vertices)
        for vertex2 in vertices2:
            self.assertTrue(vertex2 in graph.vertices)
            for vertex1 in vertex2.edges:
                self.assertTrue(vertex1 in vertex2.edges)

    def test_resetConnectivityValues(self):
        """
        Test the Graph.resetConnectivityValues() method.
        """
        self.graph.resetConnectivityValues()
        for vertex in self.graph.vertices:
            self.assertEqual(vertex.connectivity1, -1)
            self.assertEqual(vertex.connectivity2, -1)
            self.assertEqual(vertex.connectivity3, -1)
            self.assertEqual(vertex.sortingLabel, -1)

    def test_updateConnectivityValues(self):
        """
        Test the Graph.updateConnectivityValues() method.
        """
        self.graph.updateConnectivityValues()
        self.assertEqual(self.graph.vertices[0].connectivity1, 1)
        self.assertEqual(self.graph.vertices[0].connectivity2, 2)
        self.assertEqual(self.graph.vertices[0].connectivity3, 3)
        self.assertEqual(self.graph.vertices[0].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[1].connectivity1, 2)
        self.assertEqual(self.graph.vertices[1].connectivity2, 3)
        self.assertEqual(self.graph.vertices[1].connectivity3, 6)
        self.assertEqual(self.graph.vertices[1].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[2].connectivity1, 2)
        self.assertEqual(self.graph.vertices[2].connectivity2, 4)
        self.assertEqual(self.graph.vertices[2].connectivity3, 7)
        self.assertEqual(self.graph.vertices[2].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[3].connectivity1, 2)
        self.assertEqual(self.graph.vertices[3].connectivity2, 4)
        self.assertEqual(self.graph.vertices[3].connectivity3, 7)
        self.assertEqual(self.graph.vertices[3].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[4].connectivity1, 2)
        self.assertEqual(self.graph.vertices[4].connectivity2, 3)
        self.assertEqual(self.graph.vertices[4].connectivity3, 6)
        self.assertEqual(self.graph.vertices[4].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[5].connectivity1, 1)
        self.assertEqual(self.graph.vertices[5].connectivity2, 2)
        self.assertEqual(self.graph.vertices[5].connectivity3, 3)
        self.assertEqual(self.graph.vertices[5].sortingLabel, -1)

    def test_sortVertices(self):
        """
        Test the Graph.sortVertices() method.
        """
        self.graph.updateConnectivityValues()
        self.graph.sortVertices()
        for vertex1, vertex2 in zip(self.graph.vertices[:-1],
                                    self.graph.vertices[1:]):
            self.assertTrue(vertex1.sortingLabel < vertex2.sortingLabel)
            self.assertTrue(vertex1.connectivity3 >= vertex2.connectivity3)
            self.assertTrue(vertex1.connectivity2 >= vertex2.connectivity2)
            self.assertTrue(vertex1.connectivity1 >= vertex2.connectivity1)

    def test_vertex_connectivity_values(self):
        """
        Tests the vertex connectivity values as introduced by Morgan (1965).
        
        First CV1 is the number of neighbours
        CV2 is the sum of neighbouring CV1 values
        CV3 is the sum of neighbouring CV2 values
        
        Graph:     Expected (and tested) values:
        
        0-1-2-3-4            1-3-2-2-1   3-4-5-3-2    4-11-7-7-3
        |                    |           |             |
        5                    1           3             4
        
        """
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[1], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)

        graph.updateConnectivityValues()

        for i, cv_ in enumerate([1, 3, 2, 2, 1, 1]):
            cv = vertices[i].connectivity1
            self.assertEqual(
                cv, cv_,
                "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".
                format(i, cv, cv_))
        for i, cv_ in enumerate([3, 4, 5, 3, 2, 3]):
            cv = vertices[i].connectivity2
            self.assertEqual(
                cv, cv_,
                "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".
                format(i, cv, cv_))
        for i, cv_ in enumerate([4, 11, 7, 7, 3, 4]):
            cv = vertices[i].connectivity3
            self.assertEqual(
                cv, cv_,
                "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".
                format(i, cv, cv_))

    def test_isomorphism(self):
        """
        Check the graph isomorphism functions.
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]

        vertices2 = [Vertex() for i in range(6)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
            Edge(vertices2[2], vertices2[3]),
            Edge(vertices2[3], vertices2[4]),
            Edge(vertices2[4], vertices2[5]),
        ]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.addVertex(vertex)
        for edge in edges1:
            graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.addVertex(vertex)
        for edge in edges2:
            graph2.addEdge(edge)

        self.assertTrue(graph1.isIsomorphic(graph2))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))
        self.assertTrue(graph2.isIsomorphic(graph1))
        self.assertTrue(graph2.isSubgraphIsomorphic(graph1))

    def test_subgraphIsomorphism(self):
        """
        Check the subgraph isomorphism functions.
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]
        vertices2 = [Vertex() for i in range(2)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
        ]

        graph1 = Graph()
        for vertex in vertices1:
            graph1.addVertex(vertex)
        for edge in edges1:
            graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2:
            graph2.addVertex(vertex)
        for edge in edges2:
            graph2.addEdge(edge)

        self.assertFalse(graph1.isIsomorphic(graph2))
        self.assertFalse(graph2.isIsomorphic(graph1))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))

        mapList = graph1.findSubgraphIsomorphisms(graph2)
        self.assertTrue(len(mapList) == 10)

        for mapping in mapList:
            self.assertTrue(graph1.isMappingValid(graph2, mapping))
            self.assertTrue(graph1.isMappingValid(graph2, mapping))

    def test_pickle(self):
        """
        Test that a Graph object can be successfully pickled and unpickled
        with no loss of information.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph0 = Graph()
        for vertex in vertices:
            graph0.addVertex(vertex)
        for edge in edges:
            graph0.addEdge(edge)
        graph0.updateConnectivityValues()

        import cPickle
        graph = cPickle.loads(cPickle.dumps(graph0))

        self.assertEqual(len(graph0.vertices), len(graph.vertices))
        for v1, v2 in zip(graph0.vertices, graph.vertices):
            self.assertEqual(v1.connectivity1, v2.connectivity1)
            self.assertEqual(v1.connectivity2, v2.connectivity2)
            self.assertEqual(v1.connectivity3, v2.connectivity3)
            self.assertEqual(v1.sortingLabel, v2.sortingLabel)
            self.assertEqual(len(v1.edges), len(v2.edges))
        self.assertTrue(graph0.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph0))

    def test_isCyclic(self):
        """
        Test the Graph.isCyclic() method.
        """
        self.assertFalse(self.graph.isCyclic())
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        self.assertTrue(self.graph.isCyclic())

    def test_isVertexInCycle(self):
        """
        Test the Graph.isVertexInCycle() method.
        """
        for vertex in self.graph.vertices:
            self.assertFalse(self.graph.isVertexInCycle(vertex))
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        for vertex in self.graph.vertices[0:4]:
            self.assertTrue(self.graph.isVertexInCycle(vertex))
        for vertex in self.graph.vertices[4:]:
            self.assertFalse(self.graph.isVertexInCycle(vertex))

    def test_isEdgeInCycle(self):
        """
        Test the Graph.isEdgeInCycle() method.
        """
        for vertex1 in self.graph.vertices:
            for vertex2, edge in vertex1.edges.items():
                self.assertFalse(self.graph.isEdgeInCycle(edge))
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        for vertex1 in self.graph.vertices:
            for vertex2, edge in vertex1.edges.items():
                if self.graph.vertices.index(
                        vertex1) < 4 and self.graph.vertices.index(
                            vertex2) < 4:
                    self.assertTrue(self.graph.isEdgeInCycle(edge))
                else:
                    self.assertFalse(self.graph.isEdgeInCycle(edge))

    def test_getAllCyclicVertices(self):
        self.assertListEqual(self.graph.getAllCyclicVertices(), [])
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        self.assertEqual(len(self.graph.getAllCyclicVertices()), 4)

    def test_getAllPolycylicVertices(self):
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        self.assertListEqual(self.graph.getAllPolycyclicVertices(), [])
        edge2 = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.addEdge(
            edge2)  # Create another cycle to generate two fused cycles
        self.assertEqual(len(self.graph.getAllPolycyclicVertices()), 2)
        # Add new vertices and edges to generate a spirocyclic cycle
        vertices = [Vertex() for i in range(2)]
        for vertex in vertices:
            self.graph.addVertex(vertex)
        edges = [
            Edge(self.graph.vertices[5], self.graph.vertices[6]),
            Edge(self.graph.vertices[6], self.graph.vertices[7]),
            Edge(self.graph.vertices[5], self.graph.vertices[7]),
        ]
        for edge in edges:
            self.graph.addEdge(edge)
        self.assertEqual(len(self.graph.getAllPolycyclicVertices()), 3)

    def test_getAllCycles(self):
        """
        Test the Graph.getAllCycles() method.
        """
        cycleList = self.graph.getAllCycles(self.graph.vertices[0])
        self.assertEqual(len(cycleList), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        cycleList = self.graph.getAllCycles(self.graph.vertices[0])
        self.assertEqual(len(cycleList), 2)
        self.assertEqual(len(cycleList[0]), 4)
        self.assertEqual(len(cycleList[1]), 4)

    def test_getAllCyclesOfSize(self):
        """
        Test the Graph.getRingsOfSize() method
        """
        cycleList = self.graph.getAllCyclesOfSize(6)
        self.assertEqual(len(cycleList), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle of length 4
        edge = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.addEdge(
            edge
        )  # To create a cycle of length 6 and another cycle of length 4
        cycleList = self.graph.getAllCyclesOfSize(4)
        self.assertEqual(len(cycleList), 2)
        self.assertEqual(len(cycleList[0]), 4)
        self.assertEqual(len(cycleList[1]), 4)

    def test_getAllSimpleCyclesOfSize(self):
        """
        Test the Graph.getAllSimpleCyclesOfSize() method.
        """
        cycleList = self.graph.getAllCyclesOfSize(6)
        self.assertEqual(len(cycleList), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle of length 4
        edge = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.addEdge(
            edge
        )  # To create a cycle of length 6 and another cycle of length 4
        cycleList = self.graph.getAllSimpleCyclesOfSize(4)
        self.assertEqual(len(cycleList), 2)
        self.assertEqual(len(cycleList[0]), 4)
        self.assertEqual(len(cycleList[1]), 4)
        cycleList = self.graph.getAllSimpleCyclesOfSize(6)
        self.assertEqual(len(cycleList), 0)

    def test_getSmallestSetOfSmallestRings(self):
        """
        Test the Graph.getSmallestSetOfSmallestRings() method.
        """
        cycleList = self.graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(cycleList), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge)  # To create a cycle
        cycleList = self.graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(cycleList), 1)
        self.assertEqual(len(cycleList[0]), 4)

    def test_getPolycyclicRings(self):
        """
        Test that the Graph.getPolycyclicRings() method returns only polycyclic rings.
        """
        vertices = [Vertex() for i in range(27)]
        bonds = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6),
                 (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13),
                 (13, 14), (14, 15), (14, 12), (12, 16), (16, 10), (10, 17),
                 (17, 18), (18, 19), (9, 20), (20, 21), (21, 7), (6, 22),
                 (22, 23), (22, 4), (23, 3), (23, 24), (24, 25), (25, 1)]
        edges = []
        for bond in bonds:
            edges.append(Edge(vertices[bond[0]], vertices[bond[1]]))

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)
        graph.updateConnectivityValues()

        SSSR = graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(SSSR), 6)
        polycyclicVertices = set(graph.getAllPolycyclicVertices())
        expectedPolycyclicVertices = set(
            [vertices[index] for index in [3, 23, 4, 22, 12]])

        self.assertEqual(polycyclicVertices, expectedPolycyclicVertices)

        continuousRings = graph.getPolycyclicRings()
        expectedContinuousRings = [
            [vertices[index] for index in [1, 2, 3, 4, 5, 6, 22, 23, 24, 25]],
            #[vertices[index] for index in [7,8,9,21,20]], # This is a nonpolycyclic ring
            [vertices[index] for index in [10, 11, 12, 13, 14, 16]],
        ]

        # Convert to sets for comparison purposes
        continuousRings = [set(ring) for ring in continuousRings]
        expectedContinuousRings = [
            set(ring) for ring in expectedContinuousRings
        ]
        for ring in expectedContinuousRings:
            self.assertTrue(ring in continuousRings)

    def test_getLargestRing(self):
        """
        Test that the Graph.getPolycyclicRings() method returns only polycyclic rings.
        """
        vertices = [Vertex() for i in range(27)]
        bonds = [(0, 1), (1, 2),
                 (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (9, 10), (10, 11),
                 (11, 12), (12, 13), (13, 14), (14, 15), (12, 16), (10, 17),
                 (17, 18), (18, 19), (9, 20), (20, 21), (6, 22), (22, 23),
                 (22, 8), (8, 4), (23, 3), (23, 24), (24, 25), (25, 1)]
        edges = []
        for bond in bonds:
            edges.append(Edge(vertices[bond[0]], vertices[bond[1]]))

        graph = Graph()
        for vertex in vertices:
            graph.addVertex(vertex)
        for edge in edges:
            graph.addEdge(edge)
        graph.updateConnectivityValues()

        rings = graph.getPolycyclicRings()
        self.assertEqual(len(rings), 1)

        #ensure the last ring doesn't include vertex 8, since it isn't in the
        #longest ring. Try two different items since one might contain the vertex 8
        long_ring = graph.getLargestRing(rings[0][0])
        long_ring2 = graph.getLargestRing(rings[0][1])

        if len(long_ring) > len(long_ring2):
            longest_ring = long_ring
        else:
            longest_ring = long_ring2

        self.assertEqual(len(longest_ring), len(rings[0]) - 1)
Example #38
0
class TestGraph(unittest.TestCase):
    """
    Contains unit tests of the Vertex, Edge, and Graph classes. Most of the
    functionality of Vertex and Edge is only meaningful when part of a graph,
    so we test them all together instead of having separate unit test classes
    for each.
    """

    def setUp(self):
        """
        A function run before each unit test in this class.
        """
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]
        
        self.graph = Graph()
        for vertex in vertices: self.graph.addVertex(vertex)
        for edge in edges: self.graph.addEdge(edge)
        
    def test_addVertex(self):
        """
        Test the Graph.addVertex() method.
        """
        vertex = Vertex()
        self.graph.addVertex(vertex)
        self.assertTrue(vertex in self.graph.vertices)
        self.assertTrue(vertex.edges == {})
        
    def test_addEdge(self):
        """
        Test the Graph.addEdge() method.
        """
        vertex1 = Vertex(); vertex2 = Vertex(); edge = Edge(vertex1, vertex2)
        try:
            self.graph.addEdge(edge)
            self.fail('Added edge between vertices not in graph to graph.')
        except ValueError:
            pass
        self.graph.addVertex(vertex1)
        self.graph.addVertex(vertex2)
        self.graph.addEdge(edge)
        self.assertTrue(vertex1 in self.graph.vertices)
        self.assertTrue(vertex1 in vertex2.edges)
        self.assertTrue(vertex2 in self.graph.vertices)
        self.assertTrue(vertex2 in vertex1.edges)
        self.assertTrue(vertex1.edges[vertex2] is edge)
        self.assertTrue(vertex2.edges[vertex1] is edge)
        
    def test_getEdge(self):
        """
        Test the Graph.getEdge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[4]
        try:
            edge = self.graph.getEdge(vertex1, vertex2)
            self.fail('Returned an edge between vertices that should not be connected in graph.')
        except ValueError:
            pass
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        edge = self.graph.getEdge(vertex1, vertex2)
        self.assertNotEqual(edge, None)
        self.assertTrue(isinstance(edge, Edge))
        self.assertTrue(vertex1.edges[vertex2] is edge)
        self.assertTrue(vertex2.edges[vertex1] is edge)

    def test_getEdges(self):
        """
        Test the Graph.getEdges() method.
        """
        vertex1 = self.graph.vertices[2]
        edges = self.graph.getEdges(vertex1)
        self.assertTrue(isinstance(edges, dict))
        self.assertEqual(len(edges), 2)
        self.assertTrue(self.graph.vertices[1] in edges)
        self.assertTrue(self.graph.vertices[3] in edges)

    def test_hasVertex(self):
        """
        Test the Graph.hasVertex() method.
        """
        vertex = Vertex()
        self.assertFalse(self.graph.hasVertex(vertex))
        for v in self.graph.vertices:
            self.assertTrue(self.graph.hasVertex(v))

    def test_hasEdge(self):
        """
        Test the Graph.hasEdge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[4]
        self.assertFalse(self.graph.hasEdge(vertex1, vertex2))
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        self.assertTrue(self.graph.hasEdge(vertex1, vertex2))

    def test_removeVertex(self):
        """
        Test the Graph.removeVertex() method.
        """
        vertex = self.graph.vertices[2]
        self.assertTrue(self.graph.hasVertex(vertex))
        self.graph.removeVertex(vertex)
        self.assertFalse(self.graph.hasVertex(vertex))
        for v in self.graph.vertices:
            self.assertFalse(vertex in v.edges)

    def test_removeEdge(self):
        """
        Test the Graph.removeEdge() method.
        """
        vertex1 = self.graph.vertices[2]
        vertex2 = self.graph.vertices[3]
        self.assertTrue(self.graph.hasEdge(vertex1, vertex2))
        edge = self.graph.getEdge(vertex1, vertex2)
        self.graph.removeEdge(edge)
        self.assertFalse(vertex1 in vertex2.edges)
        self.assertFalse(vertex2 in vertex1.edges)
    
    def test_copy(self):
        """
        Test the graph copy function to ensure a complete copy of the graph is
        made while preserving vertices and edges.
        """
        
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)
        
        graph2 = graph.copy()
        for vertex in graph.vertices:
            self.assertTrue(graph2.hasVertex(vertex))
        for v1 in graph.vertices:
            for v2 in v1.edges:
                self.assertTrue(graph2.hasEdge(v1, v2))
                self.assertTrue(graph2.hasEdge(v2, v1))
        self.assertTrue(graph2.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph2))

    def test_copyAndMap(self):
        """
        Test the returned dictionary points toward equivaalent vertices and edges
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)

        graphDict = graph.copyAndMap()
        graph2 = Graph(vertices = graphDict.values())

        for vertex in graph.vertices:
            self.assertTrue(graph2.hasVertex(graphDict[vertex]))
        for v1 in graph.vertices:
            for v2 in v1.edges:
                self.assertTrue(graph2.hasEdge(graphDict[v1], graphDict[v2]))
                self.assertTrue(graph2.hasEdge(graphDict[v2], graphDict[v1]))
        self.assertTrue(graph2.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph2))

    def test_split(self):
        """
        Test the graph split function to ensure a proper splitting of the graph
        is being done.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[4], vertices[5]),
        ]

        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)
        
        graphs = graph.split()
        
        self.assertTrue(len(graphs) == 2)
        self.assertTrue(len(graphs[0].vertices) == 4 or len(graphs[0].vertices) == 2)
        self.assertTrue(len(graphs[0].vertices) + len(graphs[1].vertices) == len(graph.vertices))
    
    def test_merge(self):
        """
        Test the graph merge function to ensure a proper merging of the graph
        is being done.
        """

        vertices1 = [Vertex() for i in range(4)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
        ]

        vertices2 = [Vertex() for i in range(3)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
        ]

        graph1 = Graph()
        for vertex in vertices1: graph1.addVertex(vertex)
        for edge in edges1: graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2: graph2.addVertex(vertex)
        for edge in edges2: graph2.addEdge(edge)

        graph = graph1.merge(graph2)

        self.assertTrue(len(graph1.vertices) + len(graph2.vertices) == len(graph.vertices))
        for vertex1 in vertices1:
            self.assertTrue(vertex1 in graph.vertices)
            for vertex2 in vertex1.edges:
                self.assertTrue(vertex2 in graph.vertices)
        for vertex2 in vertices2:
            self.assertTrue(vertex2 in graph.vertices)
            for vertex1 in vertex2.edges:
                self.assertTrue(vertex1 in vertex2.edges)
    
    def test_resetConnectivityValues(self):
        """
        Test the Graph.resetConnectivityValues() method.
        """
        self.graph.resetConnectivityValues()
        for vertex in self.graph.vertices:
            self.assertEqual(vertex.connectivity1, -1)
            self.assertEqual(vertex.connectivity2, -1)
            self.assertEqual(vertex.connectivity3, -1)
            self.assertEqual(vertex.sortingLabel, -1)
    
    def test_updateConnectivityValues(self):
        """
        Test the Graph.updateConnectivityValues() method.
        """
        self.graph.updateConnectivityValues()
        self.assertEqual(self.graph.vertices[0].connectivity1, 1)
        self.assertEqual(self.graph.vertices[0].connectivity2, 2)
        self.assertEqual(self.graph.vertices[0].connectivity3, 3)
        self.assertEqual(self.graph.vertices[0].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[1].connectivity1, 2)
        self.assertEqual(self.graph.vertices[1].connectivity2, 3)
        self.assertEqual(self.graph.vertices[1].connectivity3, 6)
        self.assertEqual(self.graph.vertices[1].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[2].connectivity1, 2)
        self.assertEqual(self.graph.vertices[2].connectivity2, 4)
        self.assertEqual(self.graph.vertices[2].connectivity3, 7)
        self.assertEqual(self.graph.vertices[2].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[3].connectivity1, 2)
        self.assertEqual(self.graph.vertices[3].connectivity2, 4)
        self.assertEqual(self.graph.vertices[3].connectivity3, 7)
        self.assertEqual(self.graph.vertices[3].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[4].connectivity1, 2)
        self.assertEqual(self.graph.vertices[4].connectivity2, 3)
        self.assertEqual(self.graph.vertices[4].connectivity3, 6)
        self.assertEqual(self.graph.vertices[4].sortingLabel, -1)
        self.assertEqual(self.graph.vertices[5].connectivity1, 1)
        self.assertEqual(self.graph.vertices[5].connectivity2, 2)
        self.assertEqual(self.graph.vertices[5].connectivity3, 3)
        self.assertEqual(self.graph.vertices[5].sortingLabel, -1)
    
    def test_sortVertices(self):
        """
        Test the Graph.sortVertices() method.
        """
        self.graph.updateConnectivityValues()
        self.graph.sortVertices()
        for vertex1, vertex2 in zip(self.graph.vertices[:-1], self.graph.vertices[1:]):
            self.assertTrue(vertex1.sortingLabel < vertex2.sortingLabel)
            self.assertTrue(vertex1.connectivity3 >= vertex2.connectivity3)
            self.assertTrue(vertex1.connectivity2 >= vertex2.connectivity2)
            self.assertTrue(vertex1.connectivity1 >= vertex2.connectivity1)
    
    def test_vertex_connectivity_values(self):
        """
        Tests the vertex connectivity values as introduced by Morgan (1965).
        
        First CV1 is the number of neighbours
        CV2 is the sum of neighbouring CV1 values
        CV3 is the sum of neighbouring CV2 values
        
        Graph:     Expected (and tested) values:
        
        0-1-2-3-4            1-3-2-2-1   3-4-5-3-2    4-11-7-7-3
        |                    |           |             |
        5                    1           3             4
        
        """
        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[1], vertices[5]),
        ]
        
        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)
    
        graph.updateConnectivityValues()

        for i,cv_ in enumerate([1,3,2,2,1,1]):
            cv = vertices[i].connectivity1
            self.assertEqual(cv, cv_, "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".format(i,cv,cv_))
        for i,cv_ in enumerate([3,4,5,3,2,3]):
            cv = vertices[i].connectivity2
            self.assertEqual(cv, cv_, "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".format(i,cv,cv_))
        for i,cv_ in enumerate([4,11,7,7,3,4]):
            cv = vertices[i].connectivity3
            self.assertEqual(cv, cv_, "On vertex {0:d} got connectivity[0]={1:d} but expected {2:d}".format(i,cv,cv_))

    def test_isomorphism(self):
        """
        Check the graph isomorphism functions.
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]

        vertices2 = [Vertex() for i in range(6)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
            Edge(vertices2[1], vertices2[2]),
            Edge(vertices2[2], vertices2[3]),
            Edge(vertices2[3], vertices2[4]),
            Edge(vertices2[4], vertices2[5]),
        ]

        graph1 = Graph()
        for vertex in vertices1: graph1.addVertex(vertex)
        for edge in edges1: graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2: graph2.addVertex(vertex)
        for edge in edges2: graph2.addEdge(edge)

        self.assertTrue(graph1.isIsomorphic(graph2))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))
        self.assertTrue(graph2.isIsomorphic(graph1))
        self.assertTrue(graph2.isSubgraphIsomorphic(graph1))

    def test_subgraphIsomorphism(self):
        """
        Check the subgraph isomorphism functions.
        """

        vertices1 = [Vertex() for i in range(6)]
        edges1 = [
            Edge(vertices1[0], vertices1[1]),
            Edge(vertices1[1], vertices1[2]),
            Edge(vertices1[2], vertices1[3]),
            Edge(vertices1[3], vertices1[4]),
            Edge(vertices1[4], vertices1[5]),
        ]
        vertices2 = [Vertex() for i in range(2)]
        edges2 = [
            Edge(vertices2[0], vertices2[1]),
        ]

        graph1 = Graph()
        for vertex in vertices1: graph1.addVertex(vertex)
        for edge in edges1: graph1.addEdge(edge)

        graph2 = Graph()
        for vertex in vertices2: graph2.addVertex(vertex)
        for edge in edges2: graph2.addEdge(edge)

        self.assertFalse(graph1.isIsomorphic(graph2))
        self.assertFalse(graph2.isIsomorphic(graph1))
        self.assertTrue(graph1.isSubgraphIsomorphic(graph2))

        mapList = graph1.findSubgraphIsomorphisms(graph2)
        self.assertTrue(len(mapList) == 10)
        
        for mapping in mapList:
            self.assertTrue( graph1.isMappingValid(graph2,mapping) )
            self.assertTrue( graph1.isMappingValid(graph2,mapping) )
    
    def test_pickle(self):
        """
        Test that a Graph object can be successfully pickled and unpickled
        with no loss of information.
        """

        vertices = [Vertex() for i in range(6)]
        edges = [
            Edge(vertices[0], vertices[1]),
            Edge(vertices[1], vertices[2]),
            Edge(vertices[2], vertices[3]),
            Edge(vertices[3], vertices[4]),
            Edge(vertices[4], vertices[5]),
        ]

        graph0 = Graph()
        for vertex in vertices: graph0.addVertex(vertex)
        for edge in edges: graph0.addEdge(edge)
        graph0.updateConnectivityValues()
        
        import cPickle
        graph = cPickle.loads(cPickle.dumps(graph0))

        self.assertEqual(len(graph0.vertices), len(graph.vertices))
        for v1, v2 in zip(graph0.vertices, graph.vertices):
            self.assertEqual(v1.connectivity1, v2.connectivity1)
            self.assertEqual(v1.connectivity2, v2.connectivity2)
            self.assertEqual(v1.connectivity3, v2.connectivity3)
            self.assertEqual(v1.sortingLabel, v2.sortingLabel)
            self.assertEqual(len(v1.edges), len(v2.edges))
        self.assertTrue(graph0.isIsomorphic(graph))
        self.assertTrue(graph.isIsomorphic(graph0))

    def test_isCyclic(self):
        """
        Test the Graph.isCyclic() method.
        """
        self.assertFalse(self.graph.isCyclic())
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle
        self.assertTrue(self.graph.isCyclic())
        
    def test_isVertexInCycle(self):
        """
        Test the Graph.isVertexInCycle() method.
        """
        for vertex in self.graph.vertices:
            self.assertFalse(self.graph.isVertexInCycle(vertex))
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle
        for vertex in self.graph.vertices[0:4]:
            self.assertTrue(self.graph.isVertexInCycle(vertex))
        for vertex in self.graph.vertices[4:]:
            self.assertFalse(self.graph.isVertexInCycle(vertex))
        
    def test_isEdgeInCycle(self):
        """
        Test the Graph.isEdgeInCycle() method.
        """
        for vertex1 in self.graph.vertices:
            for vertex2, edge in vertex1.edges.items():
                self.assertFalse(self.graph.isEdgeInCycle(edge))
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle
        for vertex1 in self.graph.vertices:
            for vertex2, edge in vertex1.edges.items():
                if self.graph.vertices.index(vertex1) < 4 and self.graph.vertices.index(vertex2) < 4:
                    self.assertTrue(self.graph.isEdgeInCycle(edge))
                else:
                    self.assertFalse(self.graph.isEdgeInCycle(edge))
                    
    def test_getAllCyclicVertices(self):
        self.assertListEqual(self.graph.getAllCyclicVertices(), [])
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle
        self.assertEqual(len(self.graph.getAllCyclicVertices()), 4)
        
    def test_getAllPolycylicVertices(self):        
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle        
        self.assertListEqual(self.graph.getAllPolycyclicVertices(), []) 
        edge2 = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.addEdge(edge2) # Create another cycle to generate two fused cycles 
        self.assertEqual(len(self.graph.getAllPolycyclicVertices()), 2)
        # Add new vertices and edges to generate a spirocyclic cycle      
        vertices = [Vertex() for i in range(2)]        
        for vertex in vertices: self.graph.addVertex(vertex)
        edges = [
                 Edge(self.graph.vertices[5], self.graph.vertices[6]),
                 Edge(self.graph.vertices[6], self.graph.vertices[7]),
                 Edge(self.graph.vertices[5], self.graph.vertices[7]),
                 ]
        for edge in edges: self.graph.addEdge(edge)        
        self.assertEqual(len(self.graph.getAllPolycyclicVertices()), 3)
                      
    def test_getAllCycles(self):
        """
        Test the Graph.getAllCycles() method.
        """
        cycleList = self.graph.getAllCycles(self.graph.vertices[0])
        self.assertEqual(len(cycleList), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle
        cycleList = self.graph.getAllCycles(self.graph.vertices[0])
        self.assertEqual(len(cycleList), 2)
        self.assertEqual(len(cycleList[0]), 4)
        self.assertEqual(len(cycleList[1]), 4)

    def test_getAllCyclesOfSize(self):
        """
        Test the Graph.getRingsOfSize() method
        """
        cycleList = self.graph.getAllCyclesOfSize(6)
        self.assertEqual(len(cycleList), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle of length 4
        edge = Edge(self.graph.vertices[0], self.graph.vertices[5])
        self.graph.addEdge(edge) # To create a cycle of length 6 and another cycle of length 4
        cycleList = self.graph.getAllCyclesOfSize(4)
        self.assertEqual(len(cycleList), 2)
        self.assertEqual(len(cycleList[0]), 4)
        self.assertEqual(len(cycleList[1]), 4)
        
    def test_getSmallestSetOfSmallestRings(self):
        """
        Test the Graph.getSmallestSetOfSmallestRings() method.
        """
        cycleList = self.graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(cycleList), 0)
        edge = Edge(self.graph.vertices[0], self.graph.vertices[3])
        self.graph.addEdge(edge) # To create a cycle
        cycleList = self.graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(cycleList), 1)
        self.assertEqual(len(cycleList[0]), 4)

    def test_getPolycyclicRings(self):
        """
        Test that the Graph.getPolycyclicRings() method returns only polycyclic rings.
        """
        vertices = [Vertex() for i in range(27)]
        bonds = [
                 (0,1),
                 (1,2),
                 (2,3),
                 (3,4),
                 (4,5),
                 (5,6),
                 (6,7),
                 (7,8),
                 (8,9),
                 (9,10),
                 (10,11),
                 (11,12),
                 (12,13),
                 (13,14),
                 (14,15),
                 (14,12),
                 (12,16),
                 (16,10),
                 (10,17),
                 (17,18),
                 (18,19),
                 (9,20),
                 (20,21),
                 (21,7),
                 (6,22),
                 (22,23),
                 (22,4),
                 (23,3),
                 (23,24),
                 (24,25),
                 (25,1)
                 ]
        edges = []
        for bond in bonds:
            edges.append(Edge(vertices[bond[0]], vertices[bond[1]]))

        graph = Graph()
        for vertex in vertices: graph.addVertex(vertex)
        for edge in edges: graph.addEdge(edge)
        graph.updateConnectivityValues()
        
        SSSR = graph.getSmallestSetOfSmallestRings()
        self.assertEqual(len(SSSR),6)
        polycyclicVertices = set(graph.getAllPolycyclicVertices())
        expectedPolycyclicVertices = set([vertices[index] for index in [3,23,4,22,12]])
        
        self.assertEqual(polycyclicVertices, expectedPolycyclicVertices)
        
        continuousRings = graph.getPolycyclicRings()
        expectedContinuousRings = [[vertices[index] for index in [1,2,3,4,5,6,22,23,24,25]],
                                   #[vertices[index] for index in [7,8,9,21,20]], # This is a nonpolycyclic ring
                                   [vertices[index] for index in [10,11,12,13,14,16]],
                                   ]
        
        # Convert to sets for comparison purposes
        continuousRings = [set(ring) for ring in continuousRings]
        expectedContinuousRings = [set(ring) for ring in expectedContinuousRings]
        for ring in expectedContinuousRings:
            self.assertTrue(ring in continuousRings)
Example #39
0
    def test_get_polycyclic_rings(self):
        """
        Test that the Graph.get_polycycles() method returns only polycyclic rings.
        """
        vertices = [Vertex() for _ in range(27)]
        bonds = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6),
                 (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13),
                 (13, 14), (14, 15), (14, 12), (12, 16), (16, 10), (10, 17),
                 (17, 18), (18, 19), (9, 20), (20, 21), (21, 7), (6, 22),
                 (22, 23), (22, 4), (23, 3), (23, 24), (24, 25), (25, 1)]
        edges = []
        for bond in bonds:
            edges.append(Edge(vertices[bond[0]], vertices[bond[1]]))

        graph = Graph()
        for vertex in vertices:
            graph.add_vertex(vertex)
        for edge in edges:
            graph.add_edge(edge)
        graph.update_connectivity_values()

        sssr = graph.get_smallest_set_of_smallest_rings()
        self.assertEqual(len(sssr), 6)
        polycyclic_vertices = set(graph.get_all_polycyclic_vertices())
        expected_polycyclic_vertices = set(
            [vertices[index] for index in [3, 23, 4, 22, 12]])

        self.assertEqual(polycyclic_vertices, expected_polycyclic_vertices)

        continuous_rings = graph.get_polycycles()
        expected_continuous_rings = [
            [vertices[index] for index in [1, 2, 3, 4, 5, 6, 22, 23, 24, 25]],
            # [vertices[index] for index in [7,8,9,21,20]], # This is a nonpolycyclic ring
            [vertices[index] for index in [10, 11, 12, 13, 14, 16]],
        ]

        # Convert to sets for comparison purposes
        continuous_rings = [set(ring) for ring in continuous_rings]
        expected_continuous_rings = [
            set(ring) for ring in expected_continuous_rings
        ]
        for ring in expected_continuous_rings:
            self.assertTrue(ring in continuous_rings)