Repository: networkx/networkx

Issue:
 Inconsistency in Negative Cycle Detection Between Bellman-Ford and Goldberg-Radzik Algorithms
### Description:
When attempting to compute the shortest path length between two nodes in a graph using the Bellman-Ford and Goldberg-Radzik algorithms, inconsistent results regarding the presence of a negative cycle are obtained. Specifically, the Goldberg-Radzik algorithm suggests the presence of a negative cycle and fails to compute the path length, while the Bellman-Ford algorithm successfully computes the shortest path without detecting any negative cycles.

### Observed Behavior:

Shortest path length between 6 and 0 using Bellman-Ford: 11 \
Goldberg_Radzik: A negative cycle exists in the graph. \
Shortest path length between 6 and 0 using Goldberg_Radzik: None


### Expected Behavior:
The results from both algorithms should be consistent. Either both algorithms should detect a negative cycle and fail to compute the path, or both should successfully compute the shortest path without detecting any negative cycles.

### Steps to Reproduce:
1. Create a directed graph with the following nodes and edges:
   - Nodes: [0, 1, 2, 3, 4, 5, 6, 7]
   - Edges: (Provided in the code snippet below)
2. Attempt to compute the shortest path length between nodes `6` and `0` using both Bellman-Ford and Goldberg-Radzik algorithms.

### Environment:
Python version: 3.8.10 \
NetworkX version: 3.1

```python
# Python code to reproduce the issue
import networkx as nx
import matplotlib.pyplot as plt

class ManualGraphLoader:
    def __init__(self):
        self.G = self.create_graph_from_data()

    def create_graph_from_data(self):
        G = nx.DiGraph()
        # Add nodes
        for i in range(8):
            G.add_node(i)

        # Add edges with weights
        edges_data = [
            (0, 5, 12), (0, 7, 1), (0, 4, 6), (1, 0, -1), (2, 3, 5), (2, 7, -1), (4, 0, 12),
            (5, 4, 15), (5, 3, -16), (6, 3, 4), (6, 5, -4), (6, 4, -1), (7, 5, -8), (7, 0, 9),
            (7, 4, 7), (7, 2, 1)
        ]

        for src, tgt, weight in edges_data:
            G.add_edge(src, tgt, weight=weight)

        return G

    def compute_shortest_path_length_bellman_ford(self, source, target):
        """Computes shortest path length between source and target using Bellman-Ford algorithm."""
        try:
            return nx.bellman_ford_path_length(self.G, source=source, target=target, weight='weight')
        except nx.NetworkXNoPath:
            print(f"Bellman-Ford: No path exists between {source} and {target}.")
            return None
        except nx.NetworkXUnbounded:
            print(f"Bellman-Ford: A negative cycle exists in the graph.")
            return None

    def compute_shortest_path_length_goldberg_radzik(self, source, target):
        """Computes the shortest path length between source and target using the Goldberg-Radzik algorithm."""
        try:
            _, distance_map = nx.goldberg_radzik(self.G, source=source, weight='weight')
            return distance_map.get(target, None)
        except nx.NetworkXNoPath:
            print(f"Goldberg_Radzik: No path exists between {source} and {target}.")
            return None
        except nx.NetworkXUnbounded:
            print(f"Goldberg_Radzik: A negative cycle exists in the graph.")
            return None

    def get_graph(self):
        """Returns the manually created graph."""
        return self.G

if __name__ == "__main__":
    graph_loader = ManualGraphLoader()
    G = graph_loader.get_graph()

    source, target = 6, 0

    shortest_path_length = graph_loader.compute_shortest_path_length_bellman_ford(source, target)
    print(f"Shortest path length between {source} and {target} using Bellman-Ford: {shortest_path_length}")

    shortest_path_length = graph_loader.compute_shortest_path_length_goldberg_radzik(source, target)
    print(f"Shortest path length between {source} and {target} using Goldberg_Radzik: {shortest_path_length}")

    try:
        cycle = nx.negative_edge_cycle(G, weight='weight')
        if cycle:
            print("The graph has a negative weight cycle.")
        else:
            print("The graph does not have a negative weight cycle.")
    except nx.NetworkXUnbounded:
        print("The graph has a negative cycle.")


Discussion:
I'd appreciate any feedback on this issue. If more information or clarifications are needed, please let me know. 
This is puzzling -- (so perhaps a bug).   There is a cycle with weight zero.
`list(nx.simple_cycles(G)` reports `[[0, 4], [0, 7, 4], [0, 7], [0, 7, 5, 4], [0, 5, 4], [2, 7]]`
Manually checking these shows no negative cycles, but the `[2,7]` cycle has weight 0.
I suspect that is the trouble.  

Perhaps goldberg radzik is checking for non-positive cycles rather than negative cycles while bellman ford is looking for negative cycles....
But I haven't found the difference yet -- reporting early in case this helps you.

Resolving patch:
diff --git a/networkx/algorithms/shortest_paths/weighted.py b/networkx/algorithms/shortest_paths/weighted.py
--- a/networkx/algorithms/shortest_paths/weighted.py
+++ b/networkx/algorithms/shortest_paths/weighted.py
@@ -1973,6 +1973,10 @@ def goldberg_radzik(G, source, weight="weight"):
         negative (di)cycle.  Note: any negative weight edge in an
         undirected graph is a negative cycle.
 
+        As of NetworkX v3.2, a zero weight cycle is no longer
+        incorrectly reported as a negative weight cycle.
+
+
     Examples
     --------
     >>> G = nx.path_graph(5, create_using=nx.DiGraph())
@@ -2057,7 +2061,7 @@ def topo_sort(relabeled):
                     continue
                 t = d[u] + weight(u, v, e)
                 d_v = d[v]
-                if t <= d_v:
+                if t < d_v:
                     is_neg = t < d_v
                     d[v] = t
                     pred[v] = u


Test patch:
diff --git a/networkx/algorithms/shortest_paths/tests/test_weighted.py b/networkx/algorithms/shortest_paths/tests/test_weighted.py
--- a/networkx/algorithms/shortest_paths/tests/test_weighted.py
+++ b/networkx/algorithms/shortest_paths/tests/test_weighted.py
@@ -596,6 +596,20 @@ def test_negative_cycle(self):
         )
         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 doesnt 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])