Repository: networkx/networkx

Issue:
Single source dijkstra has confusing output if target is specified
I would expect `single_source_dijkstra(G, source=u, target=v)` to return a pair in which the left element is the length of the shortest path from `u` to `v` and the right element is the shortest path from `u` to `v`. However, the return value seems to be the dictionary of lengths keyed by destination node and the dictionary of paths keyed by destination node that together represent the state of Dijkstra's algorithm at the point that the target node was found. Should this be changed?



Discussion:
It seems the implementation isn't quite complete for using source and target together.

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
@@ -47,6 +47,7 @@
            'goldberg_radzik',
            'johnson']
 
+
 def _weight_function(G, weight):
     """Returns a function that returns the weight of an edge.
 
@@ -86,6 +87,7 @@ def _weight_function(G, weight):
         return lambda u, v, d: min(attr.get(weight, 1) for attr in d.values())
     return lambda u, v, data: data.get(weight, 1)
 
+
 def dijkstra_path(G, source, target, weight='weight'):
     """Returns the shortest weighted path from source to target in G.
 
@@ -157,11 +159,7 @@ def dijkstra_path(G, source, target, weight='weight'):
     """
     (length, path) = single_source_dijkstra(G, source, target=target,
                                             weight=weight)
-    try:
-        return path[target]
-    except KeyError:
-        raise nx.NetworkXNoPath(
-            "node %s not reachable from %s" % (target, source))
+    return path
 
 
 def dijkstra_path_length(G, source, target, weight='weight'):
@@ -398,16 +396,19 @@ def single_source_dijkstra(G, source, target=None, cutoff=None,
 
     Returns
     -------
-    distance,path : dictionaries
-       Returns a tuple of two dictionaries keyed by node.
-       The first dictionary stores distance from the source.
-       The second stores the path from the source to that node.
+    distance, path : pair of dictionaries, or numeric and list
+       If target is None, returns a tuple of two dictionaries keyed by node.
+       The first dictionary stores distance from one of the source nodes.
+       The second stores the path from one of the sources to that node.
+       If target is not None, returns a tuple of (distance, path) where
+       distance is the distance from source to target and path is a list
+       representing the path from source to target.
 
 
     Examples
     --------
-    >>> G=nx.path_graph(5)
-    >>> length, path=nx.single_source_dijkstra(G, 0)
+    >>> G = nx.path_graph(5)
+    >>> length, path = nx.single_source_dijkstra(G, 0)
     >>> print(length[4])
     4
     >>> for node in [0, 1, 2, 3, 4]:
@@ -419,6 +420,11 @@ def single_source_dijkstra(G, source, target=None, cutoff=None,
     4: 4
     >>> path[4]
     [0, 1, 2, 3, 4]
+    >>> length, path = nx.single_source_dijkstra(G, 0, 1)
+    >>> length
+    1
+    >>> path
+    [0, 1]
 
     Notes
     -----
@@ -596,7 +602,7 @@ def multi_source_dijkstra_path_length(G, sources, cutoff=None,
 
 
 def multi_source_dijkstra(G, sources, target=None, cutoff=None,
-                           weight='weight'):
+                          weight='weight'):
     """Find shortest weighted paths and lengths from a given set of
     source nodes.
 
@@ -635,10 +641,13 @@ def multi_source_dijkstra(G, sources, target=None, cutoff=None,
 
     Returns
     -------
-    distance, path : pair of dictionaries
-       Returns a tuple of two dictionaries keyed by node.
+    distance, path : pair of dictionaries, or numeric and list
+       If target is None, returns a tuple of two dictionaries keyed by node.
        The first dictionary stores distance from one of the source nodes.
        The second stores the path from one of the sources to that node.
+       If target is not None, returns a tuple of (distance, path) where
+       distance is the distance from source to target and path is a list
+       representing the path from source to target.
 
     Examples
     --------
@@ -656,6 +665,12 @@ def multi_source_dijkstra(G, sources, target=None, cutoff=None,
     >>> path[3]
     [4, 3]
 
+    >>> length, path = nx.multi_source_dijkstra(G, {0, 4}, 1)
+    >>> length
+    1
+    >>> path
+    [0, 1]
+
     Notes
     -----
     Edge weight attributes must be numerical.
@@ -686,12 +701,17 @@ def multi_source_dijkstra(G, sources, target=None, cutoff=None,
     if not sources:
         raise ValueError('sources must not be empty')
     if target in sources:
-        return ({target: 0}, {target: [target]})
+        return (0, [target])
     weight = _weight_function(G, weight)
     paths = {source: [source] for source in sources}  # dictionary of paths
     dist = _dijkstra_multisource(G, sources, weight, paths=paths,
                                  cutoff=cutoff, target=target)
-    return (dist, paths)
+    if target is None:
+        return (dist, paths)
+    try:
+        return (dist[target], paths[target])
+    except KeyError:
+        raise nx.NetworkXNoPath("No path to {}.".format(target))
 
 
 def _dijkstra(G, source, weight, pred=None, paths=None, cutoff=None,
@@ -836,6 +856,8 @@ def dijkstra_predecessor_and_distance(G, source, cutoff=None, weight='weight'):
     pred, distance : dictionaries
        Returns two dictionaries representing a list of predecessors
        of a node and the distance to each node.
+       Warning: If target is specified, the dicts are incomplete as they
+       only contain information for the nodes along a path to target.
 
     Notes
     -----
@@ -844,6 +866,22 @@ def dijkstra_predecessor_and_distance(G, source, cutoff=None, weight='weight'):
 
     The list of predecessors contains more than one element only when
     there are more than one shortest paths to the key node.
+
+    Examples
+    --------
+    >>> import networkx as nx
+    >>> G = nx.path_graph(5, create_using = nx.DiGraph())
+    >>> pred, dist = nx.dijkstra_predecessor_and_distance(G, 0)
+    >>> sorted(pred.items())
+    [(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.dijkstra_predecessor_and_distance(G, 0, 1)
+    >>> sorted(pred.items())
+    [(0, []), (1, [0])]
+    >>> sorted(dist.items())
+    [(0, 0), (1, 1)]
     """
 
     weight = _weight_function(G, weight)
@@ -957,17 +995,20 @@ def all_pairs_dijkstra_path(G, cutoff=None, weight='weight'):
     # TODO This can be trivially parallelized.
     return {n: path(G, n, cutoff=cutoff, weight=weight) for n in G}
 
-def bellman_ford(G, source, weight='weight'):
 
-    """DEPRECATED: Has been replaced by function bellman_ford_predecessor_and_distance().
+def bellman_ford(G, source, weight='weight'):
+    """DEPRECATED: Replaced by bellman_ford_predecessor_and_distance().
 
     """
-    _warnings.warn("Function bellman_ford() is deprecated, use function bellman_ford_predecessor_and_distance() instead.",
-                   DeprecationWarning)
+    msg = "Function bellman_ford() is deprecated, use " \
+        "bellman_ford_predecessor_and_distance() instead."
+    _warnings.warn(msg, DeprecationWarning)
 
     return bellman_ford_predecessor_and_distance(G, source, weight=weight)
 
-def bellman_ford_predecessor_and_distance(G, source, target=None, cutoff=None, weight='weight'):
+
+def bellman_ford_predecessor_and_distance(G, source, target=None,
+                                          cutoff=None, weight='weight'):
     """Compute shortest path lengths and predecessors on shortest paths
     in weighted graphs.
 
@@ -1002,6 +1043,8 @@ def bellman_ford_predecessor_and_distance(G, source, target=None, cutoff=None, w
     pred, dist : dictionaries
        Returns two dictionaries keyed by node to predecessor in the
        path and to the distance from the source respectively.
+       Warning: If target is specified, the dicts are incomplete as they
+       only contain information for the nodes along a path to target.
 
     Raises
     ------
@@ -1021,6 +1064,12 @@ def bellman_ford_predecessor_and_distance(G, source, target=None, cutoff=None, w
     >>> 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])]
+    >>> sorted(dist.items())
+    [(0, 0), (1, 1)]
+
     >>> from nose.tools import assert_raises
     >>> G = nx.cycle_graph(5, create_using = nx.DiGraph())
     >>> G[1][2]['weight'] = -7
@@ -1054,7 +1103,9 @@ def bellman_ford_predecessor_and_distance(G, source, target=None, cutoff=None, w
 
     weight = _weight_function(G, weight)
 
-    return (pred, _bellman_ford(G, [source], weight,pred=pred, dist=dist, cutoff=cutoff, target=target))
+    dist = _bellman_ford(G, [source], weight, pred=pred, dist=dist,
+                         cutoff=cutoff, target=target)
+    return (pred, dist)
 
 
 def _bellman_ford(G, source, weight, pred=None, paths=None, dist=None,
@@ -1096,8 +1147,8 @@ def _bellman_ford(G, source, weight, pred=None, paths=None, dist=None,
 
     Returns
     -------
-    Returns two dictionaries keyed by node to predecessor in the
-       path and to the distance from the source respectively.
+    Returns a dict keyed by node to the distance from the source.
+    Dicts for paths and pred are in the mutated input dicts by those names.
 
     Raises
     ------
@@ -1168,9 +1219,9 @@ def _bellman_ford(G, source, weight, pred=None, paths=None, dist=None,
             path.reverse()
             paths[dst] = path
 
-
     return dist
 
+
 def bellman_ford_path(G, source, target, weight='weight'):
     """Returns the shortest path from source to target in a weighted graph G.
 
@@ -1200,7 +1251,7 @@ def bellman_ford_path(G, source, target, weight='weight'):
     Examples
     --------
     >>> G=nx.path_graph(5)
-    >>> print(nx.bellman_ford_path(G,0,4))
+    >>> print(nx.bellman_ford_path(G, 0, 4))
     [0, 1, 2, 3, 4]
 
     Notes
@@ -1212,12 +1263,10 @@ def bellman_ford_path(G, source, target, weight='weight'):
     --------
     dijkstra_path(), bellman_ford_path_length()
     """
-    (lengths, paths) = single_source_bellman_ford(G, source, target=target, weight=weight)
-    try:
-        return paths[target]
-    except KeyError:
-        raise nx.NetworkXNoPath(
-            "Node %s not reachable from %s" % (source, target))
+    length, path = single_source_bellman_ford(G, source,
+                                              target=target, weight=weight)
+    return path
+
 
 def bellman_ford_path_length(G, source, target, weight='weight'):
     """Returns the shortest path length from source to target
@@ -1266,7 +1315,7 @@ def bellman_ford_path_length(G, source, target, weight='weight'):
 
     weight = _weight_function(G, weight)
 
-    length =  _bellman_ford(G, [source], weight, target=target)
+    length = _bellman_ford(G, [source], weight, target=target)
 
     try:
         return length[target]
@@ -1274,6 +1323,7 @@ def bellman_ford_path_length(G, source, target, weight='weight'):
         raise nx.NetworkXNoPath(
             "node %s not reachable from %s" % (source, target))
 
+
 def single_source_bellman_ford_path(G, source, cutoff=None, weight='weight'):
     """Compute shortest path between source and all other reachable
     nodes for a weighted graph.
@@ -1317,7 +1367,9 @@ def single_source_bellman_ford_path(G, source, cutoff=None, weight='weight'):
         G, source, cutoff=cutoff, weight=weight)
     return path
 
-def single_source_bellman_ford_path_length(G, source, cutoff=None, weight='weight'):
+
+def single_source_bellman_ford_path_length(G, source,
+                                           cutoff=None, weight='weight'):
     """Compute the shortest path length between source and all other
     reachable nodes for a weighted graph.
 
@@ -1367,7 +1419,9 @@ def single_source_bellman_ford_path_length(G, source, cutoff=None, weight='weigh
 
     return iter(_bellman_ford(G, [source], weight, cutoff=cutoff).items())
 
-def single_source_bellman_ford(G, source, target=None, cutoff=None, weight='weight'):
+
+def single_source_bellman_ford(G, source,
+                               target=None, cutoff=None, weight='weight'):
     """Compute shortest paths and lengths in a weighted graph G.
 
     Uses Bellman-Ford algorithm for shortest paths.
@@ -1387,16 +1441,19 @@ def single_source_bellman_ford(G, source, target=None, cutoff=None, weight='weig
 
     Returns
     -------
-    distance,path : dictionaries
-       Returns a tuple of two dictionaries keyed by node.
-       The first dictionary stores distance from the source.
-       The second stores the path from the source to that node.
+    distance, path : pair of dictionaries, or numeric and list
+       If target is None, returns a tuple of two dictionaries keyed by node.
+       The first dictionary stores distance from one of the source nodes.
+       The second stores the path from one of the sources to that node.
+       If target is not None, returns a tuple of (distance, path) where
+       distance is the distance from source to target and path is a list
+       representing the path from source to target.
 
 
     Examples
     --------
-    >>> G=nx.path_graph(5)
-    >>> length,path=nx.single_source_bellman_ford(G,0)
+    >>> G = nx.path_graph(5)
+    >>> length, path = nx.single_source_bellman_ford(G, 0)
     >>> print(length[4])
     4
     >>> for node in [0, 1, 2, 3, 4]:
@@ -1408,6 +1465,11 @@ def single_source_bellman_ford(G, source, target=None, cutoff=None, weight='weig
     4: 4
     >>> path[4]
     [0, 1, 2, 3, 4]
+    >>> length, path = nx.single_source_bellman_ford(G, 0, 1)
+    >>> length
+    1
+    >>> path
+    [0, 1]
 
     Notes
     -----
@@ -1421,13 +1483,21 @@ def single_source_bellman_ford(G, source, target=None, cutoff=None, weight='weig
     single_source_bellman_ford_path_length()
     """
     if source == target:
-        return ({source: 0}, {source: [source]})
+        return (0, [source])
 
     weight = _weight_function(G, weight)
 
     paths = {source: [source]}  # dictionary of paths
-    return (_bellman_ford(G, [source], weight, paths=paths, cutoff=cutoff,
-                     target=target), paths)
+    dist = _bellman_ford(G, [source], weight, paths=paths, cutoff=cutoff,
+                          target=target)
+    if target is None:
+        return (dist, paths)
+    try:
+        return (dist[target], paths[target])
+    except KeyError:
+        msg = "Node %s not reachable from %s" % (source, target)
+        raise nx.NetworkXNoPath(msg)
+
 
 def all_pairs_bellman_ford_path_length(G, cutoff=None, weight='weight'):
     """ Compute shortest path lengths between all nodes in a weighted graph.
@@ -1475,6 +1545,7 @@ def all_pairs_bellman_ford_path_length(G, cutoff=None, weight='weight'):
     for n in G:
         yield (n, dict(length(G, n, cutoff=cutoff, weight=weight)))
 
+
 def all_pairs_bellman_ford_path(G, cutoff=None, weight='weight'):
     """ Compute shortest paths between all nodes in a weighted graph.
 
@@ -1514,6 +1585,7 @@ def all_pairs_bellman_ford_path(G, cutoff=None, weight='weight'):
     # TODO This can be trivially parallelized.
     return {n: path(G, n, cutoff=cutoff, weight=weight) for n in G}
 
+
 def goldberg_radzik(G, source, weight='weight'):
     """Compute shortest path lengths and predecessors on shortest paths
     in weighted graphs.
@@ -1732,10 +1804,10 @@ def negative_edge_cycle(G, weight='weight'):
     Edge weight attributes must be numerical.
     Distances are calculated as sums of weighted edges traversed.
 
-    This algorithm uses bellman_ford_predecessor_and_distance() but finds negative cycles
-    on any component by first adding a new node connected to
-    every node, and starting bellman_ford_predecessor_and_distance on that node.  It then
-    removes that extra node.
+    This algorithm uses bellman_ford_predecessor_and_distance() but finds
+    negative cycles on any component by first adding a new node connected to
+    every node, and starting bellman_ford_predecessor_and_distance on that
+    node.  It then removes that extra node.
     """
     newnode = generate_unique_node()
     G.add_edges_from([(newnode, n) for n in G])
@@ -1777,12 +1849,9 @@ def bidirectional_dijkstra(G, source, target, weight='weight'):
 
     Returns
     -------
-    length : number
-        Shortest path length.
-
-    Returns a tuple of two dictionaries keyed by node.
-    The first dictionary stores distance from the source.
-    The second stores the path from the source to that node.
+    length, path : number and list
+       length is the distance from source to target.
+       path is a list of nodes on a path from source to target.
 
     Raises
     ------
@@ -1791,8 +1860,8 @@ def bidirectional_dijkstra(G, source, target, weight='weight'):
 
     Examples
     --------
-    >>> G=nx.path_graph(5)
-    >>> length,path=nx.bidirectional_dijkstra(G,0,4)
+    >>> G = nx.path_graph(5)
+    >>> length, path = nx.bidirectional_dijkstra(G, 0, 4)
     >>> print(length)
     4
     >>> print(path)
@@ -1831,10 +1900,9 @@ def bidirectional_dijkstra(G, source, target, weight='weight'):
     push = heappush
     pop = heappop
     # Init:  [Forward, Backward]
-    dists = [{}, {}]                   # dictionary of final distances
+    dists = [{}, {}]   # dictionary of final distances
     paths = [{source: [source]}, {target: [target]}]  # dictionary of paths
-    fringe = [[], []]                  # heap of (distance, node) tuples
-                                       # for choosing next node to expand
+    fringe = [[], []]  # heap of (distance, node) for choosing node to expand
     seen = [{source: 0}, {target: 0}]  # dict of distances to seen nodes
     c = count()
     # initialize fringe heap
@@ -1978,10 +2046,11 @@ def johnson(G, weight='weight'):
 
     # Calculate distance of shortest paths
     dist_bellman = _bellman_ford(G, list(G), weight, pred=pred, dist=dist)
+
     # Update the weight function to take into account the Bellman--Ford
     # relaxation distances.
-    scale = lambda u, v: dist_bellman[u] - dist_bellman[v]
-    new_weight = lambda u, v, d: weight(u, v, d) + scale(u, v)
+    def new_weight(u, v, d):
+        return weight(u, v, d) + dist_bellman[u] - dist_bellman[v]
 
     def dist_path(v):
         paths = {v: [v]}
@@ -1989,4 +2058,3 @@ def dist_path(v):
         return paths
 
     return {v: dist_path(v) for v in G}
-


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
@@ -108,7 +108,7 @@ def test_dijkstra(self):
         assert_equal(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]['v'])
+            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'])
 
@@ -123,8 +123,7 @@ def test_dijkstra(self):
         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_equal(
-            nx.single_source_dijkstra(self.cycle, 0, 0), ({0: 0}, {0: [0]}))
+        assert_equal(nx.single_source_dijkstra(self.cycle, 0, 0), (0, [0]))
 
     def test_bidirectional_dijkstra(self):
         validate_length_path(
@@ -231,14 +230,14 @@ def test_weight_function(self):
         weight = lambda u, v, d: 1 / d['weight']
         # The shortest path from 0 to 2 using the actual weights on the
         # edges should be [0, 1, 2].
-        distances, paths = nx.single_source_dijkstra(G, 0, 2)
-        assert_equal(distances[2], 2)
-        assert_equal(paths[2], [0, 1, 2])
+        distance, path = nx.single_source_dijkstra(G, 0, 2)
+        assert_equal(distance, 2)
+        assert_equal(path, [0, 1, 2])
         # However, with the above weight function, the shortest path
         # should be [0, 2], since that has a very small weight.
-        distances, paths = nx.single_source_dijkstra(G, 0, 2, weight=weight)
-        assert_equal(distances[2], 1 / 10)
-        assert_equal(paths[2], [0, 2])
+        distance, path = nx.single_source_dijkstra(G, 0, 2, weight=weight)
+        assert_equal(distance, 1 / 10)
+        assert_equal(path, [0, 2])
 
 
 class TestDijkstraPathLength(object):
@@ -397,21 +396,21 @@ def test_multigraph(self):
         assert_equal(nx.single_source_bellman_ford_path(self.MXG, 's')['v'], ['s', 'x', 'u', 'v'])
         assert_equal(dict(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_equal(D['v'], 9)
-        assert_equal(P['v'], ['s', 'x', 'u', 'v'])
+        assert_equal(D, 9)
+        assert_equal(P, ['s', 'x', 'u', 'v'])
         P, D = nx.bellman_ford_predecessor_and_distance(self.MXG, 's')
         assert_equal(P['v'], ['u'])
         assert_equal(D['v'], 9)
         P, D = nx.goldberg_radzik(self.MXG, 's')
         assert_equal(P['v'], 'u')
-        assert_equal(D['v'], 9)        
+        assert_equal(D['v'], 9)
         assert_equal(nx.bellman_ford_path(self.MXG4, 0, 2), [0, 1, 2])
         assert_equal(nx.bellman_ford_path_length(self.MXG4, 0, 2), 4)
         assert_equal(nx.single_source_bellman_ford_path(self.MXG4, 0)[2], [0, 1, 2])
         assert_equal(dict(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_equal(D[2], 4)
-        assert_equal(P[2], [0, 1, 2])
+        assert_equal(D, 4)
+        assert_equal(P, [0, 1, 2])
         P, D = nx.bellman_ford_predecessor_and_distance(self.MXG4, 0)
         assert_equal(P[2], [1])
         assert_equal(D[2], 4)
@@ -425,8 +424,8 @@ def test_others(self):
         assert_equal(nx.single_source_bellman_ford_path(self.XG, 's')['v'], ['s', 'x', 'u', 'v'])
         assert_equal(dict(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_equal(D['v'], 9)
-        assert_equal(P['v'], ['s', 'x', 'u', 'v'])
+        assert_equal(D, 9)
+        assert_equal(P, ['s', 'x', 'u', 'v'])
         (P, D) = nx.bellman_ford_predecessor_and_distance(self.XG, 's')
         assert_equal(P['v'], ['u'])
         assert_equal(D['v'], 9)