Example #1
0
    def test_exceptions(self) -> None:
        empty_g = Graph()
        with pytest.raises(exception.Unfeasible):
            components.connected_components(empty_g)

        g = Graph([(1, 2)])
        with pytest.raises(exception.GraphTypeNotSupported):
            components.strongly_connected_components(g)
Example #2
0
    def test_allows_self_loops(self) -> None:
        g = Graph()
        assert g.allows_self_loops(
        ), "graph should default to allowing self loops"
        g.add_edge(1, 1)  # graph should allow adding self loop

        g2 = Graph(allow_self_loops=False)
        assert not g2.allows_self_loops(
        ), "graph 2 should not allow self loops"
        with pytest.raises(exception.SelfLoopsNotAllowed):
            g2.add_edge(1, 1)
Example #3
0
 def test_clear(self) -> None:
     g = Graph([(1, 2), (2, 3), (3, 4)])
     assert g.edge_count > 0, "graph should have edges"
     assert g.vertex_count > 0, "graph should have vertices"
     g.clear()
     assert g.edge_count == 0, "graph should not have edges after clear()"
     assert g.vertex_count == 0, "graph should not have vertices after clear()"
Example #4
0
 def test_vertex_count(self) -> None:
     g = Graph([(1, 2), (2, 3), (3, 4)])
     assert g.vertex_count == 4, "graph should have 4 vertices"
     g.add_vertex(5)
     assert g.vertex_count == 5, "graph should have 5 vertices"
     g.remove_vertex(5)
     assert g.vertex_count == 4, "graph should have 4 vertices"
Example #5
0
    def test_add_edges_from(self) -> None:
        g = Graph()
        g.add_edges_from([
            (1, 2, 4.5, {
                "color": "blue",
                "mass": 42
            }),
            (4, 3, 9.5),
            (5, 6, {
                "color": "red",
                "mass": 99
            }),
            (8, 7),
            (7, 8),
        ])

        assert g.edge_count == 4, "graph should have 4 edges"
        assert g.get_edge(
            1, 2).weight == 4.5, "edge (1, 2) should have weight 4.5"
        assert g.get_edge(
            1, 2
        )["color"] == "blue", "edge (1, 2) should have 'color' set to 'blue'"
        assert g.get_edge(
            1, 2)["mass"] == 42, "edge (1, 2) should have 'mass' set to 42"
        assert not g.get_edge(3, 4).has_attributes_dict(
        ), "edge (3, 4) should not have attributes dict"
        assert (g.get_edge(5, 6).weight == edge_module.DEFAULT_WEIGHT
                ), "edge should have default weight"
        assert g.get_edge(7, 8) is g.get_edge(
            8, 7), "order of vertices should not matter"
Example #6
0
 def test__len__(self) -> None:
     g = Graph([(1, 2), (2, 3), (3, 4)])
     tree: Tree[Vertex, Edge] = Tree(g[1])
     tree.add_edge(g.get_edge(1, 2))
     tree.add_edge(g.get_edge(2, 3))
     tree.add_edge(g.get_edge(3, 4))
     assert len(tree) == 4, "tree should contain 4 vertices"
    def test_edmonds_undirected_graph(self) -> None:
        g = Graph([("s", "t", 10), ("s", "y", 5), ("t", "y", 2)])

        # Edmonds' algorithm does not work on undirected graphs.
        with pytest.raises(exception.GraphTypeNotSupported):
            for _ in directed.edmonds(g):
                pass
    def test_spanning_arborescence_undirected_graph(self) -> None:
        g = Graph([("s", "t", 10), ("s", "y", 5), ("t", "y", 2)])

        # Spanning arborescence is undefined for an undirected graph.
        with pytest.raises(exception.GraphTypeNotSupported):
            for _ in directed.spanning_arborescence(g):
                pass
Example #9
0
    def test_component(self) -> None:
        g = Graph([(1, 2), (2, 3), (4, 5), (7, 7)])
        component_list: List[Component[Vertex, Edge]] = list(
            components.connected_components(g))
        c_123: Component[Vertex,
                         Edge] = [c for c in component_list if 1 in c][0]
        assert not c_123._edge_set, "without accessing edges, `_edge_set` should not be initialized"

        c_123.edges()
        assert c_123._edge_set, "after accessing edges, `_edge_set` should be initialized"

        c_45 = None
        c_77 = None
        for component in component_list:
            if (4, 5) in component:
                c_45 = component
            elif (7, 7) in component:
                c_77 = component

        assert c_45 is not None
        assert c_77 is not None
        assert (
            c_45._edge_set is not None
        ), "calling __contains__ should result in _edge_set initialization"
        assert (
            c_77._edge_set is not None
        ), "calling __contains__ should result in _edge_set initialization"
        assert g[
            7] in c_77, "vertex 7 should be in component containing edge (7, 7)"
Example #10
0
 def test_vertices(self) -> None:
     g = Graph([(1, 2), (2, 3), (3, 4)])
     assert set(g.vertices()) == {
         g[1],
         g[2],
         g[3],
         g[4],
     }, "graph should have vertices 1, 2, 3, 4"
Example #11
0
    def test_kruskal_optimum_forest_single_tree(self) -> None:
        g = Graph(test_edges)

        spanning_edges = set(undirected.kruskal_spanning_tree(g))
        spanning_tree = next(undirected.kruskal_optimum_forest(g))

        assert spanning_edges == set(spanning_tree.edges())
        assert spanning_tree.weight == 28, "Min spanning tree weight should be 28."
Example #12
0
 def test_edges_and_edge_count(self) -> None:
     g = Graph([(1, 2), (2, 1), (2, 3), (3, 4)])
     assert set(g.edges()) == {
         g.get_edge(1, 2),
         g.get_edge(2, 3),
         g.get_edge(3, 4),
     }, "edges should be (1, 2), (2, 3), (3, 4)"
     assert g.edge_count == 3, "graph should have 3 edges"
Example #13
0
    def test_attr(self) -> None:
        g = Graph([(1, 2), (3, 4)], name="undirected graph", secret=42)

        assert g.attr["name"] == "undirected graph"
        assert g.attr[
            "secret"] == 42, "graph should have 'secret' attribute set to 42"
        with pytest.raises(KeyError):
            _ = g.attr["unknown_key"]
Example #14
0
 def test_has_vertex(self) -> None:
     g = Graph()
     g.add_edge(1, 2)
     assert g.has_vertex(1), "vertex specified as int should be in graph"
     assert g.has_vertex("1"), "vertex specified as str should be in graph"
     v1 = g[1]
     assert g.has_vertex(
         v1), "vertex specified as object should be in graph"
     assert not g.has_vertex(3), "vertex 3 should not be in the graph"
Example #15
0
 def test_is_weighted(self) -> None:
     g = Graph()
     g.add_edge(1, 2)
     assert (
         not g.is_weighted()
     ), "graph without custom edge weights should not identify as weighted"
     g.add_edge(3, 4, weight=9.5)
     assert g.is_weighted(
     ), "graph with custom edge weights should identify as weighted"
Example #16
0
    def test_merge(self) -> None:
        g = Graph([(1, 2), (2, 3), (1, 4), (3, 4), (4, 5)])
        tree1: Tree[Vertex, Edge] = Tree(g[1])
        tree5: Tree[Vertex, Edge] = Tree(g[5])

        tree1.add_edge(g.get_edge(1, 4))
        tree1.add_edge(g.get_edge(4, 3))
        tree5.add_edge(g.get_edge(5, 4))
        tree1.merge(tree5)
        assert set(tree1.vertices()) == {tree1[1], tree1[3], tree1[4], tree1[5]}
Example #17
0
    def test_bfs_undirected_graph_with_self_loop_and_two_trees(self) -> None:
        g = Graph([(0, 0), (0, 1), (1, 2), (3, 4)])
        results: SearchResults[Vertex, Edge] = bfs(g)

        assert len(results.graph_search_trees()) == 2, "BFS should have discovered two BFS trees"
        assert len(results.vertices_preorder()) == 5, "BFS tree should have 5 vertices"
        assert (
            results.vertices_preorder() == results.vertices_postorder()
        ), "a BFS should yield vertices in same order for both preorder and postorder"
        assert len(results.back_edges()) == 1, "graph should have one self-loop back edge"
Example #18
0
 def test_add_vertices_from(self) -> None:
     g = Graph()
     g.add_vertices_from([1, "2", ("v3", {"color": "blue", "mass": 42})])
     assert g.has_vertex(1), "graph should have vertex 1"
     assert g.has_vertex(2), "graph should have vertex 2"
     assert g.has_vertex("v3"), "graph should have vertex v3"
     assert g["v3"][
         "color"] == "blue", "vertex should have 'color' attribute set to 'blue'"
     assert g["v3"][
         "mass"] == 42, "vertex should have 'mass' attribute set to 42"
Example #19
0
 def test__iter__(self) -> None:
     g = Graph([(1, 2), (2, 3), (3, 4)])
     tree: Tree[Vertex, Edge] = Tree(g[1])
     tree.add_edge(g.get_edge(1, 2))
     tree.add_edge(g.get_edge(2, 3))
     tree.add_edge(g.get_edge(3, 4))
     count = sum(1 for _ in tree)
     assert count == 4, "tree should iterate over its 4 vertices"
     assert set([tree[1], tree[2], tree[3], tree[4]]) == set(
         tree
     ), "tree should iterate over its 4 vertices"
Example #20
0
 def test_add_vertex(self) -> None:
     g = Graph()
     g.add_vertex(1)
     g.add_vertex("2")
     g.add_vertex("v3", color="blue", mass=42)
     assert g.has_vertex(1), "graph should have vertex 1"
     assert g.has_vertex(2), "graph should have vertex 2"
     assert g.has_vertex("v3"), "graph should have vertex v3"
     assert g["v3"][
         "color"] == "blue", "vertex should have 'color' attribute set to 'blue'"
     assert g["v3"][
         "mass"] == 42, "vertex should have 'mass' attribute set to 42"
Example #21
0
    def test_contains(self) -> None:
        d: VertexDict[str] = VertexDict(**{"1": "one", "2": "two"})
        assert 1 in d
        assert "1" in d
        assert d.__contains__(2)

        g = Graph([(0, "six")])
        d2: VertexDict[str] = VertexDict()
        d2[g[0]] = "zero"
        d2[g["six"]] = "six"
        assert 0 in d2
        assert g[0] in d2
        assert "six" in d2
        assert g["six"] in d2
Example #22
0
    def test_kruskal_optimum_forest_multiple_trees(self) -> None:
        g = Graph(test_edges)
        g.add_edge("x", "y", weight=22)
        g.add_edge("y", "z", weight=20)
        g.add_vertex("isolated")

        count = 0
        total_weight = 0.0
        for tree in undirected.kruskal_optimum_forest(g):
            count += 1
            total_weight += tree.weight

        assert count == 3, "there should be 3 trees in the spanning forest"
        assert total_weight == 70, "total weight of trees should be 70"
Example #23
0
    def test_add_edge(self) -> None:
        g = Graph([(1, 2), (2, 3), (1, 4), (3, 4), (4, 5)])
        tree: Tree[Vertex, Edge] = Tree(g[1])
        tree.add_edge(g.get_edge(1, 2))
        assert (1, 2) in tree

        tree.add_edge(g.get_edge(2, 3))
        with pytest.raises(exception.Unfeasible):
            # Raise exception due to (4, 5) not containing a vertex already in the tree.
            tree.add_edge(g.get_edge(4, 5))

        tree.add_edge(g.get_edge(3, 4))
        with pytest.raises(exception.Unfeasible):
            # Raises exception due to cycle.
            tree.add_edge(g.get_edge(1, 4))
Example #24
0
    def test_get_random_edge(self) -> None:
        g = Graph([(1, 2), (3, 4), (5, 6)])

        cnt: Counter[Edge] = collections.Counter()
        for _ in range(1000):
            rand_edge = g.get_random_edge()
            if rand_edge is None:
                raise Exception("rand_edge returned None")
            cnt[rand_edge] += 1
        assert cnt[g.get_edge(
            1, 2)] > 270, r"~33% of random samples should be edge (1, 2)"
        assert cnt[g.get_edge(
            3, 4)] > 270, r"~33% of random samples should be edge (3, 4)"
        assert cnt[g.get_edge(
            5, 6)] > 270, r"~33% of random samples should be edge (5, 6)"
Example #25
0
    def test__getitem__setitem(self) -> None:
        g = Graph()
        v1: Vertex = g.add_vertex(1)
        pairs = [("1", "one"), (2, "two")]
        d1: VertexDict[str] = VertexDict(pairs)

        assert d1[
            v1] == "one", "Dict d1 getitem should work with Vertex object key"
        assert d1[1] == "one", "Dict d1 getitem should work with int key"
        assert d1["1"] == "one", "Dict d1 getitem should work with int key"
        assert d1[
            "2"] == "two", "Dict d1 should have item associated with key 2"
        d1[2] = "new value"
        assert d1[
            "2"] == "new value", 'Dict d1 should have "new value" for key 2'
Example #26
0
    def test_remove_vertex(self) -> None:
        g = Graph([(1, 2), (3, 3)])
        g.add_vertex(4)

        assert g.has_vertex(4), "graph should have vertex 4"
        g.remove_vertex(4)
        assert not g.has_vertex(
            4), "graph should not have vertex 4 after removal"

        assert g.has_vertex(3), "graph should have semi-isolated vertex 3"
        g.remove_vertex(3)
        assert not g.has_vertex(
            3), "graph should not have vertex 3 after removal"

        with pytest.raises(exception.VertizeeException):
            g.remove_vertex(1)
Example #27
0
    def test_remove_edge(self) -> None:
        g = Graph([(1, 2), (2, 3), (3, 4)])
        assert g.edge_count == 3, "graph should have 3 edges"
        g.remove_edge(1, 2)
        assert g.edge_count == 2, "after edge removal, graph should have 2 edges"
        assert g.has_vertex(
            1), "isolated vertex 1 should not have been removed"

        g.remove_edge(2, 3, remove_semi_isolated_vertices=True)
        assert g.edge_count == 1, "after edge removal, graph should have 1 edge"
        assert not g.has_vertex(
            2
        ), "with `remove_semi_isolated_vertices` set to True, vertex 2 should have been removed"

        with pytest.raises(exception.EdgeNotFound):
            g.remove_edge(8, 9)
Example #28
0
    def test_connected_components(self) -> None:
        g = Graph([(1, 2), (2, 3), (4, 5), (7, 7)])
        g.add_vertex(8)
        component_list: List[Component[Vertex, Edge]] = list(
            components.connected_components(g))
        assert len(component_list) == 4, "graph should have 4 components"
        edge_count = sum(
            len(list(component.edges())) for component in component_list)
        assert edge_count == 4, "components should contain a grand total of 4 edges"

        mg: MultiDiGraph = get_example_multidigraph()
        scc_list: List[Component[MultiDiVertex, MultiDiEdge]] = list(
            components.connected_components(mg))
        assert len(
            scc_list
        ) == 4, "multidigraph should have 4 strongly-connected components"
    def test_dfs_traversal_undirected_graph(self) -> None:
        g = Graph([(0, 1), (1, 2), (1, 3), (2, 3), (3, 4), (4, 5), (3, 5), (6, 7)])
        edge_iter = dfs_labeled_edge_traversal(g)
        dfs_edge_tuples = list(edge_iter)

        tree_roots = set(
            child for parent, child, label, direction in dfs_edge_tuples if label == Label.TREE_ROOT
        )
        assert len(tree_roots) == 2, "there should be two DFS trees"

        vertices = set(child for parent, child, label, direction in dfs_edge_tuples)
        assert len(vertices) == 8, "DFS traversal should include all vertices"

        vertices_preorder = list(
            child
            for parent, child, label, direction in dfs_edge_tuples
            if direction == Direction.PREORDER
        )
        vertices_postorder = list(
            child
            for parent, child, label, direction in dfs_edge_tuples
            if direction == Direction.POSTORDER
        )
        assert len(vertices_preorder) == len(
            vertices_postorder
        ), "the number of preorder vertices should match the number of postorder vertices"

        back_edges = set(
            (parent, child)
            for parent, child, label, direction in dfs_edge_tuples
            if label == Label.BACK_EDGE
        )
        cross_edges = set(
            (parent, child)
            for parent, child, label, direction in dfs_edge_tuples
            if label == Label.CROSS_EDGE
        )
        forward_edges = set(
            (parent, child)
            for parent, child, label, direction in dfs_edge_tuples
            if label == Label.FORWARD_EDGE
        )

        assert len(back_edges) > 0, "graph should have back edges, since there are cycles"
        assert (
            not cross_edges and not forward_edges
        ), "DFS on an undirected graph, every edge is either a tree edge or a back edge"
    def test_dfs_undirected_cyclic_graph_with_self_loop(self) -> None:
        g = Graph([(0, 0), (0, 1), (1, 2), (3, 4)])
        results: SearchResults[Vertex, Edge] = dfs(g)

        assert len(results.graph_search_trees()) == 2, "DFS should have discovered two DFS trees"
        assert len(results.vertices_preorder()) == 5, "DFS tree should have 5 vertices"
        assert len(results.vertices_preorder()) == len(
            results.vertices_postorder()
        ), "number of vertices should be the same in discovery as well post order"
        assert len(results.back_edges()) == 1, "graph should have one self-loop back edge"
        assert (
            len(results.cross_edges()) == 0
        ), "graph should have zero cross edges (true for all undirected graphs)"
        assert (
            len(results.forward_edges()) == 0
        ), "graph should have zero forward edges (true for all undirected graphs)"
        assert not results.is_acyclic(), "should not be acyclic, since it contains a self loop"