about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests')
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/__init__.py0
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_astar.py248
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_dense.py212
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py89
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_generic.py450
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_unweighted.py149
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_weighted.py972
7 files changed, 2120 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/__init__.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/__init__.py
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_astar.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_astar.py
new file mode 100644
index 00000000..40a7d4e8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_astar.py
@@ -0,0 +1,248 @@
+import pytest
+
+import networkx as nx
+from networkx.utils import pairwise
+
+
+class TestAStar:
+    @classmethod
+    def setup_class(cls):
+        edges = [
+            ("s", "u", 10),
+            ("s", "x", 5),
+            ("u", "v", 1),
+            ("u", "x", 2),
+            ("v", "y", 1),
+            ("x", "u", 3),
+            ("x", "v", 5),
+            ("x", "y", 2),
+            ("y", "s", 7),
+            ("y", "v", 6),
+        ]
+        cls.XG = nx.DiGraph()
+        cls.XG.add_weighted_edges_from(edges)
+
+    def test_multiple_optimal_paths(self):
+        """Tests that A* algorithm finds any of multiple optimal paths"""
+        heuristic_values = {"a": 1.35, "b": 1.18, "c": 0.67, "d": 0}
+
+        def h(u, v):
+            return heuristic_values[u]
+
+        graph = nx.Graph()
+        points = ["a", "b", "c", "d"]
+        edges = [("a", "b", 0.18), ("a", "c", 0.68), ("b", "c", 0.50), ("c", "d", 0.67)]
+
+        graph.add_nodes_from(points)
+        graph.add_weighted_edges_from(edges)
+
+        path1 = ["a", "c", "d"]
+        path2 = ["a", "b", "c", "d"]
+        assert nx.astar_path(graph, "a", "d", h) in (path1, path2)
+
+    def test_astar_directed(self):
+        assert nx.astar_path(self.XG, "s", "v") == ["s", "x", "u", "v"]
+        assert nx.astar_path_length(self.XG, "s", "v") == 9
+
+    def test_astar_directed_weight_function(self):
+        w1 = lambda u, v, d: d["weight"]
+        assert nx.astar_path(self.XG, "x", "u", weight=w1) == ["x", "u"]
+        assert nx.astar_path_length(self.XG, "x", "u", weight=w1) == 3
+        assert nx.astar_path(self.XG, "s", "v", weight=w1) == ["s", "x", "u", "v"]
+        assert nx.astar_path_length(self.XG, "s", "v", weight=w1) == 9
+
+        w2 = lambda u, v, d: None if (u, v) == ("x", "u") else d["weight"]
+        assert nx.astar_path(self.XG, "x", "u", weight=w2) == ["x", "y", "s", "u"]
+        assert nx.astar_path_length(self.XG, "x", "u", weight=w2) == 19
+        assert nx.astar_path(self.XG, "s", "v", weight=w2) == ["s", "x", "v"]
+        assert nx.astar_path_length(self.XG, "s", "v", weight=w2) == 10
+
+        w3 = lambda u, v, d: d["weight"] + 10
+        assert nx.astar_path(self.XG, "x", "u", weight=w3) == ["x", "u"]
+        assert nx.astar_path_length(self.XG, "x", "u", weight=w3) == 13
+        assert nx.astar_path(self.XG, "s", "v", weight=w3) == ["s", "x", "v"]
+        assert nx.astar_path_length(self.XG, "s", "v", weight=w3) == 30
+
+    def test_astar_multigraph(self):
+        G = nx.MultiDiGraph(self.XG)
+        G.add_weighted_edges_from((u, v, 1000) for (u, v) in list(G.edges()))
+        assert nx.astar_path(G, "s", "v") == ["s", "x", "u", "v"]
+        assert nx.astar_path_length(G, "s", "v") == 9
+
+    def test_astar_undirected(self):
+        GG = self.XG.to_undirected()
+        # make sure we get lower weight
+        # to_undirected might choose either edge with weight 2 or weight 3
+        GG["u"]["x"]["weight"] = 2
+        GG["y"]["v"]["weight"] = 2
+        assert nx.astar_path(GG, "s", "v") == ["s", "x", "u", "v"]
+        assert nx.astar_path_length(GG, "s", "v") == 8
+
+    def test_astar_directed2(self):
+        XG2 = nx.DiGraph()
+        edges = [
+            (1, 4, 1),
+            (4, 5, 1),
+            (5, 6, 1),
+            (6, 3, 1),
+            (1, 3, 50),
+            (1, 2, 100),
+            (2, 3, 100),
+        ]
+        XG2.add_weighted_edges_from(edges)
+        assert nx.astar_path(XG2, 1, 3) == [1, 4, 5, 6, 3]
+
+    def test_astar_undirected2(self):
+        XG3 = nx.Graph()
+        edges = [(0, 1, 2), (1, 2, 12), (2, 3, 1), (3, 4, 5), (4, 5, 1), (5, 0, 10)]
+        XG3.add_weighted_edges_from(edges)
+        assert nx.astar_path(XG3, 0, 3) == [0, 1, 2, 3]
+        assert nx.astar_path_length(XG3, 0, 3) == 15
+
+    def test_astar_undirected3(self):
+        XG4 = nx.Graph()
+        edges = [
+            (0, 1, 2),
+            (1, 2, 2),
+            (2, 3, 1),
+            (3, 4, 1),
+            (4, 5, 1),
+            (5, 6, 1),
+            (6, 7, 1),
+            (7, 0, 1),
+        ]
+        XG4.add_weighted_edges_from(edges)
+        assert nx.astar_path(XG4, 0, 2) == [0, 1, 2]
+        assert nx.astar_path_length(XG4, 0, 2) == 4
+
+    """ Tests that A* finds correct path when multiple paths exist
+        and the best one is not expanded first (GH issue #3464)
+    """
+
+    def test_astar_directed3(self):
+        heuristic_values = {"n5": 36, "n2": 4, "n1": 0, "n0": 0}
+
+        def h(u, v):
+            return heuristic_values[u]
+
+        edges = [("n5", "n1", 11), ("n5", "n2", 9), ("n2", "n1", 1), ("n1", "n0", 32)]
+        graph = nx.DiGraph()
+        graph.add_weighted_edges_from(edges)
+        answer = ["n5", "n2", "n1", "n0"]
+        assert nx.astar_path(graph, "n5", "n0", h) == answer
+
+    """ Tests that parent is not wrongly overridden when a node
+        is re-explored multiple times.
+    """
+
+    def test_astar_directed4(self):
+        edges = [
+            ("a", "b", 1),
+            ("a", "c", 1),
+            ("b", "d", 2),
+            ("c", "d", 1),
+            ("d", "e", 1),
+        ]
+        graph = nx.DiGraph()
+        graph.add_weighted_edges_from(edges)
+        assert nx.astar_path(graph, "a", "e") == ["a", "c", "d", "e"]
+
+    # >>> MXG4=NX.MultiGraph(XG4)
+    # >>> MXG4.add_edge(0,1,3)
+    # >>> NX.dijkstra_path(MXG4,0,2)
+    # [0, 1, 2]
+
+    def test_astar_w1(self):
+        G = nx.DiGraph()
+        G.add_edges_from(
+            [
+                ("s", "u"),
+                ("s", "x"),
+                ("u", "v"),
+                ("u", "x"),
+                ("v", "y"),
+                ("x", "u"),
+                ("x", "w"),
+                ("w", "v"),
+                ("x", "y"),
+                ("y", "s"),
+                ("y", "v"),
+            ]
+        )
+        assert nx.astar_path(G, "s", "v") == ["s", "u", "v"]
+        assert nx.astar_path_length(G, "s", "v") == 2
+
+    def test_astar_nopath(self):
+        with pytest.raises(nx.NodeNotFound):
+            nx.astar_path(self.XG, "s", "moon")
+
+    def test_astar_cutoff(self):
+        with pytest.raises(nx.NetworkXNoPath):
+            # optimal path_length in XG is 9
+            nx.astar_path(self.XG, "s", "v", cutoff=8.0)
+        with pytest.raises(nx.NetworkXNoPath):
+            nx.astar_path_length(self.XG, "s", "v", cutoff=8.0)
+
+    def test_astar_admissible_heuristic_with_cutoff(self):
+        heuristic_values = {"s": 36, "y": 4, "x": 0, "u": 0, "v": 0}
+
+        def h(u, v):
+            return heuristic_values[u]
+
+        assert nx.astar_path_length(self.XG, "s", "v") == 9
+        assert nx.astar_path_length(self.XG, "s", "v", heuristic=h) == 9
+        assert nx.astar_path_length(self.XG, "s", "v", heuristic=h, cutoff=12) == 9
+        assert nx.astar_path_length(self.XG, "s", "v", heuristic=h, cutoff=9) == 9
+        with pytest.raises(nx.NetworkXNoPath):
+            nx.astar_path_length(self.XG, "s", "v", heuristic=h, cutoff=8)
+
+    def test_astar_inadmissible_heuristic_with_cutoff(self):
+        heuristic_values = {"s": 36, "y": 14, "x": 10, "u": 10, "v": 0}
+
+        def h(u, v):
+            return heuristic_values[u]
+
+        # optimal path_length in XG is 9. This heuristic gives over-estimate.
+        assert nx.astar_path_length(self.XG, "s", "v", heuristic=h) == 10
+        assert nx.astar_path_length(self.XG, "s", "v", heuristic=h, cutoff=15) == 10
+        with pytest.raises(nx.NetworkXNoPath):
+            nx.astar_path_length(self.XG, "s", "v", heuristic=h, cutoff=9)
+        with pytest.raises(nx.NetworkXNoPath):
+            nx.astar_path_length(self.XG, "s", "v", heuristic=h, cutoff=12)
+
+    def test_astar_cutoff2(self):
+        assert nx.astar_path(self.XG, "s", "v", cutoff=10.0) == ["s", "x", "u", "v"]
+        assert nx.astar_path_length(self.XG, "s", "v") == 9
+
+    def test_cycle(self):
+        C = nx.cycle_graph(7)
+        assert nx.astar_path(C, 0, 3) == [0, 1, 2, 3]
+        assert nx.dijkstra_path(C, 0, 4) == [0, 6, 5, 4]
+
+    def test_unorderable_nodes(self):
+        """Tests that A* accommodates nodes that are not orderable.
+
+        For more information, see issue #554.
+
+        """
+        # Create the cycle graph on four nodes, with nodes represented
+        # as (unorderable) Python objects.
+        nodes = [object() for n in range(4)]
+        G = nx.Graph()
+        G.add_edges_from(pairwise(nodes, cyclic=True))
+        path = nx.astar_path(G, nodes[0], nodes[2])
+        assert len(path) == 3
+
+    def test_astar_NetworkXNoPath(self):
+        """Tests that exception is raised when there exists no
+        path between source and target"""
+        G = nx.gnp_random_graph(10, 0.2, seed=10)
+        with pytest.raises(nx.NetworkXNoPath):
+            nx.astar_path(G, 4, 9)
+
+    def test_astar_NodeNotFound(self):
+        """Tests that exception is raised when either
+        source or target is not in graph"""
+        G = nx.gnp_random_graph(10, 0.2, seed=10)
+        with pytest.raises(nx.NodeNotFound):
+            nx.astar_path_length(G, 11, 9)
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_dense.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_dense.py
new file mode 100644
index 00000000..6923bfef
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_dense.py
@@ -0,0 +1,212 @@
+import pytest
+
+import networkx as nx
+
+
+class TestFloyd:
+    @classmethod
+    def setup_class(cls):
+        pass
+
+    def test_floyd_warshall_predecessor_and_distance(self):
+        XG = nx.DiGraph()
+        XG.add_weighted_edges_from(
+            [
+                ("s", "u", 10),
+                ("s", "x", 5),
+                ("u", "v", 1),
+                ("u", "x", 2),
+                ("v", "y", 1),
+                ("x", "u", 3),
+                ("x", "v", 5),
+                ("x", "y", 2),
+                ("y", "s", 7),
+                ("y", "v", 6),
+            ]
+        )
+        path, dist = nx.floyd_warshall_predecessor_and_distance(XG)
+        assert dist["s"]["v"] == 9
+        assert path["s"]["v"] == "u"
+        assert dist == {
+            "y": {"y": 0, "x": 12, "s": 7, "u": 15, "v": 6},
+            "x": {"y": 2, "x": 0, "s": 9, "u": 3, "v": 4},
+            "s": {"y": 7, "x": 5, "s": 0, "u": 8, "v": 9},
+            "u": {"y": 2, "x": 2, "s": 9, "u": 0, "v": 1},
+            "v": {"y": 1, "x": 13, "s": 8, "u": 16, "v": 0},
+        }
+
+        GG = XG.to_undirected()
+        # make sure we get lower weight
+        # to_undirected might choose either edge with weight 2 or weight 3
+        GG["u"]["x"]["weight"] = 2
+        path, dist = nx.floyd_warshall_predecessor_and_distance(GG)
+        assert dist["s"]["v"] == 8
+        # skip this test, could be alternate path s-u-v
+        #        assert_equal(path['s']['v'],'y')
+
+        G = nx.DiGraph()  # no weights
+        G.add_edges_from(
+            [
+                ("s", "u"),
+                ("s", "x"),
+                ("u", "v"),
+                ("u", "x"),
+                ("v", "y"),
+                ("x", "u"),
+                ("x", "v"),
+                ("x", "y"),
+                ("y", "s"),
+                ("y", "v"),
+            ]
+        )
+        path, dist = nx.floyd_warshall_predecessor_and_distance(G)
+        assert dist["s"]["v"] == 2
+        # skip this test, could be alternate path s-u-v
+        # assert_equal(path['s']['v'],'x')
+
+        # alternate interface
+        dist = nx.floyd_warshall(G)
+        assert dist["s"]["v"] == 2
+
+        # floyd_warshall_predecessor_and_distance returns
+        # dicts-of-defautdicts
+        # make sure we don't get empty dictionary
+        XG = nx.DiGraph()
+        XG.add_weighted_edges_from(
+            [("v", "x", 5.0), ("y", "x", 5.0), ("v", "y", 6.0), ("x", "u", 2.0)]
+        )
+        path, dist = nx.floyd_warshall_predecessor_and_distance(XG)
+        inf = float("inf")
+        assert dist == {
+            "v": {"v": 0, "x": 5.0, "y": 6.0, "u": 7.0},
+            "x": {"x": 0, "u": 2.0, "v": inf, "y": inf},
+            "y": {"y": 0, "x": 5.0, "v": inf, "u": 7.0},
+            "u": {"u": 0, "v": inf, "x": inf, "y": inf},
+        }
+        assert path == {
+            "v": {"x": "v", "y": "v", "u": "x"},
+            "x": {"u": "x"},
+            "y": {"x": "y", "u": "x"},
+        }
+
+    def test_reconstruct_path(self):
+        with pytest.raises(KeyError):
+            XG = nx.DiGraph()
+            XG.add_weighted_edges_from(
+                [
+                    ("s", "u", 10),
+                    ("s", "x", 5),
+                    ("u", "v", 1),
+                    ("u", "x", 2),
+                    ("v", "y", 1),
+                    ("x", "u", 3),
+                    ("x", "v", 5),
+                    ("x", "y", 2),
+                    ("y", "s", 7),
+                    ("y", "v", 6),
+                ]
+            )
+            predecessors, _ = nx.floyd_warshall_predecessor_and_distance(XG)
+
+            path = nx.reconstruct_path("s", "v", predecessors)
+            assert path == ["s", "x", "u", "v"]
+
+            path = nx.reconstruct_path("s", "s", predecessors)
+            assert path == []
+
+            # this part raises the keyError
+            nx.reconstruct_path("1", "2", predecessors)
+
+    def test_cycle(self):
+        path, dist = nx.floyd_warshall_predecessor_and_distance(nx.cycle_graph(7))
+        assert dist[0][3] == 3
+        assert path[0][3] == 2
+        assert dist[0][4] == 3
+
+    def test_weighted(self):
+        XG3 = nx.Graph()
+        XG3.add_weighted_edges_from(
+            [[0, 1, 2], [1, 2, 12], [2, 3, 1], [3, 4, 5], [4, 5, 1], [5, 0, 10]]
+        )
+        path, dist = nx.floyd_warshall_predecessor_and_distance(XG3)
+        assert dist[0][3] == 15
+        assert path[0][3] == 2
+
+    def test_weighted2(self):
+        XG4 = nx.Graph()
+        XG4.add_weighted_edges_from(
+            [
+                [0, 1, 2],
+                [1, 2, 2],
+                [2, 3, 1],
+                [3, 4, 1],
+                [4, 5, 1],
+                [5, 6, 1],
+                [6, 7, 1],
+                [7, 0, 1],
+            ]
+        )
+        path, dist = nx.floyd_warshall_predecessor_and_distance(XG4)
+        assert dist[0][2] == 4
+        assert path[0][2] == 1
+
+    def test_weight_parameter(self):
+        XG4 = nx.Graph()
+        XG4.add_edges_from(
+            [
+                (0, 1, {"heavy": 2}),
+                (1, 2, {"heavy": 2}),
+                (2, 3, {"heavy": 1}),
+                (3, 4, {"heavy": 1}),
+                (4, 5, {"heavy": 1}),
+                (5, 6, {"heavy": 1}),
+                (6, 7, {"heavy": 1}),
+                (7, 0, {"heavy": 1}),
+            ]
+        )
+        path, dist = nx.floyd_warshall_predecessor_and_distance(XG4, weight="heavy")
+        assert dist[0][2] == 4
+        assert path[0][2] == 1
+
+    def test_zero_distance(self):
+        XG = nx.DiGraph()
+        XG.add_weighted_edges_from(
+            [
+                ("s", "u", 10),
+                ("s", "x", 5),
+                ("u", "v", 1),
+                ("u", "x", 2),
+                ("v", "y", 1),
+                ("x", "u", 3),
+                ("x", "v", 5),
+                ("x", "y", 2),
+                ("y", "s", 7),
+                ("y", "v", 6),
+            ]
+        )
+        path, dist = nx.floyd_warshall_predecessor_and_distance(XG)
+
+        for u in XG:
+            assert dist[u][u] == 0
+
+        GG = XG.to_undirected()
+        # make sure we get lower weight
+        # to_undirected might choose either edge with weight 2 or weight 3
+        GG["u"]["x"]["weight"] = 2
+        path, dist = nx.floyd_warshall_predecessor_and_distance(GG)
+
+        for u in GG:
+            dist[u][u] = 0
+
+    def test_zero_weight(self):
+        G = nx.DiGraph()
+        edges = [(1, 2, -2), (2, 3, -4), (1, 5, 1), (5, 4, 0), (4, 3, -5), (2, 5, -7)]
+        G.add_weighted_edges_from(edges)
+        dist = nx.floyd_warshall(G)
+        assert dist[1][3] == -14
+
+        G = nx.MultiDiGraph()
+        edges.append((2, 5, -7))
+        G.add_weighted_edges_from(edges)
+        dist = nx.floyd_warshall(G)
+        assert dist[1][3] == -14
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py
new file mode 100644
index 00000000..1316e23e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py
@@ -0,0 +1,89 @@
+import pytest
+
+np = pytest.importorskip("numpy")
+
+
+import networkx as nx
+
+
+def test_cycle_numpy():
+    dist = nx.floyd_warshall_numpy(nx.cycle_graph(7))
+    assert dist[0, 3] == 3
+    assert dist[0, 4] == 3
+
+
+def test_weighted_numpy_three_edges():
+    XG3 = nx.Graph()
+    XG3.add_weighted_edges_from(
+        [[0, 1, 2], [1, 2, 12], [2, 3, 1], [3, 4, 5], [4, 5, 1], [5, 0, 10]]
+    )
+    dist = nx.floyd_warshall_numpy(XG3)
+    assert dist[0, 3] == 15
+
+
+def test_weighted_numpy_two_edges():
+    XG4 = nx.Graph()
+    XG4.add_weighted_edges_from(
+        [
+            [0, 1, 2],
+            [1, 2, 2],
+            [2, 3, 1],
+            [3, 4, 1],
+            [4, 5, 1],
+            [5, 6, 1],
+            [6, 7, 1],
+            [7, 0, 1],
+        ]
+    )
+    dist = nx.floyd_warshall_numpy(XG4)
+    assert dist[0, 2] == 4
+
+
+def test_weight_parameter_numpy():
+    XG4 = nx.Graph()
+    XG4.add_edges_from(
+        [
+            (0, 1, {"heavy": 2}),
+            (1, 2, {"heavy": 2}),
+            (2, 3, {"heavy": 1}),
+            (3, 4, {"heavy": 1}),
+            (4, 5, {"heavy": 1}),
+            (5, 6, {"heavy": 1}),
+            (6, 7, {"heavy": 1}),
+            (7, 0, {"heavy": 1}),
+        ]
+    )
+    dist = nx.floyd_warshall_numpy(XG4, weight="heavy")
+    assert dist[0, 2] == 4
+
+
+def test_directed_cycle_numpy():
+    G = nx.DiGraph()
+    nx.add_cycle(G, [0, 1, 2, 3])
+    pred, dist = nx.floyd_warshall_predecessor_and_distance(G)
+    D = nx.utils.dict_to_numpy_array(dist)
+    np.testing.assert_equal(nx.floyd_warshall_numpy(G), D)
+
+
+def test_zero_weight():
+    G = nx.DiGraph()
+    edges = [(1, 2, -2), (2, 3, -4), (1, 5, 1), (5, 4, 0), (4, 3, -5), (2, 5, -7)]
+    G.add_weighted_edges_from(edges)
+    dist = nx.floyd_warshall_numpy(G)
+    assert int(np.min(dist)) == -14
+
+    G = nx.MultiDiGraph()
+    edges.append((2, 5, -7))
+    G.add_weighted_edges_from(edges)
+    dist = nx.floyd_warshall_numpy(G)
+    assert int(np.min(dist)) == -14
+
+
+def test_nodelist():
+    G = nx.path_graph(7)
+    dist = nx.floyd_warshall_numpy(G, nodelist=[3, 5, 4, 6, 2, 1, 0])
+    assert dist[0, 3] == 3
+    assert dist[0, 1] == 2
+    assert dist[6, 2] == 4
+    pytest.raises(nx.NetworkXError, nx.floyd_warshall_numpy, G, [1, 3])
+    pytest.raises(nx.NetworkXError, nx.floyd_warshall_numpy, G, list(range(9)))
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_generic.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_generic.py
new file mode 100644
index 00000000..e30de517
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_generic.py
@@ -0,0 +1,450 @@
+import pytest
+
+import networkx as nx
+
+
+def validate_grid_path(r, c, s, t, p):
+    assert isinstance(p, list)
+    assert p[0] == s
+    assert p[-1] == t
+    s = ((s - 1) // c, (s - 1) % c)
+    t = ((t - 1) // c, (t - 1) % c)
+    assert len(p) == abs(t[0] - s[0]) + abs(t[1] - s[1]) + 1
+    p = [((u - 1) // c, (u - 1) % c) for u in p]
+    for u in p:
+        assert 0 <= u[0] < r
+        assert 0 <= u[1] < c
+    for u, v in zip(p[:-1], p[1:]):
+        assert (abs(v[0] - u[0]), abs(v[1] - u[1])) in [(0, 1), (1, 0)]
+
+
+class TestGenericPath:
+    @classmethod
+    def setup_class(cls):
+        from networkx import convert_node_labels_to_integers as cnlti
+
+        cls.grid = cnlti(nx.grid_2d_graph(4, 4), first_label=1, ordering="sorted")
+        cls.cycle = nx.cycle_graph(7)
+        cls.directed_cycle = nx.cycle_graph(7, create_using=nx.DiGraph())
+        cls.neg_weights = nx.DiGraph()
+        cls.neg_weights.add_edge(0, 1, weight=1)
+        cls.neg_weights.add_edge(0, 2, weight=3)
+        cls.neg_weights.add_edge(1, 3, weight=1)
+        cls.neg_weights.add_edge(2, 3, weight=-2)
+
+    def test_shortest_path(self):
+        assert nx.shortest_path(self.cycle, 0, 3) == [0, 1, 2, 3]
+        assert nx.shortest_path(self.cycle, 0, 4) == [0, 6, 5, 4]
+        validate_grid_path(4, 4, 1, 12, nx.shortest_path(self.grid, 1, 12))
+        assert nx.shortest_path(self.directed_cycle, 0, 3) == [0, 1, 2, 3]
+        # now with weights
+        assert nx.shortest_path(self.cycle, 0, 3, weight="weight") == [0, 1, 2, 3]
+        assert nx.shortest_path(self.cycle, 0, 4, weight="weight") == [0, 6, 5, 4]
+        validate_grid_path(
+            4, 4, 1, 12, nx.shortest_path(self.grid, 1, 12, weight="weight")
+        )
+        assert nx.shortest_path(self.directed_cycle, 0, 3, weight="weight") == [
+            0,
+            1,
+            2,
+            3,
+        ]
+        # weights and method specified
+        assert nx.shortest_path(
+            self.directed_cycle, 0, 3, weight="weight", method="dijkstra"
+        ) == [0, 1, 2, 3]
+        assert nx.shortest_path(
+            self.directed_cycle, 0, 3, weight="weight", method="bellman-ford"
+        ) == [0, 1, 2, 3]
+        # when Dijkstra's will probably (depending on precise implementation)
+        # incorrectly return [0, 1, 3] instead
+        assert nx.shortest_path(
+            self.neg_weights, 0, 3, weight="weight", method="bellman-ford"
+        ) == [0, 2, 3]
+        # confirm bad method rejection
+        pytest.raises(ValueError, nx.shortest_path, self.cycle, method="SPAM")
+        # confirm absent source rejection
+        pytest.raises(nx.NodeNotFound, nx.shortest_path, self.cycle, 8)
+
+    def test_shortest_path_target(self):
+        answer = {0: [0, 1], 1: [1], 2: [2, 1]}
+        sp = nx.shortest_path(nx.path_graph(3), target=1)
+        assert sp == answer
+        # with weights
+        sp = nx.shortest_path(nx.path_graph(3), target=1, weight="weight")
+        assert sp == answer
+        # weights and method specified
+        sp = nx.shortest_path(
+            nx.path_graph(3), target=1, weight="weight", method="dijkstra"
+        )
+        assert sp == answer
+        sp = nx.shortest_path(
+            nx.path_graph(3), target=1, weight="weight", method="bellman-ford"
+        )
+        assert sp == answer
+
+    def test_shortest_path_length(self):
+        assert nx.shortest_path_length(self.cycle, 0, 3) == 3
+        assert nx.shortest_path_length(self.grid, 1, 12) == 5
+        assert nx.shortest_path_length(self.directed_cycle, 0, 4) == 4
+        # now with weights
+        assert nx.shortest_path_length(self.cycle, 0, 3, weight="weight") == 3
+        assert nx.shortest_path_length(self.grid, 1, 12, weight="weight") == 5
+        assert nx.shortest_path_length(self.directed_cycle, 0, 4, weight="weight") == 4
+        # weights and method specified
+        assert (
+            nx.shortest_path_length(
+                self.cycle, 0, 3, weight="weight", method="dijkstra"
+            )
+            == 3
+        )
+        assert (
+            nx.shortest_path_length(
+                self.cycle, 0, 3, weight="weight", method="bellman-ford"
+            )
+            == 3
+        )
+        # confirm bad method rejection
+        pytest.raises(ValueError, nx.shortest_path_length, self.cycle, method="SPAM")
+        # confirm absent source rejection
+        pytest.raises(nx.NodeNotFound, nx.shortest_path_length, self.cycle, 8)
+
+    def test_shortest_path_length_target(self):
+        answer = {0: 1, 1: 0, 2: 1}
+        sp = dict(nx.shortest_path_length(nx.path_graph(3), target=1))
+        assert sp == answer
+        # with weights
+        sp = nx.shortest_path_length(nx.path_graph(3), target=1, weight="weight")
+        assert sp == answer
+        # weights and method specified
+        sp = nx.shortest_path_length(
+            nx.path_graph(3), target=1, weight="weight", method="dijkstra"
+        )
+        assert sp == answer
+        sp = nx.shortest_path_length(
+            nx.path_graph(3), target=1, weight="weight", method="bellman-ford"
+        )
+        assert sp == answer
+
+    def test_single_source_shortest_path(self):
+        p = nx.shortest_path(self.cycle, 0)
+        assert p[3] == [0, 1, 2, 3]
+        assert p == nx.single_source_shortest_path(self.cycle, 0)
+        p = nx.shortest_path(self.grid, 1)
+        validate_grid_path(4, 4, 1, 12, p[12])
+        # now with weights
+        p = nx.shortest_path(self.cycle, 0, weight="weight")
+        assert p[3] == [0, 1, 2, 3]
+        assert p == nx.single_source_dijkstra_path(self.cycle, 0)
+        p = nx.shortest_path(self.grid, 1, weight="weight")
+        validate_grid_path(4, 4, 1, 12, p[12])
+        # weights and method specified
+        p = nx.shortest_path(self.cycle, 0, method="dijkstra", weight="weight")
+        assert p[3] == [0, 1, 2, 3]
+        assert p == nx.single_source_shortest_path(self.cycle, 0)
+        p = nx.shortest_path(self.cycle, 0, method="bellman-ford", weight="weight")
+        assert p[3] == [0, 1, 2, 3]
+        assert p == nx.single_source_shortest_path(self.cycle, 0)
+
+    def test_single_source_shortest_path_length(self):
+        ans = dict(nx.shortest_path_length(self.cycle, 0))
+        assert ans == {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
+        assert ans == dict(nx.single_source_shortest_path_length(self.cycle, 0))
+        ans = dict(nx.shortest_path_length(self.grid, 1))
+        assert ans[16] == 6
+        # now with weights
+        ans = dict(nx.shortest_path_length(self.cycle, 0, weight="weight"))
+        assert ans == {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
+        assert ans == dict(nx.single_source_dijkstra_path_length(self.cycle, 0))
+        ans = dict(nx.shortest_path_length(self.grid, 1, weight="weight"))
+        assert ans[16] == 6
+        # weights and method specified
+        ans = dict(
+            nx.shortest_path_length(self.cycle, 0, weight="weight", method="dijkstra")
+        )
+        assert ans == {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
+        assert ans == dict(nx.single_source_dijkstra_path_length(self.cycle, 0))
+        ans = dict(
+            nx.shortest_path_length(
+                self.cycle, 0, weight="weight", method="bellman-ford"
+            )
+        )
+        assert ans == {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
+        assert ans == dict(nx.single_source_bellman_ford_path_length(self.cycle, 0))
+
+    def test_single_source_all_shortest_paths(self):
+        cycle_ans = {0: [[0]], 1: [[0, 1]], 2: [[0, 1, 2], [0, 3, 2]], 3: [[0, 3]]}
+        ans = dict(nx.single_source_all_shortest_paths(nx.cycle_graph(4), 0))
+        assert sorted(ans[2]) == cycle_ans[2]
+        ans = dict(nx.single_source_all_shortest_paths(self.grid, 1))
+        grid_ans = [
+            [1, 2, 3, 7, 11],
+            [1, 2, 6, 7, 11],
+            [1, 2, 6, 10, 11],
+            [1, 5, 6, 7, 11],
+            [1, 5, 6, 10, 11],
+            [1, 5, 9, 10, 11],
+        ]
+        assert sorted(ans[11]) == grid_ans
+        ans = dict(
+            nx.single_source_all_shortest_paths(nx.cycle_graph(4), 0, weight="weight")
+        )
+        assert sorted(ans[2]) == cycle_ans[2]
+        ans = dict(
+            nx.single_source_all_shortest_paths(
+                nx.cycle_graph(4), 0, method="bellman-ford", weight="weight"
+            )
+        )
+        assert sorted(ans[2]) == cycle_ans[2]
+        ans = dict(nx.single_source_all_shortest_paths(self.grid, 1, weight="weight"))
+        assert sorted(ans[11]) == grid_ans
+        ans = dict(
+            nx.single_source_all_shortest_paths(
+                self.grid, 1, method="bellman-ford", weight="weight"
+            )
+        )
+        assert sorted(ans[11]) == grid_ans
+        G = nx.cycle_graph(4)
+        G.add_node(4)
+        ans = dict(nx.single_source_all_shortest_paths(G, 0))
+        assert sorted(ans[2]) == [[0, 1, 2], [0, 3, 2]]
+        ans = dict(nx.single_source_all_shortest_paths(G, 4))
+        assert sorted(ans[4]) == [[4]]
+
+    def test_all_pairs_shortest_path(self):
+        # shortest_path w/o source and target will return a generator instead of
+        # a dict beginning in version 3.5. Only the first call needs changed here.
+        p = nx.shortest_path(self.cycle)
+        assert p[0][3] == [0, 1, 2, 3]
+        assert p == dict(nx.all_pairs_shortest_path(self.cycle))
+        p = dict(nx.shortest_path(self.grid))
+        validate_grid_path(4, 4, 1, 12, p[1][12])
+        # now with weights
+        p = dict(nx.shortest_path(self.cycle, weight="weight"))
+        assert p[0][3] == [0, 1, 2, 3]
+        assert p == dict(nx.all_pairs_dijkstra_path(self.cycle))
+        p = dict(nx.shortest_path(self.grid, weight="weight"))
+        validate_grid_path(4, 4, 1, 12, p[1][12])
+        # weights and method specified
+        p = dict(nx.shortest_path(self.cycle, weight="weight", method="dijkstra"))
+        assert p[0][3] == [0, 1, 2, 3]
+        assert p == dict(nx.all_pairs_dijkstra_path(self.cycle))
+        p = dict(nx.shortest_path(self.cycle, weight="weight", method="bellman-ford"))
+        assert p[0][3] == [0, 1, 2, 3]
+        assert p == dict(nx.all_pairs_bellman_ford_path(self.cycle))
+
+    def test_all_pairs_shortest_path_length(self):
+        ans = dict(nx.shortest_path_length(self.cycle))
+        assert ans[0] == {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
+        assert ans == dict(nx.all_pairs_shortest_path_length(self.cycle))
+        ans = dict(nx.shortest_path_length(self.grid))
+        assert ans[1][16] == 6
+        # now with weights
+        ans = dict(nx.shortest_path_length(self.cycle, weight="weight"))
+        assert ans[0] == {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
+        assert ans == dict(nx.all_pairs_dijkstra_path_length(self.cycle))
+        ans = dict(nx.shortest_path_length(self.grid, weight="weight"))
+        assert ans[1][16] == 6
+        # weights and method specified
+        ans = dict(
+            nx.shortest_path_length(self.cycle, weight="weight", method="dijkstra")
+        )
+        assert ans[0] == {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
+        assert ans == dict(nx.all_pairs_dijkstra_path_length(self.cycle))
+        ans = dict(
+            nx.shortest_path_length(self.cycle, weight="weight", method="bellman-ford")
+        )
+        assert ans[0] == {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
+        assert ans == dict(nx.all_pairs_bellman_ford_path_length(self.cycle))
+
+    def test_all_pairs_all_shortest_paths(self):
+        ans = dict(nx.all_pairs_all_shortest_paths(nx.cycle_graph(4)))
+        assert sorted(ans[1][3]) == [[1, 0, 3], [1, 2, 3]]
+        ans = dict(nx.all_pairs_all_shortest_paths(nx.cycle_graph(4)), weight="weight")
+        assert sorted(ans[1][3]) == [[1, 0, 3], [1, 2, 3]]
+        ans = dict(
+            nx.all_pairs_all_shortest_paths(nx.cycle_graph(4)),
+            method="bellman-ford",
+            weight="weight",
+        )
+        assert sorted(ans[1][3]) == [[1, 0, 3], [1, 2, 3]]
+        G = nx.cycle_graph(4)
+        G.add_node(4)
+        ans = dict(nx.all_pairs_all_shortest_paths(G))
+        assert sorted(ans[4][4]) == [[4]]
+
+    def test_has_path(self):
+        G = nx.Graph()
+        nx.add_path(G, range(3))
+        nx.add_path(G, range(3, 5))
+        assert nx.has_path(G, 0, 2)
+        assert not nx.has_path(G, 0, 4)
+
+    def test_has_path_singleton(self):
+        G = nx.empty_graph(1)
+        assert nx.has_path(G, 0, 0)
+
+    def test_all_shortest_paths(self):
+        G = nx.Graph()
+        nx.add_path(G, [0, 1, 2, 3])
+        nx.add_path(G, [0, 10, 20, 3])
+        assert [[0, 1, 2, 3], [0, 10, 20, 3]] == sorted(nx.all_shortest_paths(G, 0, 3))
+        # with weights
+        G = nx.Graph()
+        nx.add_path(G, [0, 1, 2, 3])
+        nx.add_path(G, [0, 10, 20, 3])
+        assert [[0, 1, 2, 3], [0, 10, 20, 3]] == sorted(
+            nx.all_shortest_paths(G, 0, 3, weight="weight")
+        )
+        # weights and method specified
+        G = nx.Graph()
+        nx.add_path(G, [0, 1, 2, 3])
+        nx.add_path(G, [0, 10, 20, 3])
+        assert [[0, 1, 2, 3], [0, 10, 20, 3]] == sorted(
+            nx.all_shortest_paths(G, 0, 3, weight="weight", method="dijkstra")
+        )
+        G = nx.Graph()
+        nx.add_path(G, [0, 1, 2, 3])
+        nx.add_path(G, [0, 10, 20, 3])
+        assert [[0, 1, 2, 3], [0, 10, 20, 3]] == sorted(
+            nx.all_shortest_paths(G, 0, 3, weight="weight", method="bellman-ford")
+        )
+
+    def test_all_shortest_paths_raise(self):
+        with pytest.raises(nx.NetworkXNoPath):
+            G = nx.path_graph(4)
+            G.add_node(4)
+            list(nx.all_shortest_paths(G, 0, 4))
+
+    def test_bad_method(self):
+        with pytest.raises(ValueError):
+            G = nx.path_graph(2)
+            list(nx.all_shortest_paths(G, 0, 1, weight="weight", method="SPAM"))
+
+    def test_single_source_all_shortest_paths_bad_method(self):
+        with pytest.raises(ValueError):
+            G = nx.path_graph(2)
+            dict(
+                nx.single_source_all_shortest_paths(
+                    G, 0, weight="weight", method="SPAM"
+                )
+            )
+
+    def test_all_shortest_paths_zero_weight_edge(self):
+        g = nx.Graph()
+        nx.add_path(g, [0, 1, 3])
+        nx.add_path(g, [0, 1, 2, 3])
+        g.edges[1, 2]["weight"] = 0
+        paths30d = list(
+            nx.all_shortest_paths(g, 3, 0, weight="weight", method="dijkstra")
+        )
+        paths03d = list(
+            nx.all_shortest_paths(g, 0, 3, weight="weight", method="dijkstra")
+        )
+        paths30b = list(
+            nx.all_shortest_paths(g, 3, 0, weight="weight", method="bellman-ford")
+        )
+        paths03b = list(
+            nx.all_shortest_paths(g, 0, 3, weight="weight", method="bellman-ford")
+        )
+        assert sorted(paths03d) == sorted(p[::-1] for p in paths30d)
+        assert sorted(paths03d) == sorted(p[::-1] for p in paths30b)
+        assert sorted(paths03b) == sorted(p[::-1] for p in paths30b)
+
+
+class TestAverageShortestPathLength:
+    def test_cycle_graph(self):
+        ans = nx.average_shortest_path_length(nx.cycle_graph(7))
+        assert ans == pytest.approx(2, abs=1e-7)
+
+    def test_path_graph(self):
+        ans = nx.average_shortest_path_length(nx.path_graph(5))
+        assert ans == pytest.approx(2, abs=1e-7)
+
+    def test_weighted(self):
+        G = nx.Graph()
+        nx.add_cycle(G, range(7), weight=2)
+        ans = nx.average_shortest_path_length(G, weight="weight")
+        assert ans == pytest.approx(4, abs=1e-7)
+        G = nx.Graph()
+        nx.add_path(G, range(5), weight=2)
+        ans = nx.average_shortest_path_length(G, weight="weight")
+        assert ans == pytest.approx(4, abs=1e-7)
+
+    def test_specified_methods(self):
+        G = nx.Graph()
+        nx.add_cycle(G, range(7), weight=2)
+        ans = nx.average_shortest_path_length(G, weight="weight", method="dijkstra")
+        assert ans == pytest.approx(4, abs=1e-7)
+        ans = nx.average_shortest_path_length(G, weight="weight", method="bellman-ford")
+        assert ans == pytest.approx(4, abs=1e-7)
+        ans = nx.average_shortest_path_length(
+            G, weight="weight", method="floyd-warshall"
+        )
+        assert ans == pytest.approx(4, abs=1e-7)
+
+        G = nx.Graph()
+        nx.add_path(G, range(5), weight=2)
+        ans = nx.average_shortest_path_length(G, weight="weight", method="dijkstra")
+        assert ans == pytest.approx(4, abs=1e-7)
+        ans = nx.average_shortest_path_length(G, weight="weight", method="bellman-ford")
+        assert ans == pytest.approx(4, abs=1e-7)
+        ans = nx.average_shortest_path_length(
+            G, weight="weight", method="floyd-warshall"
+        )
+        assert ans == pytest.approx(4, abs=1e-7)
+
+    def test_directed_not_strongly_connected(self):
+        G = nx.DiGraph([(0, 1)])
+        with pytest.raises(nx.NetworkXError, match="Graph is not strongly connected"):
+            nx.average_shortest_path_length(G)
+
+    def test_undirected_not_connected(self):
+        g = nx.Graph()
+        g.add_nodes_from(range(3))
+        g.add_edge(0, 1)
+        pytest.raises(nx.NetworkXError, nx.average_shortest_path_length, g)
+
+    def test_trivial_graph(self):
+        """Tests that the trivial graph has average path length zero,
+        since there is exactly one path of length zero in the trivial
+        graph.
+
+        For more information, see issue #1960.
+
+        """
+        G = nx.trivial_graph()
+        assert nx.average_shortest_path_length(G) == 0
+
+    def test_null_graph(self):
+        with pytest.raises(nx.NetworkXPointlessConcept):
+            nx.average_shortest_path_length(nx.null_graph())
+
+    def test_bad_method(self):
+        with pytest.raises(ValueError):
+            G = nx.path_graph(2)
+            nx.average_shortest_path_length(G, weight="weight", method="SPAM")
+
+
+class TestAverageShortestPathLengthNumpy:
+    @classmethod
+    def setup_class(cls):
+        global np
+        import pytest
+
+        np = pytest.importorskip("numpy")
+
+    def test_specified_methods_numpy(self):
+        G = nx.Graph()
+        nx.add_cycle(G, range(7), weight=2)
+        ans = nx.average_shortest_path_length(
+            G, weight="weight", method="floyd-warshall-numpy"
+        )
+        np.testing.assert_almost_equal(ans, 4)
+
+        G = nx.Graph()
+        nx.add_path(G, range(5), weight=2)
+        ans = nx.average_shortest_path_length(
+            G, weight="weight", method="floyd-warshall-numpy"
+        )
+        np.testing.assert_almost_equal(ans, 4)
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_unweighted.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_unweighted.py
new file mode 100644
index 00000000..f09c8b10
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_unweighted.py
@@ -0,0 +1,149 @@
+import pytest
+
+import networkx as nx
+
+
+def validate_grid_path(r, c, s, t, p):
+    assert isinstance(p, list)
+    assert p[0] == s
+    assert p[-1] == t
+    s = ((s - 1) // c, (s - 1) % c)
+    t = ((t - 1) // c, (t - 1) % c)
+    assert len(p) == abs(t[0] - s[0]) + abs(t[1] - s[1]) + 1
+    p = [((u - 1) // c, (u - 1) % c) for u in p]
+    for u in p:
+        assert 0 <= u[0] < r
+        assert 0 <= u[1] < c
+    for u, v in zip(p[:-1], p[1:]):
+        assert (abs(v[0] - u[0]), abs(v[1] - u[1])) in [(0, 1), (1, 0)]
+
+
+class TestUnweightedPath:
+    @classmethod
+    def setup_class(cls):
+        from networkx import convert_node_labels_to_integers as cnlti
+
+        cls.grid = cnlti(nx.grid_2d_graph(4, 4), first_label=1, ordering="sorted")
+        cls.cycle = nx.cycle_graph(7)
+        cls.directed_cycle = nx.cycle_graph(7, create_using=nx.DiGraph())
+
+    def test_bidirectional_shortest_path(self):
+        assert nx.bidirectional_shortest_path(self.cycle, 0, 3) == [0, 1, 2, 3]
+        assert nx.bidirectional_shortest_path(self.cycle, 0, 4) == [0, 6, 5, 4]
+        validate_grid_path(
+            4, 4, 1, 12, nx.bidirectional_shortest_path(self.grid, 1, 12)
+        )
+        assert nx.bidirectional_shortest_path(self.directed_cycle, 0, 3) == [0, 1, 2, 3]
+        # test source = target
+        assert nx.bidirectional_shortest_path(self.cycle, 3, 3) == [3]
+
+    @pytest.mark.parametrize(
+        ("src", "tgt"),
+        (
+            (8, 3),  # source not in graph
+            (3, 8),  # target not in graph
+            (8, 10),  # neither source nor target in graph
+            (8, 8),  # src == tgt, neither in graph - tests order of input checks
+        ),
+    )
+    def test_bidirectional_shortest_path_src_tgt_not_in_graph(self, src, tgt):
+        with pytest.raises(
+            nx.NodeNotFound,
+            match=f"(Source {src}|Target {tgt}) is not in G",
+        ):
+            nx.bidirectional_shortest_path(self.cycle, src, tgt)
+
+    def test_shortest_path_length(self):
+        assert nx.shortest_path_length(self.cycle, 0, 3) == 3
+        assert nx.shortest_path_length(self.grid, 1, 12) == 5
+        assert nx.shortest_path_length(self.directed_cycle, 0, 4) == 4
+        # now with weights
+        assert nx.shortest_path_length(self.cycle, 0, 3, weight=True) == 3
+        assert nx.shortest_path_length(self.grid, 1, 12, weight=True) == 5
+        assert nx.shortest_path_length(self.directed_cycle, 0, 4, weight=True) == 4
+
+    def test_single_source_shortest_path(self):
+        p = nx.single_source_shortest_path(self.directed_cycle, 3)
+        assert p[0] == [3, 4, 5, 6, 0]
+        p = nx.single_source_shortest_path(self.cycle, 0)
+        assert p[3] == [0, 1, 2, 3]
+        p = nx.single_source_shortest_path(self.cycle, 0, cutoff=0)
+        assert p == {0: [0]}
+
+    def test_single_source_shortest_path_length(self):
+        pl = nx.single_source_shortest_path_length
+        lengths = {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
+        assert dict(pl(self.cycle, 0)) == lengths
+        lengths = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6}
+        assert dict(pl(self.directed_cycle, 0)) == lengths
+
+    def test_single_target_shortest_path(self):
+        p = nx.single_target_shortest_path(self.directed_cycle, 0)
+        assert p[3] == [3, 4, 5, 6, 0]
+        p = nx.single_target_shortest_path(self.cycle, 0)
+        assert p[3] == [3, 2, 1, 0]
+        p = nx.single_target_shortest_path(self.cycle, 0, cutoff=0)
+        assert p == {0: [0]}
+        # test missing targets
+        target = 8
+        with pytest.raises(nx.NodeNotFound, match=f"Target {target} not in G"):
+            nx.single_target_shortest_path(self.cycle, target)
+
+    def test_single_target_shortest_path_length(self):
+        pl = nx.single_target_shortest_path_length
+        lengths = {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
+        assert dict(pl(self.cycle, 0)) == lengths
+        lengths = {0: 0, 1: 6, 2: 5, 3: 4, 4: 3, 5: 2, 6: 1}
+        assert dict(pl(self.directed_cycle, 0)) == lengths
+        # test missing targets
+        target = 8
+        with pytest.raises(nx.NodeNotFound, match=f"Target {target} is not in G"):
+            nx.single_target_shortest_path_length(self.cycle, target)
+
+    def test_all_pairs_shortest_path(self):
+        p = dict(nx.all_pairs_shortest_path(self.cycle))
+        assert p[0][3] == [0, 1, 2, 3]
+        p = dict(nx.all_pairs_shortest_path(self.grid))
+        validate_grid_path(4, 4, 1, 12, p[1][12])
+
+    def test_all_pairs_shortest_path_length(self):
+        l = dict(nx.all_pairs_shortest_path_length(self.cycle))
+        assert l[0] == {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
+        l = dict(nx.all_pairs_shortest_path_length(self.grid))
+        assert l[1][16] == 6
+
+    def test_predecessor_path(self):
+        G = nx.path_graph(4)
+        assert nx.predecessor(G, 0) == {0: [], 1: [0], 2: [1], 3: [2]}
+        assert nx.predecessor(G, 0, 3) == [2]
+
+    def test_predecessor_cycle(self):
+        G = nx.cycle_graph(4)
+        pred = nx.predecessor(G, 0)
+        assert pred[0] == []
+        assert pred[1] == [0]
+        assert pred[2] in [[1, 3], [3, 1]]
+        assert pred[3] == [0]
+
+    def test_predecessor_cutoff(self):
+        G = nx.path_graph(4)
+        p = nx.predecessor(G, 0, 3)
+        assert 4 not in p
+
+    def test_predecessor_target(self):
+        G = nx.path_graph(4)
+        p = nx.predecessor(G, 0, 3)
+        assert p == [2]
+        p = nx.predecessor(G, 0, 3, cutoff=2)
+        assert p == []
+        p, s = nx.predecessor(G, 0, 3, return_seen=True)
+        assert p == [2]
+        assert s == 3
+        p, s = nx.predecessor(G, 0, 3, cutoff=2, return_seen=True)
+        assert p == []
+        assert s == -1
+
+    def test_predecessor_missing_source(self):
+        source = 8
+        with pytest.raises(nx.NodeNotFound, match=f"Source {source} not in G"):
+            nx.predecessor(self.cycle, source)
diff --git a/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_weighted.py b/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_weighted.py
new file mode 100644
index 00000000..dc88572d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/algorithms/shortest_paths/tests/test_weighted.py
@@ -0,0 +1,972 @@
+import pytest
+
+import networkx as nx
+from networkx.utils import pairwise
+
+
+def validate_path(G, s, t, soln_len, path, weight="weight"):
+    assert path[0] == s
+    assert path[-1] == t
+
+    if callable(weight):
+        weight_f = weight
+    else:
+        if G.is_multigraph():
+
+            def weight_f(u, v, d):
+                return min(e.get(weight, 1) for e in d.values())
+
+        else:
+
+            def weight_f(u, v, d):
+                return d.get(weight, 1)
+
+    computed = sum(weight_f(u, v, G[u][v]) for u, v in pairwise(path))
+    assert soln_len == computed
+
+
+def validate_length_path(G, s, t, soln_len, length, path, weight="weight"):
+    assert soln_len == length
+    validate_path(G, s, t, length, path, weight=weight)
+
+
+class WeightedTestBase:
+    """Base class for test classes that test functions for computing
+    shortest paths in weighted graphs.
+
+    """
+
+    def setup_method(self):
+        """Creates some graphs for use in the unit tests."""
+        cnlti = nx.convert_node_labels_to_integers
+        self.grid = cnlti(nx.grid_2d_graph(4, 4), first_label=1, ordering="sorted")
+        self.cycle = nx.cycle_graph(7)
+        self.directed_cycle = nx.cycle_graph(7, create_using=nx.DiGraph())
+        self.XG = nx.DiGraph()
+        self.XG.add_weighted_edges_from(
+            [
+                ("s", "u", 10),
+                ("s", "x", 5),
+                ("u", "v", 1),
+                ("u", "x", 2),
+                ("v", "y", 1),
+                ("x", "u", 3),
+                ("x", "v", 5),
+                ("x", "y", 2),
+                ("y", "s", 7),
+                ("y", "v", 6),
+            ]
+        )
+        self.MXG = nx.MultiDiGraph(self.XG)
+        self.MXG.add_edge("s", "u", weight=15)
+        self.XG2 = nx.DiGraph()
+        self.XG2.add_weighted_edges_from(
+            [
+                [1, 4, 1],
+                [4, 5, 1],
+                [5, 6, 1],
+                [6, 3, 1],
+                [1, 3, 50],
+                [1, 2, 100],
+                [2, 3, 100],
+            ]
+        )
+
+        self.XG3 = nx.Graph()
+        self.XG3.add_weighted_edges_from(
+            [[0, 1, 2], [1, 2, 12], [2, 3, 1], [3, 4, 5], [4, 5, 1], [5, 0, 10]]
+        )
+
+        self.XG4 = nx.Graph()
+        self.XG4.add_weighted_edges_from(
+            [
+                [0, 1, 2],
+                [1, 2, 2],
+                [2, 3, 1],
+                [3, 4, 1],
+                [4, 5, 1],
+                [5, 6, 1],
+                [6, 7, 1],
+                [7, 0, 1],
+            ]
+        )
+        self.MXG4 = nx.MultiGraph(self.XG4)
+        self.MXG4.add_edge(0, 1, weight=3)
+        self.G = nx.DiGraph()  # no weights
+        self.G.add_edges_from(
+            [
+                ("s", "u"),
+                ("s", "x"),
+                ("u", "v"),
+                ("u", "x"),
+                ("v", "y"),
+                ("x", "u"),
+                ("x", "v"),
+                ("x", "y"),
+                ("y", "s"),
+                ("y", "v"),
+            ]
+        )
+
+
+class TestWeightedPath(WeightedTestBase):
+    def test_dijkstra(self):
+        (D, P) = nx.single_source_dijkstra(self.XG, "s")
+        validate_path(self.XG, "s", "v", 9, P["v"])
+        assert D["v"] == 9
+
+        validate_path(
+            self.XG, "s", "v", 9, nx.single_source_dijkstra_path(self.XG, "s")["v"]
+        )
+        assert dict(nx.single_source_dijkstra_path_length(self.XG, "s"))["v"] == 9
+
+        validate_path(
+            self.XG, "s", "v", 9, nx.single_source_dijkstra(self.XG, "s")[1]["v"]
+        )
+        validate_path(
+            self.MXG, "s", "v", 9, nx.single_source_dijkstra_path(self.MXG, "s")["v"]
+        )
+
+        GG = self.XG.to_undirected()
+        # make sure we get lower weight
+        # to_undirected might choose either edge with weight 2 or weight 3
+        GG["u"]["x"]["weight"] = 2
+        (D, P) = nx.single_source_dijkstra(GG, "s")
+        validate_path(GG, "s", "v", 8, P["v"])
+        assert D["v"] == 8  # uses lower weight of 2 on u<->x edge
+        validate_path(GG, "s", "v", 8, nx.dijkstra_path(GG, "s", "v"))
+        assert nx.dijkstra_path_length(GG, "s", "v") == 8
+
+        validate_path(self.XG2, 1, 3, 4, nx.dijkstra_path(self.XG2, 1, 3))
+        validate_path(self.XG3, 0, 3, 15, nx.dijkstra_path(self.XG3, 0, 3))
+        assert nx.dijkstra_path_length(self.XG3, 0, 3) == 15
+        validate_path(self.XG4, 0, 2, 4, nx.dijkstra_path(self.XG4, 0, 2))
+        assert nx.dijkstra_path_length(self.XG4, 0, 2) == 4
+        validate_path(self.MXG4, 0, 2, 4, nx.dijkstra_path(self.MXG4, 0, 2))
+        validate_path(
+            self.G, "s", "v", 2, nx.single_source_dijkstra(self.G, "s", "v")[1]
+        )
+        validate_path(
+            self.G, "s", "v", 2, nx.single_source_dijkstra(self.G, "s")[1]["v"]
+        )
+
+        validate_path(self.G, "s", "v", 2, nx.dijkstra_path(self.G, "s", "v"))
+        assert nx.dijkstra_path_length(self.G, "s", "v") == 2
+
+        # NetworkXError: node s not reachable from moon
+        pytest.raises(nx.NetworkXNoPath, nx.dijkstra_path, self.G, "s", "moon")
+        pytest.raises(nx.NetworkXNoPath, nx.dijkstra_path_length, self.G, "s", "moon")
+
+        validate_path(self.cycle, 0, 3, 3, nx.dijkstra_path(self.cycle, 0, 3))
+        validate_path(self.cycle, 0, 4, 3, nx.dijkstra_path(self.cycle, 0, 4))
+
+        assert nx.single_source_dijkstra(self.cycle, 0, 0) == (0, [0])
+
+    def test_bidirectional_dijkstra(self):
+        validate_length_path(
+            self.XG, "s", "v", 9, *nx.bidirectional_dijkstra(self.XG, "s", "v")
+        )
+        validate_length_path(
+            self.G, "s", "v", 2, *nx.bidirectional_dijkstra(self.G, "s", "v")
+        )
+        validate_length_path(
+            self.cycle, 0, 3, 3, *nx.bidirectional_dijkstra(self.cycle, 0, 3)
+        )
+        validate_length_path(
+            self.cycle, 0, 4, 3, *nx.bidirectional_dijkstra(self.cycle, 0, 4)
+        )
+        validate_length_path(
+            self.XG3, 0, 3, 15, *nx.bidirectional_dijkstra(self.XG3, 0, 3)
+        )
+        validate_length_path(
+            self.XG4, 0, 2, 4, *nx.bidirectional_dijkstra(self.XG4, 0, 2)
+        )
+
+        # need more tests here
+        P = nx.single_source_dijkstra_path(self.XG, "s")["v"]
+        validate_path(
+            self.XG,
+            "s",
+            "v",
+            sum(self.XG[u][v]["weight"] for u, v in zip(P[:-1], P[1:])),
+            nx.dijkstra_path(self.XG, "s", "v"),
+        )
+
+        # check absent source
+        G = nx.path_graph(2)
+        pytest.raises(nx.NodeNotFound, nx.bidirectional_dijkstra, G, 3, 0)
+
+    def test_weight_functions(self):
+        def heuristic(*z):
+            return sum(val**2 for val in z)
+
+        def getpath(pred, v, s):
+            return [v] if v == s else getpath(pred, pred[v], s) + [v]
+
+        def goldberg_radzik(g, s, t, weight="weight"):
+            pred, dist = nx.goldberg_radzik(g, s, weight=weight)
+            dist = dist[t]
+            return dist, getpath(pred, t, s)
+
+        def astar(g, s, t, weight="weight"):
+            path = nx.astar_path(g, s, t, heuristic, weight=weight)
+            dist = nx.astar_path_length(g, s, t, heuristic, weight=weight)
+            return dist, path
+
+        def vlp(G, s, t, l, F, w):
+            res = F(G, s, t, weight=w)
+            validate_length_path(G, s, t, l, *res, weight=w)
+
+        G = self.cycle
+        s = 6
+        t = 4
+        path = [6] + list(range(t + 1))
+
+        def weight(u, v, _):
+            return 1 + v**2
+
+        length = sum(weight(u, v, None) for u, v in pairwise(path))
+        vlp(G, s, t, length, nx.bidirectional_dijkstra, weight)
+        vlp(G, s, t, length, nx.single_source_dijkstra, weight)
+        vlp(G, s, t, length, nx.single_source_bellman_ford, weight)
+        vlp(G, s, t, length, goldberg_radzik, weight)
+        vlp(G, s, t, length, astar, weight)
+
+        def weight(u, v, _):
+            return 2 ** (u * v)
+
+        length = sum(weight(u, v, None) for u, v in pairwise(path))
+        vlp(G, s, t, length, nx.bidirectional_dijkstra, weight)
+        vlp(G, s, t, length, nx.single_source_dijkstra, weight)
+        vlp(G, s, t, length, nx.single_source_bellman_ford, weight)
+        vlp(G, s, t, length, goldberg_radzik, weight)
+        vlp(G, s, t, length, astar, weight)
+
+    def test_bidirectional_dijkstra_no_path(self):
+        with pytest.raises(nx.NetworkXNoPath):
+            G = nx.Graph()
+            nx.add_path(G, [1, 2, 3])
+            nx.add_path(G, [4, 5, 6])
+            path = nx.bidirectional_dijkstra(G, 1, 6)
+
+    @pytest.mark.parametrize(
+        "fn",
+        (
+            nx.dijkstra_path,
+            nx.dijkstra_path_length,
+            nx.single_source_dijkstra_path,
+            nx.single_source_dijkstra_path_length,
+            nx.single_source_dijkstra,
+            nx.dijkstra_predecessor_and_distance,
+        ),
+    )
+    def test_absent_source(self, fn):
+        G = nx.path_graph(2)
+        with pytest.raises(nx.NodeNotFound):
+            fn(G, 3, 0)
+        # Test when source == target, which is handled specially by some functions
+        with pytest.raises(nx.NodeNotFound):
+            fn(G, 3, 3)
+
+    def test_dijkstra_predecessor1(self):
+        G = nx.path_graph(4)
+        assert nx.dijkstra_predecessor_and_distance(G, 0) == (
+            {0: [], 1: [0], 2: [1], 3: [2]},
+            {0: 0, 1: 1, 2: 2, 3: 3},
+        )
+
+    def test_dijkstra_predecessor2(self):
+        # 4-cycle
+        G = nx.Graph([(0, 1), (1, 2), (2, 3), (3, 0)])
+        pred, dist = nx.dijkstra_predecessor_and_distance(G, (0))
+        assert pred[0] == []
+        assert pred[1] == [0]
+        assert pred[2] in [[1, 3], [3, 1]]
+        assert pred[3] == [0]
+        assert dist == {0: 0, 1: 1, 2: 2, 3: 1}
+
+    def test_dijkstra_predecessor3(self):
+        XG = nx.DiGraph()
+        XG.add_weighted_edges_from(
+            [
+                ("s", "u", 10),
+                ("s", "x", 5),
+                ("u", "v", 1),
+                ("u", "x", 2),
+                ("v", "y", 1),
+                ("x", "u", 3),
+                ("x", "v", 5),
+                ("x", "y", 2),
+                ("y", "s", 7),
+                ("y", "v", 6),
+            ]
+        )
+        (P, D) = nx.dijkstra_predecessor_and_distance(XG, "s")
+        assert P["v"] == ["u"]
+        assert D["v"] == 9
+        (P, D) = nx.dijkstra_predecessor_and_distance(XG, "s", cutoff=8)
+        assert "v" not in D
+
+    def test_single_source_dijkstra_path_length(self):
+        pl = nx.single_source_dijkstra_path_length
+        assert dict(pl(self.MXG4, 0))[2] == 4
+        spl = pl(self.MXG4, 0, cutoff=2)
+        assert 2 not in spl
+
+    def test_bidirectional_dijkstra_multigraph(self):
+        G = nx.MultiGraph()
+        G.add_edge("a", "b", weight=10)
+        G.add_edge("a", "b", weight=100)
+        dp = nx.bidirectional_dijkstra(G, "a", "b")
+        assert dp == (10, ["a", "b"])
+
+    def test_dijkstra_pred_distance_multigraph(self):
+        G = nx.MultiGraph()
+        G.add_edge("a", "b", key="short", foo=5, weight=100)
+        G.add_edge("a", "b", key="long", bar=1, weight=110)
+        p, d = nx.dijkstra_predecessor_and_distance(G, "a")
+        assert p == {"a": [], "b": ["a"]}
+        assert d == {"a": 0, "b": 100}
+
+    def test_negative_edge_cycle(self):
+        G = nx.cycle_graph(5, create_using=nx.DiGraph())
+        assert not nx.negative_edge_cycle(G)
+        G.add_edge(8, 9, weight=-7)
+        G.add_edge(9, 8, weight=3)
+        graph_size = len(G)
+        assert nx.negative_edge_cycle(G)
+        assert graph_size == len(G)
+        pytest.raises(ValueError, nx.single_source_dijkstra_path_length, G, 8)
+        pytest.raises(ValueError, nx.single_source_dijkstra, G, 8)
+        pytest.raises(ValueError, nx.dijkstra_predecessor_and_distance, G, 8)
+        G.add_edge(9, 10)
+        pytest.raises(ValueError, nx.bidirectional_dijkstra, G, 8, 10)
+        G = nx.MultiDiGraph()
+        G.add_edge(2, 2, weight=-1)
+        assert nx.negative_edge_cycle(G)
+
+    def test_negative_edge_cycle_empty(self):
+        G = nx.DiGraph()
+        assert not nx.negative_edge_cycle(G)
+
+    def test_negative_edge_cycle_custom_weight_key(self):
+        d = nx.DiGraph()
+        d.add_edge("a", "b", w=-2)
+        d.add_edge("b", "a", w=-1)
+        assert nx.negative_edge_cycle(d, weight="w")
+
+    def test_weight_function(self):
+        """Tests that a callable weight is interpreted as a weight
+        function instead of an edge attribute.
+
+        """
+        # Create a triangle in which the edge from node 0 to node 2 has
+        # a large weight and the other two edges have a small weight.
+        G = nx.complete_graph(3)
+        G.adj[0][2]["weight"] = 10
+        G.adj[0][1]["weight"] = 1
+        G.adj[1][2]["weight"] = 1
+
+        # The weight function will take the multiplicative inverse of
+        # the weights on the edges. This way, weights that were large
+        # before now become small and vice versa.
+
+        def weight(u, v, d):
+            return 1 / d["weight"]
+
+        # The shortest path from 0 to 2 using the actual weights on the
+        # edges should be [0, 1, 2].
+        distance, path = nx.single_source_dijkstra(G, 0, 2)
+        assert distance == 2
+        assert path == [0, 1, 2]
+        # However, with the above weight function, the shortest path
+        # should be [0, 2], since that has a very small weight.
+        distance, path = nx.single_source_dijkstra(G, 0, 2, weight=weight)
+        assert distance == 1 / 10
+        assert path == [0, 2]
+
+    def test_all_pairs_dijkstra_path(self):
+        cycle = nx.cycle_graph(7)
+        p = dict(nx.all_pairs_dijkstra_path(cycle))
+        assert p[0][3] == [0, 1, 2, 3]
+
+        cycle[1][2]["weight"] = 10
+        p = dict(nx.all_pairs_dijkstra_path(cycle))
+        assert p[0][3] == [0, 6, 5, 4, 3]
+
+    def test_all_pairs_dijkstra_path_length(self):
+        cycle = nx.cycle_graph(7)
+        pl = dict(nx.all_pairs_dijkstra_path_length(cycle))
+        assert pl[0] == {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
+
+        cycle[1][2]["weight"] = 10
+        pl = dict(nx.all_pairs_dijkstra_path_length(cycle))
+        assert pl[0] == {0: 0, 1: 1, 2: 5, 3: 4, 4: 3, 5: 2, 6: 1}
+
+    def test_all_pairs_dijkstra(self):
+        cycle = nx.cycle_graph(7)
+        out = dict(nx.all_pairs_dijkstra(cycle))
+        assert out[0][0] == {0: 0, 1: 1, 2: 2, 3: 3, 4: 3, 5: 2, 6: 1}
+        assert out[0][1][3] == [0, 1, 2, 3]
+
+        cycle[1][2]["weight"] = 10
+        out = dict(nx.all_pairs_dijkstra(cycle))
+        assert out[0][0] == {0: 0, 1: 1, 2: 5, 3: 4, 4: 3, 5: 2, 6: 1}
+        assert out[0][1][3] == [0, 6, 5, 4, 3]
+
+
+class TestDijkstraPathLength:
+    """Unit tests for the :func:`networkx.dijkstra_path_length`
+    function.
+
+    """
+
+    def test_weight_function(self):
+        """Tests for computing the length of the shortest path using
+        Dijkstra's algorithm with a user-defined weight function.
+
+        """
+        # Create a triangle in which the edge from node 0 to node 2 has
+        # a large weight and the other two edges have a small weight.
+        G = nx.complete_graph(3)
+        G.adj[0][2]["weight"] = 10
+        G.adj[0][1]["weight"] = 1
+        G.adj[1][2]["weight"] = 1
+
+        # The weight function will take the multiplicative inverse of
+        # the weights on the edges. This way, weights that were large
+        # before now become small and vice versa.
+
+        def weight(u, v, d):
+            return 1 / d["weight"]
+
+        # The shortest path from 0 to 2 using the actual weights on the
+        # edges should be [0, 1, 2]. However, with the above weight
+        # function, the shortest path should be [0, 2], since that has a
+        # very small weight.
+        length = nx.dijkstra_path_length(G, 0, 2, weight=weight)
+        assert length == 1 / 10
+
+
+class TestMultiSourceDijkstra:
+    """Unit tests for the multi-source dialect of Dijkstra's shortest
+    path algorithms.
+
+    """
+
+    def test_no_sources(self):
+        with pytest.raises(ValueError):
+            nx.multi_source_dijkstra(nx.Graph(), {})
+
+    def test_path_no_sources(self):
+        with pytest.raises(ValueError):
+            nx.multi_source_dijkstra_path(nx.Graph(), {})
+
+    def test_path_length_no_sources(self):
+        with pytest.raises(ValueError):
+            nx.multi_source_dijkstra_path_length(nx.Graph(), {})
+
+    @pytest.mark.parametrize(
+        "fn",
+        (
+            nx.multi_source_dijkstra_path,
+            nx.multi_source_dijkstra_path_length,
+            nx.multi_source_dijkstra,
+        ),
+    )
+    def test_absent_source(self, fn):
+        G = nx.path_graph(2)
+        with pytest.raises(nx.NodeNotFound):
+            fn(G, [3], 0)
+        with pytest.raises(nx.NodeNotFound):
+            fn(G, [3], 3)
+
+    def test_two_sources(self):
+        edges = [(0, 1, 1), (1, 2, 1), (2, 3, 10), (3, 4, 1)]
+        G = nx.Graph()
+        G.add_weighted_edges_from(edges)
+        sources = {0, 4}
+        distances, paths = nx.multi_source_dijkstra(G, sources)
+        expected_distances = {0: 0, 1: 1, 2: 2, 3: 1, 4: 0}
+        expected_paths = {0: [0], 1: [0, 1], 2: [0, 1, 2], 3: [4, 3], 4: [4]}
+        assert distances == expected_distances
+        assert paths == expected_paths
+
+    def test_simple_paths(self):
+        G = nx.path_graph(4)
+        lengths = nx.multi_source_dijkstra_path_length(G, [0])
+        assert lengths == {n: n for n in G}
+        paths = nx.multi_source_dijkstra_path(G, [0])
+        assert paths == {n: list(range(n + 1)) for n in G}
+
+
+class TestBellmanFordAndGoldbergRadzik(WeightedTestBase):
+    def test_single_node_graph(self):
+        G = nx.DiGraph()
+        G.add_node(0)
+        assert nx.single_source_bellman_ford_path(G, 0) == {0: [0]}
+        assert nx.single_source_bellman_ford_path_length(G, 0) == {0: 0}
+        assert nx.single_source_bellman_ford(G, 0) == ({0: 0}, {0: [0]})
+        assert nx.bellman_ford_predecessor_and_distance(G, 0) == ({0: []}, {0: 0})
+        assert nx.goldberg_radzik(G, 0) == ({0: None}, {0: 0})
+
+    def test_absent_source_bellman_ford(self):
+        # the check is in _bellman_ford; this provides regression testing
+        # against later changes to "client" Bellman-Ford functions
+        G = nx.path_graph(2)
+        for fn in (
+            nx.bellman_ford_predecessor_and_distance,
+            nx.bellman_ford_path,
+            nx.bellman_ford_path_length,
+            nx.single_source_bellman_ford_path,
+            nx.single_source_bellman_ford_path_length,
+            nx.single_source_bellman_ford,
+        ):
+            pytest.raises(nx.NodeNotFound, fn, G, 3, 0)
+            pytest.raises(nx.NodeNotFound, fn, G, 3, 3)
+
+    def test_absent_source_goldberg_radzik(self):
+        with pytest.raises(nx.NodeNotFound):
+            G = nx.path_graph(2)
+            nx.goldberg_radzik(G, 3, 0)
+
+    def test_negative_cycle_heuristic(self):
+        G = nx.DiGraph()
+        G.add_edge(0, 1, weight=-1)
+        G.add_edge(1, 2, weight=-1)
+        G.add_edge(2, 3, weight=-1)
+        G.add_edge(3, 0, weight=3)
+        assert not nx.negative_edge_cycle(G, heuristic=True)
+        G.add_edge(2, 0, weight=1.999)
+        assert nx.negative_edge_cycle(G, heuristic=True)
+        G.edges[2, 0]["weight"] = 2
+        assert not nx.negative_edge_cycle(G, heuristic=True)
+
+    def test_negative_cycle_consistency(self):
+        import random
+
+        unif = random.uniform
+        for random_seed in range(2):  # range(20):
+            random.seed(random_seed)
+            for density in [0.1, 0.9]:  # .3, .7, .9]:
+                for N in [1, 10, 20]:  # range(1, 60 - int(30 * density)):
+                    for max_cost in [1, 90]:  # [1, 10, 40, 90]:
+                        G = nx.binomial_graph(N, density, seed=4, directed=True)
+                        edges = ((u, v, unif(-1, max_cost)) for u, v in G.edges)
+                        G.add_weighted_edges_from(edges)
+
+                        no_heuristic = nx.negative_edge_cycle(G, heuristic=False)
+                        with_heuristic = nx.negative_edge_cycle(G, heuristic=True)
+                        assert no_heuristic == with_heuristic
+
+    def test_negative_cycle(self):
+        G = nx.cycle_graph(5, create_using=nx.DiGraph())
+        G.add_edge(1, 2, weight=-7)
+        for i in range(5):
+            pytest.raises(
+                nx.NetworkXUnbounded, nx.single_source_bellman_ford_path, G, i
+            )
+            pytest.raises(
+                nx.NetworkXUnbounded, nx.single_source_bellman_ford_path_length, G, i
+            )
+            pytest.raises(nx.NetworkXUnbounded, nx.single_source_bellman_ford, G, i)
+            pytest.raises(
+                nx.NetworkXUnbounded, nx.bellman_ford_predecessor_and_distance, G, i
+            )
+            pytest.raises(nx.NetworkXUnbounded, nx.goldberg_radzik, G, i)
+        G = nx.cycle_graph(5)  # undirected Graph
+        G.add_edge(1, 2, weight=-3)
+        for i in range(5):
+            pytest.raises(
+                nx.NetworkXUnbounded, nx.single_source_bellman_ford_path, G, i
+            )
+            pytest.raises(
+                nx.NetworkXUnbounded, nx.single_source_bellman_ford_path_length, G, i
+            )
+            pytest.raises(nx.NetworkXUnbounded, nx.single_source_bellman_ford, G, i)
+            pytest.raises(
+                nx.NetworkXUnbounded, nx.bellman_ford_predecessor_and_distance, G, i
+            )
+            pytest.raises(nx.NetworkXUnbounded, nx.goldberg_radzik, G, i)
+        G = nx.DiGraph([(1, 1, {"weight": -1})])
+        pytest.raises(nx.NetworkXUnbounded, nx.single_source_bellman_ford_path, G, 1)
+        pytest.raises(
+            nx.NetworkXUnbounded, nx.single_source_bellman_ford_path_length, G, 1
+        )
+        pytest.raises(nx.NetworkXUnbounded, nx.single_source_bellman_ford, G, 1)
+        pytest.raises(
+            nx.NetworkXUnbounded, nx.bellman_ford_predecessor_and_distance, G, 1
+        )
+        pytest.raises(nx.NetworkXUnbounded, nx.goldberg_radzik, G, 1)
+        G = nx.MultiDiGraph([(1, 1, {"weight": -1})])
+        pytest.raises(nx.NetworkXUnbounded, nx.single_source_bellman_ford_path, G, 1)
+        pytest.raises(
+            nx.NetworkXUnbounded, nx.single_source_bellman_ford_path_length, G, 1
+        )
+        pytest.raises(nx.NetworkXUnbounded, nx.single_source_bellman_ford, G, 1)
+        pytest.raises(
+            nx.NetworkXUnbounded, nx.bellman_ford_predecessor_and_distance, G, 1
+        )
+        pytest.raises(nx.NetworkXUnbounded, nx.goldberg_radzik, G, 1)
+
+    def test_zero_cycle(self):
+        G = nx.cycle_graph(5, create_using=nx.DiGraph())
+        G.add_edge(2, 3, weight=-4)
+        # check that zero cycle doesn't raise
+        nx.goldberg_radzik(G, 1)
+        nx.bellman_ford_predecessor_and_distance(G, 1)
+
+        G.add_edge(2, 3, weight=-4.0001)
+        # check that negative cycle does raise
+        pytest.raises(
+            nx.NetworkXUnbounded, nx.bellman_ford_predecessor_and_distance, G, 1
+        )
+        pytest.raises(nx.NetworkXUnbounded, nx.goldberg_radzik, G, 1)
+
+    def test_find_negative_cycle_longer_cycle(self):
+        G = nx.cycle_graph(5, create_using=nx.DiGraph())
+        nx.add_cycle(G, [3, 5, 6, 7, 8, 9])
+        G.add_edge(1, 2, weight=-30)
+        assert nx.find_negative_cycle(G, 1) == [0, 1, 2, 3, 4, 0]
+        assert nx.find_negative_cycle(G, 7) == [2, 3, 4, 0, 1, 2]
+
+    def test_find_negative_cycle_no_cycle(self):
+        G = nx.path_graph(5, create_using=nx.DiGraph())
+        pytest.raises(nx.NetworkXError, nx.find_negative_cycle, G, 3)
+
+    def test_find_negative_cycle_single_edge(self):
+        G = nx.Graph()
+        G.add_edge(0, 1, weight=-1)
+        assert nx.find_negative_cycle(G, 1) == [1, 0, 1]
+
+    def test_negative_weight(self):
+        G = nx.cycle_graph(5, create_using=nx.DiGraph())
+        G.add_edge(1, 2, weight=-3)
+        assert nx.single_source_bellman_ford_path(G, 0) == {
+            0: [0],
+            1: [0, 1],
+            2: [0, 1, 2],
+            3: [0, 1, 2, 3],
+            4: [0, 1, 2, 3, 4],
+        }
+        assert nx.single_source_bellman_ford_path_length(G, 0) == {
+            0: 0,
+            1: 1,
+            2: -2,
+            3: -1,
+            4: 0,
+        }
+        assert nx.single_source_bellman_ford(G, 0) == (
+            {0: 0, 1: 1, 2: -2, 3: -1, 4: 0},
+            {0: [0], 1: [0, 1], 2: [0, 1, 2], 3: [0, 1, 2, 3], 4: [0, 1, 2, 3, 4]},
+        )
+        assert nx.bellman_ford_predecessor_and_distance(G, 0) == (
+            {0: [], 1: [0], 2: [1], 3: [2], 4: [3]},
+            {0: 0, 1: 1, 2: -2, 3: -1, 4: 0},
+        )
+        assert nx.goldberg_radzik(G, 0) == (
+            {0: None, 1: 0, 2: 1, 3: 2, 4: 3},
+            {0: 0, 1: 1, 2: -2, 3: -1, 4: 0},
+        )
+
+    def test_not_connected(self):
+        G = nx.complete_graph(6)
+        G.add_edge(10, 11)
+        G.add_edge(10, 12)
+        assert nx.single_source_bellman_ford_path(G, 0) == {
+            0: [0],
+            1: [0, 1],
+            2: [0, 2],
+            3: [0, 3],
+            4: [0, 4],
+            5: [0, 5],
+        }
+        assert nx.single_source_bellman_ford_path_length(G, 0) == {
+            0: 0,
+            1: 1,
+            2: 1,
+            3: 1,
+            4: 1,
+            5: 1,
+        }
+        assert nx.single_source_bellman_ford(G, 0) == (
+            {0: 0, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1},
+            {0: [0], 1: [0, 1], 2: [0, 2], 3: [0, 3], 4: [0, 4], 5: [0, 5]},
+        )
+        assert nx.bellman_ford_predecessor_and_distance(G, 0) == (
+            {0: [], 1: [0], 2: [0], 3: [0], 4: [0], 5: [0]},
+            {0: 0, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1},
+        )
+        assert nx.goldberg_radzik(G, 0) == (
+            {0: None, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0},
+            {0: 0, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1},
+        )
+
+        # not connected, with a component not containing the source that
+        # contains a negative cycle.
+        G = nx.complete_graph(6)
+        G.add_edges_from(
+            [
+                ("A", "B", {"load": 3}),
+                ("B", "C", {"load": -10}),
+                ("C", "A", {"load": 2}),
+            ]
+        )
+        assert nx.single_source_bellman_ford_path(G, 0, weight="load") == {
+            0: [0],
+            1: [0, 1],
+            2: [0, 2],
+            3: [0, 3],
+            4: [0, 4],
+            5: [0, 5],
+        }
+        assert nx.single_source_bellman_ford_path_length(G, 0, weight="load") == {
+            0: 0,
+            1: 1,
+            2: 1,
+            3: 1,
+            4: 1,
+            5: 1,
+        }
+        assert nx.single_source_bellman_ford(G, 0, weight="load") == (
+            {0: 0, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1},
+            {0: [0], 1: [0, 1], 2: [0, 2], 3: [0, 3], 4: [0, 4], 5: [0, 5]},
+        )
+        assert nx.bellman_ford_predecessor_and_distance(G, 0, weight="load") == (
+            {0: [], 1: [0], 2: [0], 3: [0], 4: [0], 5: [0]},
+            {0: 0, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1},
+        )
+        assert nx.goldberg_radzik(G, 0, weight="load") == (
+            {0: None, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0},
+            {0: 0, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1},
+        )
+
+    def test_multigraph(self):
+        assert nx.bellman_ford_path(self.MXG, "s", "v") == ["s", "x", "u", "v"]
+        assert nx.bellman_ford_path_length(self.MXG, "s", "v") == 9
+        assert nx.single_source_bellman_ford_path(self.MXG, "s")["v"] == [
+            "s",
+            "x",
+            "u",
+            "v",
+        ]
+        assert nx.single_source_bellman_ford_path_length(self.MXG, "s")["v"] == 9
+        D, P = nx.single_source_bellman_ford(self.MXG, "s", target="v")
+        assert D == 9
+        assert P == ["s", "x", "u", "v"]
+        P, D = nx.bellman_ford_predecessor_and_distance(self.MXG, "s")
+        assert P["v"] == ["u"]
+        assert D["v"] == 9
+        P, D = nx.goldberg_radzik(self.MXG, "s")
+        assert P["v"] == "u"
+        assert D["v"] == 9
+        assert nx.bellman_ford_path(self.MXG4, 0, 2) == [0, 1, 2]
+        assert nx.bellman_ford_path_length(self.MXG4, 0, 2) == 4
+        assert nx.single_source_bellman_ford_path(self.MXG4, 0)[2] == [0, 1, 2]
+        assert nx.single_source_bellman_ford_path_length(self.MXG4, 0)[2] == 4
+        D, P = nx.single_source_bellman_ford(self.MXG4, 0, target=2)
+        assert D == 4
+        assert P == [0, 1, 2]
+        P, D = nx.bellman_ford_predecessor_and_distance(self.MXG4, 0)
+        assert P[2] == [1]
+        assert D[2] == 4
+        P, D = nx.goldberg_radzik(self.MXG4, 0)
+        assert P[2] == 1
+        assert D[2] == 4
+
+    def test_others(self):
+        assert nx.bellman_ford_path(self.XG, "s", "v") == ["s", "x", "u", "v"]
+        assert nx.bellman_ford_path_length(self.XG, "s", "v") == 9
+        assert nx.single_source_bellman_ford_path(self.XG, "s")["v"] == [
+            "s",
+            "x",
+            "u",
+            "v",
+        ]
+        assert nx.single_source_bellman_ford_path_length(self.XG, "s")["v"] == 9
+        D, P = nx.single_source_bellman_ford(self.XG, "s", target="v")
+        assert D == 9
+        assert P == ["s", "x", "u", "v"]
+        (P, D) = nx.bellman_ford_predecessor_and_distance(self.XG, "s")
+        assert P["v"] == ["u"]
+        assert D["v"] == 9
+        (P, D) = nx.goldberg_radzik(self.XG, "s")
+        assert P["v"] == "u"
+        assert D["v"] == 9
+
+    def test_path_graph(self):
+        G = nx.path_graph(4)
+        assert nx.single_source_bellman_ford_path(G, 0) == {
+            0: [0],
+            1: [0, 1],
+            2: [0, 1, 2],
+            3: [0, 1, 2, 3],
+        }
+        assert nx.single_source_bellman_ford_path_length(G, 0) == {
+            0: 0,
+            1: 1,
+            2: 2,
+            3: 3,
+        }
+        assert nx.single_source_bellman_ford(G, 0) == (
+            {0: 0, 1: 1, 2: 2, 3: 3},
+            {0: [0], 1: [0, 1], 2: [0, 1, 2], 3: [0, 1, 2, 3]},
+        )
+        assert nx.bellman_ford_predecessor_and_distance(G, 0) == (
+            {0: [], 1: [0], 2: [1], 3: [2]},
+            {0: 0, 1: 1, 2: 2, 3: 3},
+        )
+        assert nx.goldberg_radzik(G, 0) == (
+            {0: None, 1: 0, 2: 1, 3: 2},
+            {0: 0, 1: 1, 2: 2, 3: 3},
+        )
+        assert nx.single_source_bellman_ford_path(G, 3) == {
+            0: [3, 2, 1, 0],
+            1: [3, 2, 1],
+            2: [3, 2],
+            3: [3],
+        }
+        assert nx.single_source_bellman_ford_path_length(G, 3) == {
+            0: 3,
+            1: 2,
+            2: 1,
+            3: 0,
+        }
+        assert nx.single_source_bellman_ford(G, 3) == (
+            {0: 3, 1: 2, 2: 1, 3: 0},
+            {0: [3, 2, 1, 0], 1: [3, 2, 1], 2: [3, 2], 3: [3]},
+        )
+        assert nx.bellman_ford_predecessor_and_distance(G, 3) == (
+            {0: [1], 1: [2], 2: [3], 3: []},
+            {0: 3, 1: 2, 2: 1, 3: 0},
+        )
+        assert nx.goldberg_radzik(G, 3) == (
+            {0: 1, 1: 2, 2: 3, 3: None},
+            {0: 3, 1: 2, 2: 1, 3: 0},
+        )
+
+    def test_4_cycle(self):
+        # 4-cycle
+        G = nx.Graph([(0, 1), (1, 2), (2, 3), (3, 0)])
+        dist, path = nx.single_source_bellman_ford(G, 0)
+        assert dist == {0: 0, 1: 1, 2: 2, 3: 1}
+        assert path[0] == [0]
+        assert path[1] == [0, 1]
+        assert path[2] in [[0, 1, 2], [0, 3, 2]]
+        assert path[3] == [0, 3]
+
+        pred, dist = nx.bellman_ford_predecessor_and_distance(G, 0)
+        assert pred[0] == []
+        assert pred[1] == [0]
+        assert pred[2] in [[1, 3], [3, 1]]
+        assert pred[3] == [0]
+        assert dist == {0: 0, 1: 1, 2: 2, 3: 1}
+
+        pred, dist = nx.goldberg_radzik(G, 0)
+        assert pred[0] is None
+        assert pred[1] == 0
+        assert pred[2] in [1, 3]
+        assert pred[3] == 0
+        assert dist == {0: 0, 1: 1, 2: 2, 3: 1}
+
+    def test_negative_weight_bf_path(self):
+        G = nx.DiGraph()
+        G.add_nodes_from("abcd")
+        G.add_edge("a", "d", weight=0)
+        G.add_edge("a", "b", weight=1)
+        G.add_edge("b", "c", weight=-3)
+        G.add_edge("c", "d", weight=1)
+
+        assert nx.bellman_ford_path(G, "a", "d") == ["a", "b", "c", "d"]
+        assert nx.bellman_ford_path_length(G, "a", "d") == -1
+
+    def test_zero_cycle_smoke(self):
+        D = nx.DiGraph()
+        D.add_weighted_edges_from([(0, 1, 1), (1, 2, 1), (2, 3, 1), (3, 1, -2)])
+
+        nx.bellman_ford_path(D, 1, 3)
+        nx.dijkstra_path(D, 1, 3)
+        nx.bidirectional_dijkstra(D, 1, 3)
+        # FIXME nx.goldberg_radzik(D, 1)
+
+
+class TestJohnsonAlgorithm(WeightedTestBase):
+    def test_single_node_graph(self):
+        G = nx.DiGraph()
+        G.add_node(0)
+        assert nx.johnson(G) == {0: {0: [0]}}
+
+    def test_negative_cycle(self):
+        G = nx.DiGraph()
+        G.add_weighted_edges_from(
+            [
+                ("0", "3", 3),
+                ("0", "1", -5),
+                ("1", "0", -5),
+                ("0", "2", 2),
+                ("1", "2", 4),
+                ("2", "3", 1),
+            ]
+        )
+        pytest.raises(nx.NetworkXUnbounded, nx.johnson, G)
+        G = nx.Graph()
+        G.add_weighted_edges_from(
+            [
+                ("0", "3", 3),
+                ("0", "1", -5),
+                ("1", "0", -5),
+                ("0", "2", 2),
+                ("1", "2", 4),
+                ("2", "3", 1),
+            ]
+        )
+        pytest.raises(nx.NetworkXUnbounded, nx.johnson, G)
+
+    def test_negative_weights(self):
+        G = nx.DiGraph()
+        G.add_weighted_edges_from(
+            [("0", "3", 3), ("0", "1", -5), ("0", "2", 2), ("1", "2", 4), ("2", "3", 1)]
+        )
+        paths = nx.johnson(G)
+        assert paths == {
+            "1": {"1": ["1"], "3": ["1", "2", "3"], "2": ["1", "2"]},
+            "0": {
+                "1": ["0", "1"],
+                "0": ["0"],
+                "3": ["0", "1", "2", "3"],
+                "2": ["0", "1", "2"],
+            },
+            "3": {"3": ["3"]},
+            "2": {"3": ["2", "3"], "2": ["2"]},
+        }
+
+    def test_unweighted_graph(self):
+        G = nx.Graph()
+        G.add_edges_from([(1, 0), (2, 1)])
+        H = G.copy()
+        nx.set_edge_attributes(H, values=1, name="weight")
+        assert nx.johnson(G) == nx.johnson(H)
+
+    def test_partially_weighted_graph_with_negative_edges(self):
+        G = nx.DiGraph()
+        G.add_edges_from([(0, 1), (1, 2), (2, 0), (1, 0)])
+        G[1][0]["weight"] = -2
+        G[0][1]["weight"] = 3
+        G[1][2]["weight"] = -4
+
+        H = G.copy()
+        H[2][0]["weight"] = 1
+
+        I = G.copy()
+        I[2][0]["weight"] = 8
+
+        assert nx.johnson(G) == nx.johnson(H)
+        assert nx.johnson(G) != nx.johnson(I)
+
+    def test_graphs(self):
+        validate_path(self.XG, "s", "v", 9, nx.johnson(self.XG)["s"]["v"])
+        validate_path(self.MXG, "s", "v", 9, nx.johnson(self.MXG)["s"]["v"])
+        validate_path(self.XG2, 1, 3, 4, nx.johnson(self.XG2)[1][3])
+        validate_path(self.XG3, 0, 3, 15, nx.johnson(self.XG3)[0][3])
+        validate_path(self.XG4, 0, 2, 4, nx.johnson(self.XG4)[0][2])
+        validate_path(self.MXG4, 0, 2, 4, nx.johnson(self.MXG4)[0][2])