def test_dfs_reverse_traversal(self) -> None:
        g = DiGraph([(0, 1), (1, 2), (2, 0)])

        vertices = list(dfs_preorder_traversal(g, source=0))
        assert vertices == [g[0], g[1], g[2]], "preorder vertices should be 0, 1, 2"
        vertices = list(dfs_preorder_traversal(g, source=0, reverse_graph=True))
        assert vertices == [g[0], g[2], g[1]], "reverse graph preorder vertices should be 0, 2, 1"

        vertices = list(dfs_postorder_traversal(g, source=0))
        assert vertices == [g[2], g[1], g[0]], "postorder vertices should be 2, 1, 0"
        vertices = list(dfs_postorder_traversal(g, source=0, reverse_graph=True))
        assert vertices == [g[1], g[2], g[0]], "reverse graph postorder vertices should be 1, 2, 0"

        g = DiGraph([(0, 1), (1, 4), (4, 0), (4, 3), (3, 1), (2, 3), (2, 4)])

        vertices = list(dfs_preorder_traversal(g, source=0))
        assert vertices == [g[0], g[1], g[4], g[3]], "preorder vertices should be 0, 1, 4, 3"
        vertices = list(dfs_preorder_traversal(g, source=0, reverse_graph=True))

        preorder1 = [g[0], g[4], g[1], g[3], g[2]]
        preorder2 = [g[0], g[4], g[2], g[1], g[3]]
        assert vertices in (
            preorder1,
            preorder2,
        ), f"reverse graph preorder vertices should be either {preorder1} or {preorder2}."
Example #2
0
 def test_edges_and_edge_count(self) -> None:
     g = DiGraph([(1, 2), (2, 1), (2, 3)])
     assert set(g.edges()) == {
         g.get_edge(1, 2),
         g.get_edge(2, 1),
         g.get_edge(2, 3),
     }, "edges should be (1, 2), (2, 1), (2, 3)"
     assert g.edge_count == 3, "graph should have 3 edges"
Example #3
0
    def test_kruskal_directed_graph(self) -> None:
        g = DiGraph([("s", "t", 10), ("s", "y", 5), ("t", "y", 2)])

        # Kruskal algorithm does not work on directed graphs.
        with pytest.raises(exception.GraphTypeNotSupported):
            for _ in undirected.kruskal_spanning_tree(g):
                pass
    def test_edmonds_empty_graph(self) -> None:
        g = DiGraph()

        # Edmonds' algorithm does not work on empty graphs.
        with pytest.raises(exception.Unfeasible):
            for _ in directed.edmonds(g):
                pass
Example #5
0
    def test_dfs_reverse_traversal(self) -> None:
        g = DiGraph([(0, 1), (1, 2)])

        vertices = list(bfs_vertex_traversal(g, source=1))
        assert vertices == [g[1], g[2]]
        vertices = list(bfs_vertex_traversal(g, source=1, reverse_graph=True))
        assert vertices == [g[1], g[0]]
    def test_edmonds_unfeasible_spanning_arborescence(self) -> None:
        """Test to ensure that Unfeasible raised if a digraph does not contain a spanning
        arborescence."""
        g = DiGraph([("a", "b", 1), ("c", "d", 3)])

        with pytest.raises(exception.Unfeasible):
            _ = next(directed.edmonds(g, find_spanning_arborescence=True))
    def test_bellman_ford_path_reconstruction(self) -> None:
        g = DiGraph(
            [
                ("s", "t", 10),
                ("s", "y", 5),
                ("t", "y", -6),
                ("t", "x", 1),
                ("x", "z", 4),
                ("y", "t", 8),
                ("y", "x", 4),
                ("y", "z", -3),
                ("z", "s", 7),
                ("z", "x", 6),
            ]
        )

        path_dict: VertexDict[ShortestPath[DiVertex]] = bellman_ford(g, "s", save_paths=True)

        assert path_dict["t"].path() == ["s", "t"], "Path s ~> t should be [s, t]."
        assert path_dict["x"].path() == [
            "s",
            "t",
            "y",
            "z",
            "x",
        ], "Path s ~> x should be [s, t, y, z, x]."
        assert path_dict["z"].path() == ["s", "t", "y", "z"], "Path s ~> z should be [s, t, y, z]."

        path_s_t = reconstruct_path("s", "t", path_dict)
        assert path_s_t == path_dict["t"].path(), "Algorithm path should match reconstructed path."
        path_s_x = reconstruct_path("s", "x", path_dict)
        assert path_s_x == path_dict["x"].path(), "Algorithm path should match reconstructed path."
        path_s_z = reconstruct_path("s", "z", path_dict)
        assert path_s_z == path_dict["z"].path(), "Algorithm path should match reconstructed path."
    def test_spanning_arborescence_empty_graph(self) -> None:
        g = DiGraph()

        # Spanning arborescence is undefined for an empty graph.
        with pytest.raises(exception.Unfeasible):
            for _ in directed.spanning_arborescence(g):
                pass
Example #9
0
    def test_bfs_traversal_directed_graph(self) -> None:
        g = DiGraph([(0, 1), (1, 2), (2, 1)])

        tuple_generator = bfs_labeled_edge_traversal(g, source=0)
        parent, child, label, direction, depth = next(tuple_generator)
        assert parent == 0 and child == 0, "traversal should start with source vertex 0"
        assert label == Label.TREE_ROOT, "source vertex should be a BFS tree root"
        assert direction == Direction.PREORDER, "direction should start out as preorder"
        assert depth == 0

        parent, child, label, direction, depth = next(tuple_generator)
        assert parent == 0 and child == 1, "vertex after 0 should be 1"
        assert label == Label.TREE_EDGE
        assert direction == Direction.PREORDER
        assert depth == 1

        parent, child, label, direction, depth = next(tuple_generator)
        assert parent == 0 and child == 0, "finished visiting tree root 0"
        assert label == Label.TREE_ROOT
        assert direction == Direction.POSTORDER
        assert depth == 0

        parent, child, label, direction, depth = next(tuple_generator)
        assert parent == 1 and child == 2, "discovered vertex 2"
        assert label == Label.TREE_EDGE
        assert direction == Direction.PREORDER
        assert depth == 2

        parent, child, label, direction, depth = next(tuple_generator)
        assert parent == 0 and child == 1, "finished visiting vertex 1"
        assert label == Label.TREE_EDGE
        assert direction == Direction.POSTORDER
        assert depth == 1

        parent, child, label, direction, depth = next(tuple_generator)
        assert parent == 2 and child == 1
        assert label == Label.BACK_EDGE
        assert direction == Direction.ALREADY_DISCOVERED
        assert depth == INFINITY

        # Test depth limit.
        vertex_generator = bfs_vertex_traversal(g, source=0, depth_limit=2)
        v = next(vertex_generator)
        assert v == 0
        v = next(vertex_generator)
        assert v == 1
        # With depth_limit = 2, StopIteration should be raised on third request to next().
        with pytest.raises(StopIteration):
            v = next(vertex_generator)

        vertex_generator = bfs_vertex_traversal(g, source=0)
        v = next(vertex_generator)
        assert v == 0, "first preorder vertex should be 0"
        v = next(vertex_generator)
        assert v == 1, "second preorder vertex should be 1"
        v = next(vertex_generator)
        assert v == 2, "third preorder vertex should be 2"
        with pytest.raises(StopIteration):
            v = next(vertex_generator)
Example #10
0
    def test_floyd_warshall_negative_edge_weights(self) -> None:
        g = DiGraph([
            ("s", "t", 10),
            ("s", "y", 5),
            ("t", "y", -6),
            ("t", "x", 1),
            ("x", "z", 4),
            ("y", "t", 8),
            ("y", "x", 4),
            ("y", "z", -3),
            ("z", "s", 7),
            ("z", "x", 6),
            ("a", "s", -100),
        ])

        all_paths_dict: VertexDict[VertexDict[
            ShortestPath[DiVertex]]] = floyd_warshall(g)

        assert (len(all_paths_dict) == 6
                ), "all_paths_dict dictionary should have length equal to |V|."
        assert len(all_paths_dict["s"]) == 6, (
            "Each source in the shortest all_paths_dict dictionary should have "
            "a destinations dictionary of length equal to |V|.")

        assert all_paths_dict["a"][
            "s"].length == -100, "Length of path a ~> s should be -100."
        assert all_paths_dict["a"][
            "t"].length == -90, "Length of path a ~> t should be -90."
        assert (all_paths_dict["s"]["a"].length == INFINITY
                ), "Length of path s ~> a should be infinity."
        assert (all_paths_dict["z"]["a"].length == INFINITY
                ), "Length of path z ~> a should be infinity."
        assert not all_paths_dict["s"]["a"].is_destination_reachable(
        ), "'a' should not be reachable from 's'."

        assert all_paths_dict["s"][
            "s"].length == 0, "length of s path should be 0"
        assert all_paths_dict["s"][
            "t"].length == 10, "length of path s ~> t should be 10"
        assert all_paths_dict["s"][
            "x"].length == 7, "length of path s ~> x should be 7"
        assert all_paths_dict["s"][
            "y"].length == 4, "length of path s ~> y should be 4"
        assert all_paths_dict["s"][
            "z"].length == 1, "length of path s ~> z should be 1"

        assert all_paths_dict["z"][
            "s"].length == 7, "length of path z ~> s should be 7"
        assert all_paths_dict["z"][
            "t"].length == 17, "length of path z ~> t should be 17"
        assert all_paths_dict["z"][
            "x"].length == 6, "length of path z ~> x should be 6"
        assert all_paths_dict["z"][
            "y"].length == 11, "length of path z ~> y should be 11"
        assert all_paths_dict["z"][
            "z"].length == 0, "length of path z ~> z should be 0"
Example #11
0
    def test_add_edge(self) -> None:
        g = DiGraph()
        edge = g.add_edge(2, 1, weight=4.5, color="blue", mass=42)
        assert isinstance(edge, DiEdge), "new edge should an DiEdge object"
        assert g.get_edge(
            2, 1).weight == 4.5, "edge (2, 1) should have weight 4.5"
        assert edge[
            "color"] == "blue", "edge should have 'color' attribute set to 'blue'"
        assert edge[
            "mass"] == 42, "edge should have 'mass' attribute set to 42"

        edge_dup = g.add_edge(2, 1, weight=1.5, color="red", mass=57)
        assert (
            edge is edge_dup
        ), "adding an edge with same vertices as existing edge should return existing edge"

        g.add_edge(3, 4)
        assert (g.get_edge(3, 4).weight == edge_module.DEFAULT_WEIGHT
                ), "edge should have default weight"
        assert not g.get_edge(3, 4).has_attributes_dict(
        ), "edge should not have attributes dictionary"
        assert not g.has_edge(1, 2), "digraph should not have edge (1, 2)"
    def test_min_directed_forest(self) -> None:
        g = DiGraph(test_edges_multi_tree)
        main_arborescence = {("j", "g"), ("g", "h"), ("h", "i"), ("g", "c")}

        count = 0
        weight = 0.0
        for arborescence in directed.optimum_directed_forest(g, minimum=True):
            count += 1
            weight += arborescence.weight
            if len(arborescence) > 2:
                for edge_label in main_arborescence:
                    assert edge_label in arborescence

        assert count == 5, "min branching should have 5 arborescences"
        assert weight == -24
    def test_max_directed_forest(self) -> None:
        g = DiGraph(test_edges_multi_tree)
        main_arborescence = {("d", "a"), ("a", "b"), ("b", "c"), ("c", "f")}

        count = 0
        weight = 0.0
        for arborescence in directed.optimum_directed_forest(g, minimum=False):
            count += 1
            weight += arborescence.weight
            if len(arborescence) > 1:
                for edge_label in main_arborescence:
                    assert edge_label in arborescence

        assert count == 6, "max branching should have 6 arborescences"
        assert weight == 20
Example #14
0
    def test_johnson_negative_weight_cycle(self) -> None:
        g = DiGraph([
            ("s", "t", 10),
            ("s", "y", 5),
            ("t", "y", -6),
            ("t", "x", 1),
            ("x", "z", 4),
            ("y", "t", 8),
            ("y", "x", 4),
            ("y", "z", -3),
            ("z", "s", -2),
            ("z", "x", 6),
        ])

        with pytest.raises(NegativeWeightCycle):
            johnson(g)
    def test_min_spanning_arborescence(self) -> None:
        g = DiGraph(test_edges_acyclic)
        spanning_edges_min_arborescence = {
            ("a", "e"),
            ("a", "b"),
            ("b", "c"),
            ("g", "h"),
            ("f", "g"),
            ("c", "f"),
            ("c", "d"),
        }
        arborescence = directed.spanning_arborescence(g, minimum=True)
        for edge_label in spanning_edges_min_arborescence:
            assert edge_label in arborescence

        assert arborescence.weight == 30, "min spanning arborescence weight should be 30"
Example #16
0
    def test_floyd_warshall_path_reconstruction(self) -> None:
        g = DiGraph([
            ("s", "t", 10),
            ("s", "y", 5),
            ("t", "y", 2),
            ("t", "x", 1),
            ("x", "z", 4),
            ("y", "t", 3),
            ("y", "x", 9),
            ("y", "z", 2),
            ("z", "s", 7),
            ("z", "x", 6),
            ("a", "s", -100),
        ])

        all_paths_dict: VertexDict[VertexDict[
            ShortestPath[DiVertex]]] = floyd_warshall(g, save_paths=True)

        assert all_paths_dict["s"]["z"].path() == ["s", "y", "z"]
        assert all_paths_dict["y"]["s"].path() == ["y", "z", "s"]
        assert all_paths_dict["y"]["t"].path() == [
            "y", "t"
        ], "path y ~> t should be [y, t]"
        assert all_paths_dict["y"]["x"].path() == ["y", "t", "x"]
        assert all_paths_dict["y"]["y"].path() == [
            "y"
        ], "path y ~> y should be [y]"
        assert all_paths_dict["y"]["z"].path() == [
            "y", "z"
        ], "path y ~> z should be [y, z]"
        assert all_paths_dict["y"]["a"].path(
        ) == [], "path y ~> a should be []"

        path_s_z = reconstruct_path("s", "z", all_paths_dict)
        assert path_s_z == all_paths_dict["s"]["z"].path()
        path_y_s = reconstruct_path("y", "s", all_paths_dict)
        assert path_y_s == all_paths_dict["y"]["s"].path()
        path_y_t = reconstruct_path("y", "t", all_paths_dict)
        assert path_y_t == all_paths_dict["y"]["t"].path()
        path_y_x = reconstruct_path("y", "x", all_paths_dict)
        assert path_y_x == all_paths_dict["y"]["x"].path()
        path_y_y = reconstruct_path("y", "y", all_paths_dict)
        assert path_y_y == all_paths_dict["y"]["y"].path()
        path_y_z = reconstruct_path("y", "z", all_paths_dict)
        assert path_y_z == all_paths_dict["y"]["z"].path()
        path_y_a = reconstruct_path("y", "a", all_paths_dict)
        assert path_y_a == all_paths_dict["y"]["a"].path()
    def test_max_spanning_arborescence(self) -> None:
        g = DiGraph(test_edges_acyclic)
        spanning_edges_max_arborescence = {
            ("a", "e"),
            ("a", "b"),
            ("b", "c"),
            ("c", "h"),
            ("c", "d"),
            ("d", "f"),
            ("e", "g"),
        }

        arborescence = directed.spanning_arborescence(g, minimum=False)
        for edge_label in spanning_edges_max_arborescence:
            assert edge_label in arborescence

        assert arborescence.weight == 45, "max spanning arborescence weight should be 45"
    def test_dfs_traversal_directed_graph(self) -> None:
        g = DiGraph([(0, 1), (1, 2), (2, 1)])

        # from pprint import pprint
        # print('\n\nPretty Print DFS Labeled Edge Traversal\n')
        # pprint(list(dfs_labeled_edge_traversal(g, source=0)))

        tuple_generator = dfs_labeled_edge_traversal(g, source=0)
        parent, child, label, direction = next(tuple_generator)
        assert parent == 0 and child == 0, "traversal should start with source vertex 0"
        assert label == Label.TREE_ROOT, "source vertex should be a BFS tree root"
        assert direction == Direction.PREORDER, "direction should start out as preorder"

        parent, child, label, direction = next(tuple_generator)
        assert parent == 0 and child == 1, "vertex after 0 should be 1"
        assert label == Label.TREE_EDGE, "source vertex should be a DFS tree root"
        assert direction == Direction.PREORDER, "direction should start out as preorder"

        vertex_generator = dfs_postorder_traversal(g, source=0)
        v = next(vertex_generator)
        assert v == 2, "first vertex in postorder should be 2"
        v = next(vertex_generator)
        assert v == 1, "second vertex in postorder should 1"
        v = next(vertex_generator)
        assert v == 0, "third vertex in postorder should 0"

        # Test depth limit.
        vertex_generator = dfs_postorder_traversal(g, source=0, depth_limit=2)
        v = next(vertex_generator)
        assert v == 1, "first vertex in postorder should be 1 with depth limited to 2"
        v = next(vertex_generator)
        assert v == 0, "second vertex in postorder should 0"
        # With depth_limit = 2, StopIteration should be raised on third request to next().
        with pytest.raises(StopIteration):
            v = next(vertex_generator)

        vertex_generator = dfs_preorder_traversal(g, source=0)
        v = next(vertex_generator)
        assert v == 0, "first preorder vertex should be 0"
        v = next(vertex_generator)
        assert v == 1, "second preorder vertex should be 1"
        v = next(vertex_generator)
        assert v == 2, "third preorder vertex should be 2"
        with pytest.raises(StopIteration):
            v = next(vertex_generator)
    def test_bellman_ford_negative_weight_cycle(self) -> None:
        g = DiGraph(
            [
                ("s", "t", 10),
                ("s", "y", 5),
                ("t", "y", -6),
                ("t", "x", 1),
                ("x", "z", 4),
                ("y", "t", 8),
                ("y", "x", 4),
                ("y", "z", -3),
                ("z", "s", -2),
                ("z", "x", 6),
            ]
        )

        with pytest.raises(NegativeWeightCycle):
            bellman_ford(g, "s")
Example #20
0
    def test__init__from_graph(self) -> None:
        g = Graph([(2, 1, 5.0, {"color": "red"}), (4, 3)])
        assert g.weight == 6.0, "graph should have weight 6.0"

        dg = DiGraph(g)
        assert dg.has_edge(
            2, 1), "digraph should have edge (2, 1) copied from graph"
        assert (dg.get_edge(2, 1)["color"] == "red"
                ), "edge (2, 1) should have 'color' attribute set to red"
        assert dg.get_edge(
            2, 1).weight == 5.0, "edge (2, 1) should have weight 5.0"
        assert dg.has_edge(
            4, 3), "graph should have edge (4, 3) copied from graph"
        assert dg.edge_count == 2, "graph should have two edges"
        assert dg.weight == 6.0, "graph should have weight 6.0"
        assert dg.get_edge(2, 1) is not g.get_edge(  # type: ignore
            2, 1), "digraph should have deep copies of edges and vertices"
    def test_edmonds_min_spanning_arborescence_cyclic_graph(self) -> None:
        g = DiGraph(test_edges_cyclic)
        spanning_edges_min_arborescence = {
            ("d", "e"),
            ("e", "f"),
            ("f", "h"),
            ("e", "c"),
            ("c", "a"),
            ("a", "b"),
            ("b", "g"),
        }

        arborescence = next(
            directed.edmonds(g, minimum=True, find_spanning_arborescence=True))

        for edge_label in spanning_edges_min_arborescence:
            assert edge_label in arborescence

        assert arborescence.weight == 18, "min spanning arborescence weight should be 18"
Example #22
0
    def test_floyd_warshall_positive_edge_weights(self) -> None:
        g = DiGraph([
            ("s", "t", 10),
            ("s", "y", 5),
            ("t", "y", 2),
            ("t", "x", 1),
            ("x", "z", 4),
            ("y", "t", 3),
            ("y", "x", 9),
            ("y", "z", 2),
            ("z", "s", 7),
            ("z", "x", 6),
        ])

        all_paths_dict: VertexDict[VertexDict[
            ShortestPath[DiVertex]]] = floyd_warshall(g)

        assert len(
            all_paths_dict
        ) == 5, "all_paths_dict dictionary should have length equal to |V|"
        assert len(all_paths_dict["s"]) == 5, (
            "Each source in the shortest all_paths_dict dictionary should have "
            "a destinations dictionary of length equal to |V|.")
        assert all_paths_dict["s"][
            "s"].length == 0, "length of s ~> s path should be 0"
        assert all_paths_dict["s"][
            "t"].length == 8, "length of path s ~> t should be 8"
        assert all_paths_dict["s"][
            "x"].length == 9, "length of path s ~> x should be 9"
        assert all_paths_dict["s"][
            "y"].length == 5, "length of path s ~> y should be 5"
        assert all_paths_dict["s"][
            "z"].length == 7, "length of path s ~> z should be 7"

        assert all_paths_dict["y"][
            "t"].length == 3, "length of path y ~> t should be 3"
        assert all_paths_dict["y"][
            "x"].length == 4, "length of path y ~> x should be 4"
        assert all_paths_dict["y"][
            "s"].length == 9, "length of path y ~> s should be 9"
        assert all_paths_dict["y"][
            "z"].length == 2, "length of path y ~> z should be 2"
Example #23
0
    def test_get_random_edge(self) -> None:
        g = DiGraph([(1, 2), (2, 1), (5, 6)])

        cnt: Counter[DiEdge] = 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(
            2, 1)] > 270, r"~33% of random samples should be edge (2, 1)"
        assert cnt[g.get_edge(
            5, 6)] > 270, r"~33% of random samples should be edge (5, 6)"
Example #24
0
    def test_floyd_warshall_save_paths(self) -> None:
        g = DiGraph([
            ("s", "t", 10),
            ("s", "y", 5),
            ("t", "y", 2),
            ("t", "x", 1),
            ("x", "z", 4),
            ("y", "t", 3),
            ("y", "x", 9),
            ("y", "z", 2),
            ("z", "s", 7),
            ("z", "x", 6),
        ])

        all_paths_dict: VertexDict[VertexDict[
            ShortestPath[DiVertex]]] = floyd_warshall(g, save_paths=False)

        assert all_paths_dict["s"]["z"].path() == []
        assert all_paths_dict["y"]["s"].path() == []
        assert all_paths_dict["y"]["t"].path() == []
        assert all_paths_dict["y"]["x"].path() == []
        assert all_paths_dict["y"]["y"].path() == []
        assert all_paths_dict["y"]["z"].path() == []
class TestShortestPaths:
    """Tests for shortest-paths function."""

    g = DiGraph()
    for i in range(100):
        g.add_edge(i, i + 1)
        g.add_edge(i, i + 3)
    for i in range(100):
        g.add_edge(i + 6, i)

    # shortest_paths(g, source=0)
    bfs_time = timeit.timeit(
        "shortest_paths(g, source=0)", globals={"shortest_paths": shortest_paths, "g": g}, number=5
    )

    for index, edge in enumerate(g.edges()):
        if index % 3 == 0:
            edge._weight = -2
    g._is_weighted_graph = True

    bellman_ford_time = timeit.timeit(
        "shortest_paths(g, source=0)", globals={"shortest_paths": shortest_paths, "g": g}, number=5
    )
    def test_bellman_ford_negative_edge_weights(self) -> None:
        g = DiGraph(
            [
                ("s", "t", 10),
                ("s", "y", 5),
                ("t", "y", -6),
                ("t", "x", 1),
                ("x", "z", 4),
                ("y", "t", 8),
                ("y", "x", 4),
                ("y", "z", -3),
                ("z", "s", 7),
                ("z", "x", 6),
            ]
        )

        path_dict: VertexDict[ShortestPath[DiVertex]] = bellman_ford(g, "s")

        assert len(path_dict) == 5, "Shortest path_dict dictionary should have length equal to |V|."
        assert path_dict["s"].length == 0, "Length of s path should be 0."
        assert path_dict["t"].length == 10, "Length of path s ~> t should be 10."
        assert path_dict["x"].length == 7, "Length of path s ~> x should be 7."
        assert path_dict["y"].length == 4, "Length of path s ~> y should be 4."
        assert path_dict["z"].length == 1, "Length of path s ~> z should be 1."
    def test_dijkstra_fibonacci_default_edge_weight(self) -> None:
        g = DiGraph(
            [
                ("s", "t", 10),
                ("s", "y", 5),
                ("t", "y", 2),
                ("t", "x", 1),
                ("x", "z", 4),
                ("y", "t", 3),
                ("y", "x", 9),
                ("y", "z", 2),
                ("z", "s", 7),
                ("z", "x", 6),
            ]
        )

        path_dict: VertexDict[ShortestPath[DiVertex]] = dijkstra_fibonacci(g, "s")

        assert len(path_dict) == 5, "Shortest path_dict dictionary should have length equal to |V|."
        assert path_dict["s"].length == 0, "Length of s path should be 0."
        assert path_dict["t"].length == 8, "Length of path s ~> t should be 8."
        assert path_dict["x"].length == 9, "Length of path s ~> x should be 9."
        assert path_dict["y"].length == 5, "Length of path s ~> y should be 5."
        assert path_dict["z"].length == 7, "Length of path s ~> z should be 7."
    def test_breadth_first_search_shortest_paths(self) -> None:
        g = DiGraph(
            [
                ("s", "t"),
                ("s", "y"),
                ("t", "y"),
                ("t", "x"),
                ("x", "z"),
                ("y", "t"),
                ("y", "x"),
                ("y", "z"),
                ("z", "s"),
                ("z", "x"),
            ]
        )

        path_dict: VertexDict[ShortestPath[DiVertex]] = breadth_first_search_shortest_paths(g, "s")

        assert len(path_dict) == 5, "Shortest path_dict dictionary should have length equal to |V|."
        assert path_dict["s"].length == 0, "Length of s path should be 0."
        assert path_dict["t"].length == 1, "Length of path s ~> t should be 1."
        assert path_dict["x"].length == 2, "Length of path s ~> x should be 2."
        assert path_dict["y"].length == 1, "Length of path s ~> y should be 1."
        assert path_dict["z"].length == 2, "Length of path s ~> z should be 2."
    def test_vertices_topological_order(self) -> None:
        g = DiGraph([("s", "t"), ("t", "u"), ("u", "v")])

        results: SearchResults[DiVertex, DiEdge] = dfs(g)
        topo_sorted = results.vertices_topological_order()
        assert topo_sorted[0] == "s", "first element of path graph topo sort should be s"
        assert topo_sorted[1] == "t", "second element of path graph topo sort should be t"
        assert topo_sorted[2] == "u", "third element of path graph topo sort should be u"
        assert topo_sorted[3] == "v", "fourth element of path graph topo sort should be v"

        g = DiGraph()
        g.add_edges_from([("s", "v"), ("s", "w"), ("v", "t"), ("w", "t")])

        results: SearchResults[DiVertex, DiEdge] = dfs(g)
        topo_sorted = results.vertices_topological_order()
        assert topo_sorted[0] == "s", "first element of topo sort should be s"
        assert topo_sorted[1] == "v" or topo_sorted[1] == "w", (
            "second element of topo sort " "should be v or w"
        )
        assert topo_sorted[3] == "t", "fourth element topo sort should be t"
    def test_dijkstra_edge_weight_filter_function(self) -> None:
        COLOR = "color_key"

        g = DiGraph(
            [
                ("s", "t", 10),
                ("s", "y", 5),
                ("t", "y", 2),
                ("t", "x", 1),
                ("x", "z", 4),
                ("y", "t", 3),
                ("y", "x", 9),
                ("y", "z", 2),
                ("z", "s", 7),
                ("z", "x", 6),
            ]
        )

        g.get_edge("s", "t")[COLOR] = "RED"
        g.get_edge("s", "y")[COLOR] = "BLUE"
        g.get_edge("t", "y")[COLOR] = "RED"
        g.get_edge("t", "x")[COLOR] = "RED"
        g.get_edge("x", "z")[COLOR] = "RED"
        g.get_edge("y", "t")[COLOR] = "BLUE"
        g.get_edge("y", "x")[COLOR] = "RED"
        g.get_edge("y", "z")[COLOR] = "BLUE"
        g.get_edge("z", "s")[COLOR] = "BLUE"
        g.get_edge("z", "x")[COLOR] = "BLUE"

        # Exclude blue edges.
        def get_weight(v1: V, v2: V, reverse_graph: bool) -> Optional[float]:
            graph = v1._parent_graph
            if reverse_graph:
                edge = graph.get_edge(v2, v1)
                edge_str = f"({v2.label}, {v1.label})"
            else:
                edge = graph.get_edge(v1, v2)
                edge_str = f"({v1.label}, {v2.label})"
            if edge is None:
                raise ValueError(f"graph does not have edge {edge_str}")

            if graph.is_multigraph():
                assert isinstance(edge, MultiEdgeBase)
                has_color = any(c.attr.get(COLOR, "no color") == "BLUE" for c in edge.connections())
                if has_color:
                    return None
                return min(c.weight for c in edge.connections())

            if cast(Attributes, edge).attr.get(COLOR, "no color attribute") == "BLUE":
                return None
            return edge.weight

        path_dict: VertexDict[ShortestPath[DiVertex]] = dijkstra(g, "s", weight=get_weight)
        assert path_dict["s"].length == 0, "Length of s path should be 0."
        assert path_dict["t"].length == 10, "Length of path s ~> t should be 10."
        assert path_dict["x"].length == 11, "Length of path s ~> x should be 11."
        assert path_dict["y"].length == 12, "Length of path s ~> y should be 12."
        assert path_dict["z"].length == 15, "Length of path s ~> z should be 15."