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}."
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"
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
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
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)
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"
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
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"
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")
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"
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"
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)"
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."