def setUp(self):
        """
        Create same graph using Graphit and NetworkX
        """

        nodes = [1, 2, 3, 4, 5, 6, 7, 8]
        edges = [(1, 2), (2, 3), (2, 4), (3, 4), (5, 4), (6, 4), (5, 7),
                 (7, 8)]

        self.gn = NetworkXGraph()
        self.gn.add_nodes(nodes)
        self.gn.add_edges(edges)

        self.nx = networkx.Graph()
        for node in nodes:
            self.nx.add_node(node, _id=node, key=node)
        self.nx.add_edges_from(edges)
    def test_graph_size(self):
        """
        Test 'size' which is a method in NetworkX and a function in Graphit
        """

        gn = NetworkXGraph()
        gn.add_edge('a', 'b', node_from_edge=True, weight=2)
        gn.add_edge('b', 'c', node_from_edge=True, weight=4)

        nx = networkx.Graph()
        nx.add_edge('a', 'b', weight=2)
        nx.add_edge('b', 'c', weight=4)

        self.assertEqual(gn.size(), nx.size())
        self.assertEqual(gn.size(weight='weight'), nx.size(weight='weight'))
        self.assertEqual(gn.number_of_edges(), nx.number_of_edges())
    def test_graph_add_edges(self):
        """
        Graphit has add_edges and NetworkX add_edges_from
        """

        gn = NetworkXGraph()
        gn.add_nodes([1, 2, 3, 4])
        gn.add_edges_from([(1, 2), (2, 3), (2, 4)])

        nx = networkx.Graph()
        nx.add_nodes_from([1, 2, 3, 4])
        nx.add_edges_from([(1, 2), (2, 3), (2, 4)])

        self.assertNotEqual(list(gn.edges), list(nx.edges))
        self.assertItemsEqual([e for e in gn.edges if e[0] < e[1]],
                              list(nx.edges))
    def test_graph_add_nodes(self):
        """
        Graphit has add_nodes and NetworkX add_nodes_from
        """

        gn = NetworkXGraph()
        gn.add_nodes_from([1, 2, 3, 4])

        nx = networkx.Graph()
        nx.add_nodes_from([1, 2, 3, 4])

        self.assertListEqual(list(gn.nodes), list(nx.nodes))

        gn.add_nodes_from([(5, {'test': True}), (6, {'test': False})])
        nx.add_nodes_from([(5, {'test': True}), (6, {'test': False})])

        d = nx.nodes[5]
        d.update({u'_id': 5, u'key': 5})
        self.assertDictEqual(gn.nodes[5], d)
class TestGraphNetworkxCompatibility(UnittestPythonCompatibility):
    """
    Unit tests for the compatibility of the NetworkXGraph with the NetworkX
    package.
    """
    def setUp(self):
        """
        Create same graph using Graphit and NetworkX
        """

        nodes = [1, 2, 3, 4, 5, 6, 7, 8]
        edges = [(1, 2), (2, 3), (2, 4), (3, 4), (5, 4), (6, 4), (5, 7),
                 (7, 8)]

        self.gn = NetworkXGraph()
        self.gn.add_nodes(nodes)
        self.gn.add_edges(edges)

        self.nx = networkx.Graph()
        for node in nodes:
            self.nx.add_node(node, _id=node, key=node)
        self.nx.add_edges_from(edges)

    def test_graph_magic_methods(self):
        """
        Test similarity in graph class magic methods between graphit and networkx
        """

        # NetworkX has 3 different ways to get the length of a graph (number of nodes)
        self.assertEqual(len(self.gn), len(self.nx))
        self.assertEqual(self.gn.order(), self.nx.order())  # same as __len__
        self.assertEqual(self.gn.number_of_nodes(),
                         self.nx.number_of_nodes())  # same as __len__

    def test_graph_directionality(self):

        self.assertEqual(self.gn.is_directed(), self.nx.is_directed())

    def test_graph_to_directional(self):

        nx_dir = self.nx.to_directed()
        gn_dir = self.gn.to_directed()

        self.assertTrue(nx_dir.is_directed())
        self.assertTrue(gn_dir.is_directed())

    def test_graph_nodes(self):

        print(self.nx.nodes)
        print(self.nx.nodes(data='_id'))
        print(repr(self.nx.nodes))
        print(self.nx.nodes.keys())
        print(self.nx.nodes.items())
        print(self.nx.nodes.values())

        self.assertListEqual(list(self.gn.nodes), list(self.nx.nodes))
        self.assertDictEqual(self.gn.nodes[2], self.nx.nodes[2])

    def test_graph_add_nodes(self):
        """
        Graphit has add_nodes and NetworkX add_nodes_from
        """

        gn = NetworkXGraph()
        gn.add_nodes_from([1, 2, 3, 4])

        nx = networkx.Graph()
        nx.add_nodes_from([1, 2, 3, 4])

        self.assertListEqual(list(gn.nodes), list(nx.nodes))

        gn.add_nodes_from([(5, {'test': True}), (6, {'test': False})])
        nx.add_nodes_from([(5, {'test': True}), (6, {'test': False})])

        d = nx.nodes[5]
        d.update({u'_id': 5, u'key': 5})
        self.assertDictEqual(gn.nodes[5], d)

    def test_graph_edges(self):
        """
        Edges are not equal because NetworkX stores a single edge to represent
        both directions in a undirectional graph while graphit stores both.
        """

        self.assertNotEqual(list(self.gn.edges), list(self.nx.edges))
        self.assertItemsEqual([e for e in self.gn.edges if e[0] < e[1]],
                              list(self.nx.edges))

    def test_graph_add_edges(self):
        """
        Graphit has add_edges and NetworkX add_edges_from
        """

        gn = NetworkXGraph()
        gn.add_nodes([1, 2, 3, 4])
        gn.add_edges_from([(1, 2), (2, 3), (2, 4)])

        nx = networkx.Graph()
        nx.add_nodes_from([1, 2, 3, 4])
        nx.add_edges_from([(1, 2), (2, 3), (2, 4)])

        self.assertNotEqual(list(gn.edges), list(nx.edges))
        self.assertItemsEqual([e for e in gn.edges if e[0] < e[1]],
                              list(nx.edges))

    def test_graph_remove_nodes(self):
        """
        Graphit has remove_nodes and NetworkX remove_nodes_from
        """

        self.gn.remove_nodes_from([3, 4])
        self.nx.remove_nodes_from([3, 4])

        self.assertListEqual(list(self.gn.nodes), list(self.nx.nodes))

    def test_graph_remove_edges(self):
        """
        Graphit has remove_edges and NetworkX remove_edges_from
        """

        self.gn.remove_edges_from([(1, 2), (2, 4)])
        self.nx.remove_edges_from([(1, 2), (2, 4)])

        self.assertNotEqual(list(self.gn.edges), list(self.nx.edges))
        self.assertItemsEqual([e for e in self.gn.edges if e[0] < e[1]],
                              list(self.nx.edges))

    def test_graph_size(self):
        """
        Test 'size' which is a method in NetworkX and a function in Graphit
        """

        gn = NetworkXGraph()
        gn.add_edge('a', 'b', node_from_edge=True, weight=2)
        gn.add_edge('b', 'c', node_from_edge=True, weight=4)

        nx = networkx.Graph()
        nx.add_edge('a', 'b', weight=2)
        nx.add_edge('b', 'c', weight=4)

        self.assertEqual(gn.size(), nx.size())
        self.assertEqual(gn.size(weight='weight'), nx.size(weight='weight'))
        self.assertEqual(gn.number_of_edges(), nx.number_of_edges())

    def test_graph_attributes(self):

        for a in self.gn.nodes.items():
            print(a)
예제 #6
0
    def setUp(self):

        edges = {
            (1, 2): {
                'weight': 1.0
            },
            (2, 3): {
                'weight': 1.0
            },
            (2, 4): {
                'weight': 1.0
            },
            (3, 5): {
                'weight': 1.0
            },
            (4, 5): {
                'weight': 1.0
            },
            (4, 7): {
                'weight': 1.0
            },
            (5, 7): {
                'weight': 0.75
            },
            (3, 14): {
                'weight': 2.0
            },
            (14, 15): {
                'weight': 2.0
            },
            (14, 16): {
                'weight': 1.0
            },
            (15, 12): {
                'weight': 2.0
            },
            (22, 24): {
                'weight': 1.0
            },
            (12, 13): {
                'weight': 2.0
            },
            (13, 28): {
                'weight': 1.0
            },
            (5, 8): {
                'weight': 1.0
            },
            (8, 9): {
                'weight': 0.5
            },
            (8, 10): {
                'weight': 3.0
            },
            (9, 12): {
                'weight': 0.5
            },
            (10, 11): {
                'weight': 3.0
            },
            (11, 13): {
                'weight': 1.0
            },
            (7, 25): {
                'weight': 1.0
            },
            (25, 26): {
                'weight': 1.0
            },
            (26, 27): {
                'weight': 1.0
            },
            (11, 26): {
                'weight': 1.0
            },
            (7, 17): {
                'weight': 1.0
            },
            (17, 18): {
                'weight': 1.0
            },
            (18, 19): {
                'weight': 2.0
            },
            (18, 20): {
                'weight': 1.0
            },
            (20, 21): {
                'weight': 1.0
            },
            (21, 22): {
                'weight': 1.0
            },
            (22, 23): {
                'weight': 1.0
            }
        }

        # Regular graphit Graph
        self.graph = Graph(auto_nid=False)
        self.graph.directed = True

        # NetworkX graphit Graph
        self.gn = NetworkXGraph(auto_nid=False)
        self.gn.directed = True

        # NetworkX graph
        #self.nx = networkx.DiGraph()

        for eid in edges:
            self.graph.add_edge(node_from_edge=True, *eid, **edges[eid])
            self.gn.add_edge(node_from_edge=True, *eid, **edges[eid])
예제 #7
0
class TestGraphAlgorithms(UnittestPythonCompatibility):
    def setUp(self):

        edges = {
            (1, 2): {
                'weight': 1.0
            },
            (2, 3): {
                'weight': 1.0
            },
            (2, 4): {
                'weight': 1.0
            },
            (3, 5): {
                'weight': 1.0
            },
            (4, 5): {
                'weight': 1.0
            },
            (4, 7): {
                'weight': 1.0
            },
            (5, 7): {
                'weight': 0.75
            },
            (3, 14): {
                'weight': 2.0
            },
            (14, 15): {
                'weight': 2.0
            },
            (14, 16): {
                'weight': 1.0
            },
            (15, 12): {
                'weight': 2.0
            },
            (22, 24): {
                'weight': 1.0
            },
            (12, 13): {
                'weight': 2.0
            },
            (13, 28): {
                'weight': 1.0
            },
            (5, 8): {
                'weight': 1.0
            },
            (8, 9): {
                'weight': 0.5
            },
            (8, 10): {
                'weight': 3.0
            },
            (9, 12): {
                'weight': 0.5
            },
            (10, 11): {
                'weight': 3.0
            },
            (11, 13): {
                'weight': 1.0
            },
            (7, 25): {
                'weight': 1.0
            },
            (25, 26): {
                'weight': 1.0
            },
            (26, 27): {
                'weight': 1.0
            },
            (11, 26): {
                'weight': 1.0
            },
            (7, 17): {
                'weight': 1.0
            },
            (17, 18): {
                'weight': 1.0
            },
            (18, 19): {
                'weight': 2.0
            },
            (18, 20): {
                'weight': 1.0
            },
            (20, 21): {
                'weight': 1.0
            },
            (21, 22): {
                'weight': 1.0
            },
            (22, 23): {
                'weight': 1.0
            }
        }

        # Regular graphit Graph
        self.graph = Graph(auto_nid=False)
        self.graph.directed = True

        # NetworkX graphit Graph
        self.gn = NetworkXGraph(auto_nid=False)
        self.gn.directed = True

        # NetworkX graph
        #self.nx = networkx.DiGraph()

        for eid in edges:
            self.graph.add_edge(node_from_edge=True, *eid, **edges[eid])
            self.gn.add_edge(node_from_edge=True, *eid, **edges[eid])
            #self.nx.add_edge(*eid, **edges[eid])

    def test_algorithm_degree(self):
        """
        Test degree method part of the Adjacency view
        """

        # Degree
        self.assertDictEqual(
            self.graph.adjacency.degree(), {
                1: 1,
                2: 3,
                3: 3,
                4: 3,
                5: 4,
                7: 4,
                14: 3,
                15: 2,
                16: 1,
                12: 3,
                22: 3,
                24: 1,
                13: 3,
                28: 1,
                8: 3,
                9: 2,
                10: 2,
                11: 3,
                25: 2,
                26: 3,
                27: 1,
                17: 2,
                18: 3,
                19: 1,
                20: 2,
                21: 2,
                23: 1
            })

        # Outdegree
        self.assertDictEqual(
            self.graph.adjacency.degree(method='outdegree'), {
                1: 1,
                2: 2,
                3: 2,
                4: 2,
                5: 2,
                7: 2,
                14: 2,
                15: 1,
                16: 0,
                12: 1,
                22: 2,
                24: 0,
                13: 1,
                28: 0,
                8: 2,
                9: 1,
                10: 1,
                11: 2,
                25: 1,
                26: 1,
                27: 0,
                17: 1,
                18: 2,
                19: 0,
                20: 1,
                21: 1,
                23: 0
            })

        # Indegree
        self.assertDictEqual(
            self.graph.adjacency.degree(method='indegree'), {
                1: 0,
                2: 1,
                3: 1,
                4: 1,
                5: 2,
                7: 2,
                14: 1,
                15: 1,
                16: 1,
                12: 2,
                22: 1,
                24: 1,
                13: 2,
                28: 1,
                8: 1,
                9: 1,
                10: 1,
                11: 1,
                25: 1,
                26: 2,
                27: 1,
                17: 1,
                18: 1,
                19: 1,
                20: 1,
                21: 1,
                23: 1
            })

        # Weighted degree
        self.assertDictEqual(
            self.graph.adjacency.degree(weight='weight'), {
                1: 1.0,
                2: 3.0,
                3: 4.0,
                4: 3.0,
                5: 3.75,
                7: 3.75,
                14: 5.0,
                15: 4.0,
                16: 1.0,
                12: 4.5,
                22: 3.0,
                24: 1.0,
                13: 4.0,
                28: 1.0,
                8: 4.5,
                9: 1.0,
                10: 6.0,
                11: 5.0,
                25: 2.0,
                26: 3.0,
                27: 1.0,
                17: 2.0,
                18: 4.0,
                19: 2.0,
                20: 2.0,
                21: 2.0,
                23: 1.0
            })

    def test_algorithm_dijkstra_shortest_path(self):
        """
        Test Dijkstra shortest path method, weighted and non-weighted.
        """

        # Shortest path in fully directed graph
        self.assertListEqual(dijkstra_shortest_path(self.graph, 1, 28),
                             [1, 2, 3, 14, 15, 12, 13, 28])

        # Shortest path in fully directed graph considering weights
        self.assertListEqual(
            dijkstra_shortest_path(self.graph, 1, 28, weight='weight'),
            [1, 2, 3, 5, 8, 9, 12, 13, 28])

        # Reverse directionality of edge (14, 15)
        self.graph.add_edge(15, 14)
        self.graph.remove_edge(14, 15)

        self.assertListEqual(dijkstra_shortest_path(self.graph, 1, 28),
                             [1, 2, 3, 5, 8, 10, 11, 13, 28])

    def test_algorithm_dfs_paths(self):
        """
        Test depth-first search of all paths between two nodes
        """

        self.assertListEqual(
            list(dfs_paths(self.graph, 2, 26)),
            [[2, 4, 7, 25, 26], [2, 4, 5, 7, 25, 26], [2, 4, 5, 8, 10, 11, 26],
             [2, 3, 5, 7, 25, 26], [2, 3, 5, 8, 10, 11, 26]])

        # Nodes 13 and 26 not connected via directional path
        self.assertListEqual(list(dfs_paths(self.graph, 13, 26)), [])

        # Switch to breath-first search
        self.assertListEqual(
            list(dfs_paths(self.graph, 2, 26, method='bfs')),
            [[2, 4, 7, 25, 26], [2, 3, 5, 7, 25, 26], [2, 4, 5, 7, 25, 26],
             [2, 3, 5, 8, 10, 11, 26], [2, 4, 5, 8, 10, 11, 26]])

        # dfs_path with path length cutoff
        self.assertListEqual(
            list(dfs_paths(self.graph, 2, 26, cutoff=5)),
            [[2, 4, 7, 25, 26], [2, 4, 5, 7, 25, 26], [2, 3, 5, 7, 25, 26]])

    def test_algorithm_dfs_edges(self):
        """
        Test graph dfs_edges method in depth-first-search (dfs) and
        breath-first-search (bfs) mode.
        """

        self.assertListEqual(list(dfs_edges(self.graph, 5)),
                             [(5, 7), (7, 17), (17, 18), (18, 19), (18, 20),
                              (20, 21), (21, 22), (22, 23), (22, 24), (7, 25),
                              (25, 26), (26, 27), (5, 8), (8, 9), (9, 12),
                              (12, 13), (13, 28), (8, 10), (10, 11)])

        self.assertListEqual(list(dfs_edges(self.graph, 8)),
                             [(8, 9), (9, 12), (12, 13), (13, 28), (8, 10),
                              (10, 11), (11, 26), (26, 27)])

        # Breath-first search
        self.assertListEqual(list(dfs_edges(self.graph, 8, method='bfs')),
                             [(8, 9), (8, 10), (9, 12), (10, 11), (12, 13),
                              (11, 26), (13, 28), (26, 27)])

        # With depth limit
        self.assertListEqual(list(dfs_edges(self.graph, 5,
                                            max_depth=2)), [(5, 7), (7, 17),
                                                            (7, 25), (5, 8),
                                                            (8, 9), (8, 10)])

    def test_algorithm_dfs_edges__edge_based(self):
        """
        Test graph dfs_edges in True edge traversal mode
        """

        # Use true edge oriented DFS method
        self.assertListEqual(list(dfs_edges(self.graph, 8, edge_based=True)),
                             [(8, 9), (9, 12), (12, 13), (13, 28), (8, 10),
                              (10, 11), (11, 13), (11, 26), (26, 27)])

    def test_algorithm_dfs_nodes(self):
        """
        Test graph dfs_nodes method in depth-first-search (dfs) and
        breath-first-search (bfs) mode
        """

        # Connectivity information using Depth First Search / Breath first search
        self.assertListEqual(list(dfs_nodes(self.graph, 8)),
                             [8, 9, 12, 13, 28, 10, 11, 26, 27])
        self.assertListEqual(list(dfs_nodes(self.graph, 8, method='bfs')),
                             [8, 9, 10, 12, 11, 13, 26, 28, 27])

    def test_algorithm_is_reachable(self):
        """
        Test is_reachable method to test connectivity between two nodes
        """

        self.assertTrue(is_reachable(self.graph, 3, 21))

        # Reverse directionality of edge (20, 21)
        self.graph.add_edge(21, 20)
        self.graph.remove_edge(20, 21)
        self.assertFalse(is_reachable(self.graph, 7, 23))

    def test_algorithm_brandes_betweenness_centrality(self):
        """
        Test graph Brandes betweenness centrality measure
        """

        # Non-weighted Brandes betweenness centrality
        self.assertDictEqual(
            brandes_betweenness_centrality(self.graph), {
                1: 0.0,
                2: 0.038461538461538464,
                3: 0.026153846153846153,
                4: 0.04461538461538461,
                5: 0.047692307692307694,
                7: 0.08461538461538462,
                8: 0.03230769230769231,
                9: 0.00923076923076923,
                10: 0.016923076923076923,
                11: 0.013846153846153847,
                12: 0.023076923076923078,
                13: 0.01846153846153846,
                14: 0.023076923076923078,
                15: 0.01846153846153846,
                16: 0.0,
                17: 0.06461538461538462,
                18: 0.06461538461538462,
                19: 0.0,
                20: 0.04923076923076923,
                21: 0.04153846153846154,
                22: 0.03076923076923077,
                23: 0.0,
                24: 0.0,
                25: 0.01846153846153846,
                26: 0.015384615384615385,
                27: 0.0,
                28: 0.0
            })

        # Weighted Brandes betweenness centrality
        self.assertDictEqual(
            brandes_betweenness_centrality(self.graph, weight='weight'), {
                1: 0.0,
                2: 0.038461538461538464,
                3: 0.021538461538461538,
                4: 0.04923076923076923,
                5: 0.06153846153846154,
                7: 0.08461538461538462,
                8: 0.046153846153846156,
                9: 0.027692307692307693,
                10: 0.012307692307692308,
                11: 0.00923076923076923,
                12: 0.027692307692307693,
                13: 0.01846153846153846,
                14: 0.00923076923076923,
                15: 0.004615384615384615,
                16: 0.0,
                17: 0.06461538461538462,
                18: 0.06461538461538462,
                19: 0.0,
                20: 0.04923076923076923,
                21: 0.04153846153846154,
                22: 0.03076923076923077,
                23: 0.0,
                24: 0.0,
                25: 0.01846153846153846,
                26: 0.015384615384615385,
                27: 0.0,
                28: 0.0
            })

        # Non-Normalized Brandes betweenness centrality
        self.assertDictEqual(
            brandes_betweenness_centrality(self.graph, normalized=False), {
                1: 0.0,
                2: 25.0,
                3: 17.0,
                4: 29.0,
                5: 31.0,
                7: 55.0,
                8: 21.0,
                9: 6.0,
                10: 11.0,
                11: 9.0,
                12: 15.0,
                13: 12.0,
                14: 15.0,
                15: 12.0,
                16: 0.0,
                17: 42.0,
                18: 42.0,
                19: 0.0,
                20: 32.0,
                21: 27.0,
                22: 20.0,
                23: 0.0,
                24: 0.0,
                25: 12.0,
                26: 10.0,
                27: 0.0,
                28: 0.0
            })

    def test_algorithm_eigenvector_centrality(self):
        """
        Test graph node eigenvector centrality
        """

        # Default eigenvector centrality
        self.assertDictAlmostEqual(eigenvector_centrality(self.graph,
                                                          max_iter=1000),
                                   {
                                       1: 4.625586668162422e-22,
                                       2: 2.585702947502789e-19,
                                       3: 7.21415747939946e-17,
                                       4: 7.21415747939946e-17,
                                       5: 2.6788916354749308e-14,
                                       7: 3.737126037589252e-12,
                                       8: 3.723731579643154e-12,
                                       9: 4.133449353873311e-10,
                                       10: 4.133449353873311e-10,
                                       11: 3.816675918922932e-08,
                                       12: 3.8373431692993267e-08,
                                       13: 6.049668071167027e-06,
                                       14: 1.3394458408653937e-14,
                                       15: 1.861865919106721e-12,
                                       16: 1.861865919106721e-12,
                                       17: 4.152068010478676e-10,
                                       18: 3.837343162085218e-08,
                                       19: 3.0343757153351047e-06,
                                       20: 3.0343757153351047e-06,
                                       21: 0.0002095723846317974,
                                       22: 0.012842890187130716,
                                       23: 0.7070483707650801,
                                       24: 0.7070483707650801,
                                       25: 4.152068010478676e-10,
                                       26: 3.0536657740585734e-06,
                                       27: 0.00021109911510684646,
                                       28: 0.0004176371258851023
                                   },
                                   places=14)

        # Weighted eigenvector centrality
        self.assertDictAlmostEqual(eigenvector_centrality(self.graph,
                                                          max_iter=1000,
                                                          weight='weight'),
                                   {
                                       1: 8.688902566026301e-23,
                                       2: 5.899764842331867e-20,
                                       3: 2.0000289704530646e-17,
                                       4: 2.0000289704530646e-17,
                                       5: 9.026876112472413e-15,
                                       7: 1.148684996586023e-12,
                                       8: 1.5255620780686783e-12,
                                       9: 1.029772476508652e-10,
                                       10: 6.178634859047565e-10,
                                       11: 2.0822457823635333e-07,
                                       12: 6.607834043233963e-09,
                                       13: 2.131713811356956e-05,
                                       14: 9.026876112472416e-15,
                                       15: 3.051124156050468e-12,
                                       16: 1.5255620780686783e-12,
                                       17: 1.5522865250051764e-10,
                                       18: 1.745502542904305e-08,
                                       19: 3.359775472482072e-06,
                                       20: 1.679887736241036e-06,
                                       21: 0.00014125541592609745,
                                       22: 0.010542254842300024,
                                       23: 0.7070653405331172,
                                       24: 0.7070653405331172,
                                       25: 1.5522865250051764e-10,
                                       26: 2.0037291488250496e-05,
                                       27: 0.0016833983234686718,
                                       28: 0.0017929426478924984
                                   },
                                   places=14)

        # Non-convergence exception
        self.assertRaises(GraphitAlgorithmError,
                          eigenvector_centrality,
                          self.graph,
                          max_iter=100)
    def setUp(self):

        edges = {
            (5, 4): {
                'type': 'universal'
            },
            (5, 6): {
                'type': 'universal'
            },
            (11, 9): {
                'type': 'universal'
            },
            (3, 2): {
                'type': 'universal'
            },
            (2, 1): {
                'type': 'monotone'
            },
            (9, 10): {
                'type': 'universal'
            },
            (2, 3): {
                'type': 'universal'
            },
            (9, 6): {
                'type': 'universal'
            },
            (6, 5): {
                'type': 'universal'
            },
            (1, 2): {
                'type': 'monotone'
            },
            ('object', 12): {
                'type': 'universal'
            },
            (6, 9): {
                'type': 'universal'
            },
            (6, 7): {
                'type': 'universal'
            },
            (12, 13): {
                'type': 'monotone'
            },
            (7, 8): {},
            (7, 6): {
                'type': 'universal'
            },
            (13, 12): {
                'type': 'monotone'
            },
            (3, 8): {
                'type': 'universal'
            },
            (4, 5): {
                'type': 'universal'
            },
            (12, 'object'): {
                'type': 'universal'
            },
            (9, 11): {
                'type': 'universal'
            },
            (4, 3): {
                'type': 'universal'
            },
            (8, 3): {
                'type': 'universal'
            },
            (3, 4): {
                'type': 'universal'
            },
            (10, 9): {
                'type': 'universal'
            }
        }

        self.graph = Graph(auto_nid=False)
        self.graph.directed = True

        self.gn = NetworkXGraph()
        self.gn.directed = True

        self.nx = networkx.DiGraph()

        weight = 0
        for node in range(1, 14):
            self.graph.add_node(node, weight=weight)
            self.gn.add_node(node, weight=weight)
            self.nx.add_node(node, _id=node, key=node, weight=weight)
            weight += 1
        self.graph.add_node('object')
        self.gn.add_node('object')
        self.nx.add_node('object', _id=node + 1, key='object')

        weight = 0
        for eid in sorted(edges.keys(), key=lambda x: str(x[0])):
            self.graph.add_edge(*eid, weight=weight)
            self.gn.add_edge(*eid, weight=weight)
            self.nx.add_edge(*eid, weight=weight)
            weight += 0.05
class TestGraphAlgorithms(UnittestPythonCompatibility):
    def setUp(self):

        edges = {
            (5, 4): {
                'type': 'universal'
            },
            (5, 6): {
                'type': 'universal'
            },
            (11, 9): {
                'type': 'universal'
            },
            (3, 2): {
                'type': 'universal'
            },
            (2, 1): {
                'type': 'monotone'
            },
            (9, 10): {
                'type': 'universal'
            },
            (2, 3): {
                'type': 'universal'
            },
            (9, 6): {
                'type': 'universal'
            },
            (6, 5): {
                'type': 'universal'
            },
            (1, 2): {
                'type': 'monotone'
            },
            ('object', 12): {
                'type': 'universal'
            },
            (6, 9): {
                'type': 'universal'
            },
            (6, 7): {
                'type': 'universal'
            },
            (12, 13): {
                'type': 'monotone'
            },
            (7, 8): {},
            (7, 6): {
                'type': 'universal'
            },
            (13, 12): {
                'type': 'monotone'
            },
            (3, 8): {
                'type': 'universal'
            },
            (4, 5): {
                'type': 'universal'
            },
            (12, 'object'): {
                'type': 'universal'
            },
            (9, 11): {
                'type': 'universal'
            },
            (4, 3): {
                'type': 'universal'
            },
            (8, 3): {
                'type': 'universal'
            },
            (3, 4): {
                'type': 'universal'
            },
            (10, 9): {
                'type': 'universal'
            }
        }

        self.graph = Graph(auto_nid=False)
        self.graph.directed = True

        self.gn = NetworkXGraph()
        self.gn.directed = True

        self.nx = networkx.DiGraph()

        weight = 0
        for node in range(1, 14):
            self.graph.add_node(node, weight=weight)
            self.gn.add_node(node, weight=weight)
            self.nx.add_node(node, _id=node, key=node, weight=weight)
            weight += 1
        self.graph.add_node('object')
        self.gn.add_node('object')
        self.nx.add_node('object', _id=node + 1, key='object')

        weight = 0
        for eid in sorted(edges.keys(), key=lambda x: str(x[0])):
            self.graph.add_edge(*eid, weight=weight)
            self.gn.add_edge(*eid, weight=weight)
            self.nx.add_edge(*eid, weight=weight)
            weight += 0.05

    def test_graph_shortest_path_method(self):
        """
        Test Dijkstra shortest path method
        """

        from networkx.algorithms.shortest_paths.generic import shortest_path
        from networkx.algorithms.traversal.depth_first_search import dfs_preorder_nodes

        print(shortest_path(self.nx, 8, 10))
        print(list(dfs_preorder_nodes(self.nx, 8)))

        # In a mixed directed graph where 7 connects to 8 but not 8 to 7
        self.assertEqual(dijkstra_shortest_path(self.graph, 8, 10),
                         [8, 3, 4, 5, 6, 9, 10])
        self.assertEqual(list(dfs_paths(self.graph, 8, 10)),
                         [[8, 3, 4, 5, 6, 9, 10]])
        self.assertEqual(list(dfs_paths(self.graph, 8, 10, method='bfs')),
                         [[8, 3, 4, 5, 6, 9, 10]])

        # Fully connect 7 and 8
        self.graph.add_edge(8, 7, directed=True)
        self.assertEqual(dijkstra_shortest_path(self.graph, 8, 10),
                         [8, 7, 6, 9, 10])
        self.assertEqual(list(dfs_paths(self.graph, 8, 10)),
                         [[8, 7, 6, 9, 10], [8, 3, 4, 5, 6, 9, 10]])
        self.assertEqual(list(dfs_paths(self.graph, 8, 10, method='bfs')),
                         [[8, 7, 6, 9, 10], [8, 3, 4, 5, 6, 9, 10]])

    def test_graph_dfs_method(self):
        """
        Test graph depth-first-search and breath-first-search
        """

        # Connectivity information using Depth First Search / Breath first search
        self.assertListEqual(dfs(self.graph, 8),
                             [8, 3, 4, 5, 6, 9, 11, 10, 7, 2, 1])
        self.assertListEqual(dfs(self.graph, 8, method='bfs'),
                             [8, 3, 2, 4, 1, 5, 6, 7, 9, 10, 11])

    def test_graph_node_reachability_methods(self):
        """
        Test graph algorithms
        """

        # Test if node is reachable from other node (uses dfs internally)
        self.assertTrue(is_reachable(self.graph, 8, 10))
        self.assertFalse(is_reachable(self.graph, 8, 12))

    def test_graph_centrality_method(self):
        """
        Test graph Brandes betweenness centrality measure
        """

        # Return Brandes betweenness centrality
        self.assertDictEqual(
            brandes_betweenness_centrality(self.graph), {
                1: 0.0,
                2: 0.11538461538461538,
                3: 0.26282051282051283,
                4: 0.21474358974358973,
                5: 0.22756410256410256,
                6: 0.3205128205128205,
                7: 0.0673076923076923,
                8: 0.060897435897435896,
                9: 0.21794871794871795,
                10: 0.0,
                11: 0.0,
                12: 0.01282051282051282,
                13: 0.0,
                u'object': 0.0
            })

        print(brandes_betweenness_centrality(self.graph, weight='weight'))
        print(brandes_betweenness_centrality(self.graph, normalized=False))

        # Test against NetworkX if possible
        if self.nx is not None:

            from networkx.algorithms.centrality.betweenness import betweenness_centrality

            # Regular Brandes betweenness centrality
            nx_between = betweenness_centrality(self.nx)
            gn_between = brandes_betweenness_centrality(self.graph)
            self.assertDictEqual(gn_between, nx_between)

            # Weighted Brandes betweenness centrality
            nx_between = betweenness_centrality(self.nx, weight='weight')
            gn_between = brandes_betweenness_centrality(self.graph,
                                                        weight='weight')
            self.assertDictEqual(gn_between, nx_between)

            # Normalized Brandes betweenness centrality
            nx_between = betweenness_centrality(self.nx, normalized=False)
            gn_between = brandes_betweenness_centrality(self.graph,
                                                        normalized=False)
            self.assertDictEqual(gn_between, nx_between)

    def test_graph_nodes_are_interconnected(self):
        """
        Test if all nodes directly connected with one another
        """

        nodes = [1, 2, 3, 4, 5, 6]

        self.graph = Graph()
        self.graph.add_nodes(nodes)
        for edge in itertools.combinations(nodes, 2):
            self.graph.add_edge(*edge)
        self.graph.remove_edge(5, 6)

        self.assertTrue(nodes_are_interconnected(self.graph, [1, 2, 4]))
        self.assertFalse(nodes_are_interconnected(self.graph, [3, 5, 6]))

    def test_graph_degree(self):
        """
        Test (weighted) degree method
        """

        self.assertDictEqual(degree(self.graph, [1, 3, 12]), {
            1: 1,
            3: 3,
            12: 2
        })

        # Directed graphs behave the same as undirected
        self.graph.directed = False
        self.assertDictEqual(degree(self.graph, [1, 3, 12]), {
            1: 1,
            3: 3,
            12: 2
        })
        self.assertDictEqual(degree(self.graph, [1, 3, 12], weight='weight'), {
            1: 0,
            3: 1.3499999999999999,
            12: 0.35000000000000003
        })

        # Loops counted twice
        self.graph.add_edge(12, 12)
        self.assertDictEqual(degree(self.graph, [1, 3, 12]), {
            1: 1,
            3: 3,
            12: 4
        })
        self.assertDictEqual(degree(self.graph, [1, 3, 12], weight='weight'), {
            1: 0,
            3: 1.3499999999999999,
            12: 2.3499999999999996
        })