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_bellman_ford_undirected_negative_weight_cycle(self) -> None: g = MultiGraph( [ ("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), ] ) with pytest.raises(NegativeWeightCycle): bellman_ford(g, "s")
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_bellman_ford_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]] = 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 == 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_bellman_ford_undirected(self) -> None: g = MultiGraph( [ ("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[MultiVertex]] = 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 == 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 johnson_fibonacci( graph: "GraphBase[V_co, E_co]", save_paths: bool = False, weight: str = "Edge__weight" ) -> "VertexDict[VertexDict[ShortestPath[V_co]]]": r"""Finds the shortest paths between all pairs of vertices in a graph using Donald Johnson's algorithm implemented with a Fibonacci heap version of Dijkstra's algorithm. Running time: :math:`O(n^2 (\log{n}) + mn)` where :math:`m = |E|` and :math:`n = |V|` Pairs of vertices for which there is no connecting path will have path length infinity. In additional, :meth:`ShortestPath.is_destination_reachable() <vertizee.algorithms.algo_utils.path_utils.ShortestPath.is_destination_reachable>` will return False. Note: This implementation is based on JOHNSON. :cite:`2009:clrs` Args: graph: The graph to search. save_paths: Optional; If True, saves the actual vertex sequences comprising each path. To reconstruct specific shortest paths, see :func:`vertizee.algorithms.algo_utils.path_utils.reconstruct_path`. Defaults to False. weight: Optional; The key to use to retrieve the weight from the edge ``attr`` dictionary. The default value ("Edge__weight") uses the edge property ``weight``. Returns: VertexDict[VertexDict[ShortestPath]]: A dictionary mapping source vertices to dictionaries mapping destination vertices to :class:`ShortestPath <vertizee.algorithms.algo_utils.path_utils.ShortestPath>` objects. Raises: NegativeWeightCycle: If the graph contains a negative weight cycle. See Also: * :func:`reconstruct_path <vertizee.algorithms.algo_utils.path_utils.reconstruct_path>` * :class:`ShortestPath <vertizee.algorithms.algo_utils.path_utils.ShortestPath>` * :class:`VertexDict <vertizee.classes.data_structures.vertex_dict.VertexDict>` """ weight_function = get_weight_function(weight) g_prime = graph.deepcopy() G_PRIME_SOURCE = "__g_prime_src" for v in graph.vertices(): g_prime.add_edge(G_PRIME_SOURCE, v, weight=0) bellman_paths: VertexDict[ShortestPath[V_co]] = single_source.bellman_ford( g_prime, G_PRIME_SOURCE) # pylint: disable=unused-argument def new_weight(v1: VertexType, v2: VertexType, reverse_graph: bool = False) -> float: edge = graph.get_edge(v1, v2) return weight_function( edge) + bellman_paths[v1].length - bellman_paths[v2].length source_and_destination_to_path: VertexDict[VertexDict[ ShortestPath[V_co]]] = VertexDict() for i in graph: source_and_destination_to_path[i] = VertexDict() dijkstra_paths: VertexDict[ ShortestPath[V_co]] = single_source.dijkstra_fibonacci( graph, source=i, weight=new_weight, save_paths=save_paths) for j in graph: source_and_destination_to_path[i][j] = dijkstra_paths[j] source_and_destination_to_path[i][j]._length += ( bellman_paths[j].length - bellman_paths[i].length) return source_and_destination_to_path