Repository: networkx/networkx

Issue:
inconsistent behavior of predecessor-and-distance functions
While working on #2346 , I ran into the following slightly surprising behavior:

* `dijkstra_predecessor_and_distance()` sets `pred[source]` to be `[]`
* `bellman_ford_predecessor_and_distance()` sets `pred[source]` to be `[None]`

This behavior should be replicated by the following (as of commit `1a521966`):
```
>>> import networkx as nx
>>> G = nx.path_graph(2)
>>> nx.dijkstra_predecessor_and_distance(G, 0)[0]
{0: [], 1: [0]}
>>> nx.bellman_ford_predecessor_and_distance(G, 0)[0]
{0: [None], 1: [0]}
```

(From the [code for `_bellman_ford()`](https://github.com/networkx/networkx/blob/1a52196699668ec5531711590621b0d4821e57ae/networkx/algorithms/shortest_paths/weighted.py#L1166), `None` is being used as a sentinel value when constructing paths.)

This produces an error when trying to use the two algorithms' output interchangeably.

The inconsistency is small, but does seem like a bug. These two functions' return values should probably have similar conventions; the behavior of `dijkstra_predecessor_and_distance()` makes the most sense to me.


Discussion:

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
@@ -1115,13 +1115,13 @@ def bellman_ford_predecessor_and_distance(G, source, target=None,
     >>> G = nx.path_graph(5, create_using = nx.DiGraph())
     >>> pred, dist = nx.bellman_ford_predecessor_and_distance(G, 0)
     >>> sorted(pred.items())
-    [(0, [None]), (1, [0]), (2, [1]), (3, [2]), (4, [3])]
+    [(0, []), (1, [0]), (2, [1]), (3, [2]), (4, [3])]
     >>> sorted(dist.items())
     [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
 
     >>> pred, dist = nx.bellman_ford_predecessor_and_distance(G, 0, 1)
     >>> sorted(pred.items())
-    [(0, [None]), (1, [0])]
+    [(0, []), (1, [0])]
     >>> sorted(dist.items())
     [(0, 0), (1, 1)]
 
@@ -1143,6 +1143,8 @@ def bellman_ford_predecessor_and_distance(G, source, target=None,
     not containing the source contains a negative cost (di)cycle, it
     will not be detected.
 
+    In NetworkX v2.1 and prior, the source node had predecessor `[None]`.
+    In NetworkX v2.2 this changed to the source node having predecessor `[]`
     """
     if source not in G:
         raise nx.NodeNotFound("Node %s is not found in the graph" % source)
@@ -1151,7 +1153,7 @@ def bellman_ford_predecessor_and_distance(G, source, target=None,
         raise nx.NetworkXUnbounded("Negative cost cycle detected.")
 
     dist = {source: 0}
-    pred = {source: [None]}
+    pred = {source: []}
 
     if len(G) == 1:
         return pred, dist
@@ -1215,7 +1217,7 @@ def _bellman_ford(G, source, weight, pred=None, paths=None, dist=None,
     """
 
     if pred is None:
-        pred = {v: [None] for v in source}
+        pred = {v: [] for v in source}
 
     if dist is None:
         dist = {v: 0 for v in source}
@@ -1267,7 +1269,7 @@ def _bellman_ford(G, source, weight, pred=None, paths=None, dist=None,
             path = [dst]
             cur = dst
 
-            while pred[cur][0] is not None:
+            while pred[cur]:
                 cur = pred[cur][0]
                 path.append(cur)
 
@@ -2096,7 +2098,7 @@ def johnson(G, weight='weight'):
         raise nx.NetworkXError('Graph is not weighted.')
 
     dist = {v: 0 for v in G}
-    pred = {v: [None] for v in G}
+    pred = {v: [] for v in G}
     weight = _weight_function(G, weight)
 
     # Calculate distance of shortest paths


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
@@ -346,7 +346,7 @@ def test_single_node_graph(self):
         assert_equal(nx.single_source_bellman_ford_path(G, 0), {0: [0]})
         assert_equal(nx.single_source_bellman_ford_path_length(G, 0), {0: 0})
         assert_equal(nx.single_source_bellman_ford(G, 0), ({0: 0}, {0: [0]}))
-        assert_equal(nx.bellman_ford_predecessor_and_distance(G, 0), ({0: [None]}, {0: 0}))
+        assert_equal(nx.bellman_ford_predecessor_and_distance(G, 0), ({0: []}, {0: 0}))
         assert_equal(nx.goldberg_radzik(G, 0), ({0: None}, {0: 0}))
         assert_raises(nx.NodeNotFound, nx.bellman_ford_predecessor_and_distance, G, 1)
         assert_raises(nx.NodeNotFound, nx.goldberg_radzik, G, 1)
@@ -385,7 +385,7 @@ def test_negative_weight_cycle(self):
                      ({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_equal(nx.bellman_ford_predecessor_and_distance(G, 0),
-                     ({0: [None], 1: [0], 2: [1], 3: [2], 4: [3]},
+                     ({0: [], 1: [0], 2: [1], 3: [2], 4: [3]},
                       {0: 0, 1: 1, 2: -2, 3: -1, 4: 0}))
         assert_equal(nx.goldberg_radzik(G, 0),
                      ({0: None, 1: 0, 2: 1, 3: 2, 4: 3},
@@ -403,7 +403,7 @@ def test_not_connected(self):
                      ({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_equal(nx.bellman_ford_predecessor_and_distance(G, 0),
-                     ({0: [None], 1: [0], 2: [0], 3: [0], 4: [0], 5: [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_equal(nx.goldberg_radzik(G, 0),
                      ({0: None, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0},
@@ -423,7 +423,7 @@ def test_not_connected(self):
                      ({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_equal(nx.bellman_ford_predecessor_and_distance(G, 0, weight='load'),
-                     ({0: [None], 1: [0], 2: [0], 3: [0], 4: [0], 5: [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_equal(nx.goldberg_radzik(G, 0, weight='load'),
                      ({0: None, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0},
@@ -481,7 +481,7 @@ def test_path_graph(self):
         assert_equal(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_equal(nx.bellman_ford_predecessor_and_distance(G, 0),
-                     ({0: [None], 1: [0], 2: [1], 3: [2]}, {0: 0, 1: 1, 2: 2, 3: 3}))
+                     ({0: [], 1: [0], 2: [1], 3: [2]}, {0: 0, 1: 1, 2: 2, 3: 3}))
         assert_equal(nx.goldberg_radzik(G, 0),
                      ({0: None, 1: 0, 2: 1, 3: 2}, {0: 0, 1: 1, 2: 2, 3: 3}))
         assert_equal(nx.single_source_bellman_ford_path(G, 3),
@@ -491,7 +491,7 @@ def test_path_graph(self):
         assert_equal(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_equal(nx.bellman_ford_predecessor_and_distance(G, 3),
-                     ({0: [1], 1: [2], 2: [3], 3: [None]}, {0: 3, 1: 2, 2: 1, 3: 0}))
+                     ({0: [1], 1: [2], 2: [3], 3: []}, {0: 3, 1: 2, 2: 1, 3: 0}))
         assert_equal(nx.goldberg_radzik(G, 3),
                      ({0: 1, 1: 2, 2: 3, 3: None}, {0: 3, 1: 2, 2: 1, 3: 0}))
 
@@ -506,7 +506,7 @@ def test_4_cycle(self):
         assert_equal(path[3], [0, 3])
 
         pred, dist = nx.bellman_ford_predecessor_and_distance(G, 0)
-        assert_equal(pred[0], [None])
+        assert_equal(pred[0], [])
         assert_equal(pred[1], [0])
         assert_true(pred[2] in [[1, 3], [3, 1]])
         assert_equal(pred[3], [0])