예제 #1
0
class TestGraphAddEdgeExceptionWarning(UnittestPythonCompatibility):
    """
    Test logged warnings and raised Exceptions by Graph add_edge.
    Same as for add_nodes
    """

    def setUp(self):
        """
        Build empty graph to add a node to and test default state
        """

        self.graph = Graph()

    def test_add_edge_node_not_exist(self):
        """
        Test adding edges for nodes that do not exist
        """

        self.assertRaises(GraphitException, self.graph.add_edge, 1, 2)

    def test_add_edge_exist(self):
        """
        Test adding edges that already exist. A warning is logged
        but edge ID is returned
        """

        self.graph.add_nodes([1, 2])
        self.graph.add_edge(1, 2)

        eid = self.graph.add_edge(1, 2)
        self.assertTrue(eid, (1, 2))

    def test_remove_edge_exist(self):
        """
        Test removal of edges that do not exist should only log a warning
        """

        self.graph.add_nodes([1, 2])
        try:
            self.graph.remove_edge(1, 2)
        except GraphitException:
            self.fail('remove_edge raised GraphitException unexpectedly')
예제 #2
0
class TestGraphRemoveEdges(UnittestPythonCompatibility):
    """
    Test removal of edges in directed and undirected way
    """

    def setUp(self):
        """
        Build Graph with nodes and edges
        """

        self.graph = Graph()
        self.graph.add_edges([(1, 2), (2, 3), (3, 4), (3, 5), (4, 5)], node_from_edge=True)

        self.assertTrue(len(self.graph) == 5)
        self.assertTrue(len(self.graph.nodes) == 5)
        self.assertTrue(len(self.graph.edges) == 10)
        self.assertTrue(len(self.graph.adjacency) == 5)

    def tearDown(self):
        """
        Test state after edge removal
        """

        if self.edges:

            # If undirected, add reversed edges
            if not self.graph.directed:
                self.edges.extend([e[::-1] for e in self.edges if e[::-1] not in self.edges])

            for edge in self.edges:

                # Edge should be removed
                self.assertTrue(edge not in self.graph.edges)

                # Nodes connected should still be there
                self.assertTrue(all([node in self.graph.nodes for node in edge]))

                # Adjacency should be corrected
                self.assertTrue(edge[1] not in self.graph.adjacency[edge[0]])

                # If directional, reverse edge still in graph
                if self.graph.directed:
                    rev_edge = edge[::-1]
                    if rev_edge not in self.edges:
                        self.assertTrue(rev_edge in self.graph.edges)

            # filled after addition
            self.assertTrue(len(self.graph) == 5)
            self.assertTrue(len(self.graph.nodes) == 5)
            self.assertTrue(len(self.graph.edges) == 10 - len(self.edges))
            self.assertTrue(len(self.graph.adjacency) == 5)

    def test_remove_edge_single_undirected(self):
        """
        Test removal of single undirected edge
        """

        self.edges = [(1, 2)]
        self.graph.remove_edge(*self.edges[0])

    def test_remove_edge_single_directed(self):
        """
        Test removal of single directed edge
        """

        self.graph.directed = True
        self.edges = [(1, 2)]
        self.graph.remove_edge(*self.edges[0])

    def test_remove_edge_multiple_undirected(self):
        """
        Test removal of multiple undirected edges
        """

        self.edges = [(1, 2), (2, 3), (4, 5)]
        self.graph.remove_edges(self.edges)

    def test_remove_edge_multiple_directed(self):
        """
        Test removal of multiple directed edges
        """

        self.graph.directed = True
        self.edges = [(1, 2), (2, 3), (4, 5)]
        self.graph.remove_edges(self.edges)

    def test_remove_edge_single_mixed(self):
        """
        Test removal of a single directed edge in a global undirected graph
        using local override of directionality
        """

        self.edges = [(1, 2)]
        self.graph.remove_edge(*self.edges[0], directed=True)

        self.graph.directed = True

    def test_remove_edge_multiple_mixed(self):
        """
        Test removal of multiple directed edges in a global undirected graph
        using local override of directionality
        """

        self.edges = [(1, 2), (2, 3), (4, 5)]
        self.graph.remove_edges(self.edges, directed=True)

        self.graph.directed = True

    def test_graph_clear(self):
        """
        Test clear method to removal all nodes and edges
        """

        self.edges = []
        self.graph.clear()

        self.assertTrue(len(self.graph) == 0)
        self.assertTrue(len(self.graph.nodes) == 0)
        self.assertTrue(len(self.graph.edges) == 0)
        self.assertTrue(len(self.graph.adjacency) == 0)
class TestGraphCombinatorialSetlike(UnittestPythonCompatibility):

    def setUp(self):
        """
        Setup two graphs for combinatorial tests
        """

        self.graph1 = Graph(auto_nid=False)
        self.graph1.add_nodes(range(1, 11))
        self.graph1.add_edges([(1, 2), (2, 3), (3, 4), (3, 5), (5, 6), (4, 7), (6, 8), (7, 8), (8, 9), (9, 10)])

        self.graph2 = Graph(auto_nid=False)
        self.graph2.add_nodes(range(6, 16))
        self.graph2.add_edges([(6, 8), (7, 8), (8, 9), (9, 10), (10, 11), (10, 12), (12, 13), (11, 14), (13, 15),
                               (14, 15)])

    def test_combinatorial_intersection(self):
        """
        Test intersection between two graphs
        """

        intr = graph_intersection(self.graph1, self.graph2)
        self.assertItemsEqual(intr.nodes.keys(), range(6, 11))
        self.assertItemsEqual(intr.edges.keys(), [(8, 9), (6, 8), (9, 8), (9, 10), (8, 7), (8, 6), (7, 8), (10, 9)])

        self.assertFalse(intr.nodes.is_view)
        self.assertFalse(intr.edges.is_view)
        self.assertEqual(intr, intr.origin)

    def test_combinatorial_intersection_edgediff(self):
        """
        Test intersection between two graphs with a different edge population
        """

        self.graph2.remove_edge(8, 9)
        intr = graph_intersection(self.graph1, self.graph2)

        self.assertItemsEqual(intr.nodes.keys(), range(6, 11))
        self.assertItemsEqual(intr.edges.keys(), [(6, 8), (9, 10), (8, 7), (8, 6), (7, 8), (10, 9)])

        self.assertFalse(intr.nodes.is_view)
        self.assertFalse(intr.edges.is_view)
        self.assertEqual(intr, intr.origin)

    def test_combinatorial_difference(self):
        """
        Test difference between two graphs
        """

        diff = graph_difference(self.graph1, self.graph2)

        self.assertItemsEqual(diff.nodes.keys(), range(1, 6))
        self.assertItemsEqual(diff.edges.keys(), [(1, 2), (3, 2), (2, 1), (2, 3), (4, 3), (5, 3), (3, 4), (3, 5)])

        self.assertFalse(diff.nodes.is_view)
        self.assertFalse(diff.edges.is_view)
        self.assertEqual(diff, diff.origin)

        diff = graph_difference(self.graph2, self.graph1)

        self.assertItemsEqual(diff.nodes.keys(), range(11, 16))
        self.assertItemsEqual(diff.edges.keys(), [(14, 11), (13, 12), (15, 13), (12, 13), (13, 15), (14, 15), (11, 14),
                                                  (15, 14)])

        self.assertFalse(diff.nodes.is_view)
        self.assertFalse(diff.edges.is_view)
        self.assertEqual(diff, diff.origin)

    def test_combinatorial_difference_edgediff(self):
        """
        Test difference between two graphs using edge oriented difference.
        """

        diff = graph_difference(self.graph1, self.graph2, edge_diff=True)

        self.assertItemsEqual(diff.nodes.keys(), range(1, 8))
        self.assertItemsEqual(diff.edges.keys(), [(1, 2), (3, 2), (2, 1), (2, 3), (4, 3), (5, 3), (3, 4), (3, 5),
                                                  (5, 6), (6, 5), (4, 7), (7, 4)])

        self.assertFalse(diff.nodes.is_view)
        self.assertFalse(diff.edges.is_view)
        self.assertEqual(diff, diff.origin)

        diff = graph_difference(self.graph2, self.graph1, edge_diff=True)

        self.assertItemsEqual(diff.nodes.keys(), range(10, 16))
        self.assertItemsEqual(diff.edges.keys(), [(14, 11), (13, 12), (15, 13), (12, 13), (13, 15), (14, 15), (11, 14),
                                                  (15, 14), (10, 11), (11, 10), (10, 12), (12, 10)])

        self.assertFalse(diff.nodes.is_view)
        self.assertFalse(diff.edges.is_view)
        self.assertEqual(diff, diff.origin)

    def test_combinatorial_symmetric_difference(self):
        """
        Test symmetric difference between two graphs using edge oriented
        difference. Returns a new graph
        """

        diff = graph_symmetric_difference(self.graph1, self.graph2, edge_diff=True)

        self.assertItemsEqual(diff.nodes.keys(), [1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15])
        self.assertItemsEqual(diff.edges.keys(), [(1, 2), (2, 1), (2, 3), (3, 2), (3, 4), (4, 3), (3, 5), (5, 3),
                                                  (11, 14), (14, 11), (12, 13), (13, 12), (14, 15), (15, 14), (13, 15),
                                                  (15, 13), (4, 7), (7, 4), (5, 6), (6, 5), (10, 11), (11, 10),
                                                  (10, 12), (12, 10)])

        self.assertFalse(diff.nodes.is_view)
        self.assertFalse(diff.edges.is_view)
        self.assertEqual(diff, diff.origin)

    def test_combinatorial_symmetric_difference_edgediff(self):
        """
        Test symmetric difference between two graphs. Returns a new graph
        """

        diff = graph_symmetric_difference(self.graph1, self.graph2)

        self.assertItemsEqual(diff.nodes.keys(), [1, 2, 3, 4, 5, 11, 12, 13, 14, 15])
        self.assertItemsEqual(diff.edges.keys(), [(1, 2), (2, 1), (2, 3), (3, 2), (3, 4), (4, 3), (3, 5), (5, 3),
                                                  (11, 14), (14, 11), (12, 13), (13, 12), (14, 15), (15, 14), (13, 15),
                                                  (15, 13)])

        self.assertFalse(diff.nodes.is_view)
        self.assertFalse(diff.edges.is_view)
        self.assertEqual(diff, diff.origin)

    def test_combinatorial_union(self):
        """
        Test union between two graphs. Returns a new graph
        """

        union = graph_union(self.graph1, self.graph2)

        self.assertItemsEqual(union.nodes.keys(), range(1, 16))
        self.assertItemsEqual(union.edges.keys(), [(1, 2), (2, 1), (2, 3), (3, 2), (3, 4), (4, 3), (3, 5), (5, 3),
                                                       (5, 6), (6, 5), (4, 7), (7, 4), (6, 8), (8, 6), (7, 8), (8, 7),
                                                       (8, 9), (9, 8), (9, 10), (10, 9), (10, 11), (11, 10), (12, 10),
                                                       (10, 12), (12, 13), (13, 12), (11, 14), (14, 11), (13, 15),
                                                       (15, 13), (14, 15), (15, 14)])

        self.assertFalse(union.nodes.is_view)
        self.assertFalse(union.edges.is_view)
        self.assertEqual(union, union.origin)

    def test_combinatorial_issubset(self):
        """
        Test graph 1 issubset of graph 2
        """

        graph2 = Graph(auto_nid=False)
        graph2.add_nodes(range(7, 11))
        graph2.add_edges([(7, 8), (8, 9), (9, 10)])

        self.assertTrue(graph_issubset(graph2, self.graph1))
        self.assertFalse(graph_issubset(self.graph1, graph2))

    def test_combinatorial_issuperset(self):
        """
        Test graph 1 issuperset of graph 2
        """

        graph2 = Graph(auto_nid=False)
        graph2.add_nodes(range(7, 11))
        graph2.add_edges([(7, 8), (8, 9), (9, 10)])

        self.assertFalse(graph_issuperset(graph2, self.graph1))
        self.assertTrue(graph_issuperset(self.graph1, graph2))
예제 #4
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)
예제 #5
0
class TestGraphUndirectional(UnittestPythonCompatibility):
    """
    Test graph with undirected edges
    """
    def setUp(self):
        """
        Build undirected Graph
        """

        self.graph = Graph()
        self.graph.add_edges([(1, 2), (2, 3), (2, 4), (4, 5), (4, 3), (3, 5),
                              (3, 6)],
                             node_from_edge=True,
                             arg1=1.22,
                             arg2=False)

    def test_graph_is_undirected(self):

        self.assertFalse(self.graph.directed)
        self.assertEqual(graph_directionality(self.graph), 'undirectional')
        self.assertTrue(
            all([not edge.is_directed for edge in self.graph.iteredges()]))
        self.assertTrue(len(self.graph.edges) == 14)  # 2 * 7

    def test_graph_contains(self):
        """
        Test if pair of directed edges is contained in undirected edge
        """

        for edge in self.graph.edges:
            self.assertTrue(edge in self.graph.edges)
            self.assertTrue(tuple(reversed(edge)) in self.graph.edges)

    def test_graph_adjacency(self):
        """
        Node adjacency in a undirected graph reflects the pairs of directed
        edges that exists between nodes. This is also seen in the adjacency
        'link count' and 'degree' metrics for nodes.
        """

        # Number of edges equals the link count of the full node adjacency
        self.assertEqual(len(self.graph.edges),
                         self.graph.adjacency.link_count())

        # Undirected degree is bidirectional. It equals the number of
        # connected edges to a node
        degree = self.graph.adjacency.degree()
        for node in self.graph:
            self.assertEqual(degree[node.nid], len(node.connected_edges()))

    def test_graph_degree(self):
        """
        Total degree equals sum of equal inwards and outwards degree
        """

        degree = self.graph.adjacency.degree()
        indegree = self.graph.adjacency.degree(method='indegree')
        outdegree = self.graph.adjacency.degree(method='outdegree')

        self.assertDictEqual(indegree, outdegree)
        self.assertEqual(sum(indegree.values()) * 2, sum(degree.values()))

    def test_graph_edge_removal_undirected(self):
        """
        Undirected edge removal removes pair of directed edges
        """

        self.graph.remove_edge(2, 3)

        self.assertFalse((2, 3) in self.graph.edges)
        self.assertFalse((3, 2) in self.graph.edges)
        self.assertEqual(len(self.graph.edges), 12)

    def test_graph_edge_removal_directed(self):
        """
        Directed removal in a undirected graph is supported
        """

        self.graph.remove_edge(2, 3, directed=True)

        self.assertFalse((2, 3) in self.graph.edges)
        self.assertTrue((3, 2) in self.graph.edges)
        self.assertEqual(len(self.graph.edges), 13)

        # globally still marked as undirected but of mixed type
        self.assertFalse(self.graph.directed)
        self.assertEqual(graph_directionality(self.graph), 'mixed')

    def test_graph_edge_removal_directed_data_reference(self):
        """
        Test resolving data references after directed edge removal in a
        undirected graph to prevent orphan pointers
        """

        # Before removal values are referenced
        self.assertDictEqual(self.graph.edges[(2, 3)],
                             self.graph.edges[(2, 3)])
        self.assertEqual(id(self.graph.edges[(2, 3)]),
                         id(self.graph.edges[(2, 3)]))

        # Remove parent edge copies data to referencing edge
        self.graph.remove_edge(2, 3, directed=True)

        self.assertTrue(
            self.graph.edges._data_pointer_key not in self.graph.edges[(3, 2)])
        self.assertDictEqual(self.graph.edges[(3, 2)], {
            'arg1': 1.22,
            'arg2': False
        })

        # Remove referencing edge does not change anything
        self.graph.remove_edge(4, 2, directed=True)

        self.assertTrue(
            self.graph.edges._data_pointer_key not in self.graph.edges[(2, 4)])
        self.assertDictEqual(self.graph.edges[(2, 4)], {
            'arg1': 1.22,
            'arg2': False
        })

    def test_graph_undirectional_to_directional(self):
        """
        Test conversion of a undirectional to directional graph
        Conversion essentially breaks all linked edge pairs by removing the
        data reference pointer.
        """

        directional = graph_undirectional_to_directional(self.graph)

        # directed attribute changed to True
        self.assertTrue(directional.directed)
        self.assertNotEqual(id(directional), id(self.graph))

        # directional graph still contains same nodes and edges
        self.assertEqual(directional, self.graph)

        # data reference pointers determine directionality
        self.assertEqual(graph_directionality(directional), 'directional')
        self.assertEqual(
            graph_directionality(directional, has_data_reference=False),
            'undirectional')

        # directional edge pair no longer point to same value
        directional_checked = []
        for edge in directional.edges:
            directional_checked.append(
                id(directional.edges[edge]) != id(directional.edges[(
                    edge[1], edge[0])]))
        self.assertTrue(all(directional_checked))

    def test_graph_undirected_linked_values(self):
        """
        Test setting and getting linked edge data
        """

        self.graph.edges[(2, 3)]['test'] = True

        # In storage backend only one edge of the pair has the data
        self.assertTrue('test' in self.graph.edges._storage[(2, 3)])
        self.assertFalse('test' in self.graph.edges._storage[(3, 2)])

        # transparent getting and setting of linked data
        self.assertTrue(self.graph.edges[(2, 3)]['test'])
        self.assertTrue(self.graph.edges[(3, 2)]['test'])

        self.graph.edges[(3, 2)]['test'] = False
        self.assertFalse(self.graph.edges[(2, 3)]['test'])
        self.assertFalse(self.graph.edges[(3, 2)]['test'])
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
        })