1"""Floyd-Warshall algorithm for shortest paths."""
2
3import networkx as nx
4
5__all__ = [
6 "floyd_warshall",
7 "floyd_warshall_predecessor_and_distance",
8 "reconstruct_path",
9 "floyd_warshall_numpy",
10]
11
12
13@nx._dispatchable(edge_attrs="weight")
14def floyd_warshall_numpy(G, nodelist=None, weight="weight"):
15 """Find all-pairs shortest path lengths using Floyd's algorithm.
16
17 This algorithm for finding shortest paths takes advantage of
18 matrix representations of a graph and works well for dense
19 graphs where all-pairs shortest path lengths are desired.
20 The results are returned as a NumPy array, distance[i, j],
21 where i and j are the indexes of two nodes in nodelist.
22 The entry distance[i, j] is the distance along a shortest
23 path from i to j. If no path exists the distance is Inf.
24
25 Parameters
26 ----------
27 G : NetworkX graph
28
29 nodelist : list, optional (default=G.nodes)
30 The rows and columns are ordered by the nodes in nodelist.
31 If nodelist is None then the ordering is produced by G.nodes.
32 Nodelist should include all nodes in G.
33
34 weight: string, optional (default='weight')
35 Edge data key corresponding to the edge weight.
36
37 Returns
38 -------
39 distance : 2D numpy.ndarray
40 A numpy array of shortest path distances between nodes.
41 If there is no path between two nodes the value is Inf.
42
43 Examples
44 --------
45 >>> G = nx.DiGraph()
46 >>> G.add_weighted_edges_from(
47 ... [(0, 1, 5), (1, 2, 2), (2, 3, -3), (1, 3, 10), (3, 2, 8)]
48 ... )
49 >>> nx.floyd_warshall_numpy(G)
50 array([[ 0., 5., 7., 4.],
51 [inf, 0., 2., -1.],
52 [inf, inf, 0., -3.],
53 [inf, inf, 8., 0.]])
54
55 Notes
56 -----
57 Floyd's algorithm is appropriate for finding shortest paths in
58 dense graphs or graphs with negative weights when Dijkstra's
59 algorithm fails. This algorithm can still fail if there are negative
60 cycles. It has running time $O(n^3)$ with running space of $O(n^2)$.
61
62 Raises
63 ------
64 NetworkXError
65 If nodelist is not a list of the nodes in G.
66 """
67 import numpy as np
68
69 if nodelist is not None:
70 if not (len(nodelist) == len(G) == len(set(nodelist))):
71 raise nx.NetworkXError(
72 "nodelist must contain every node in G with no repeats."
73 "If you wanted a subgraph of G use G.subgraph(nodelist)"
74 )
75
76 # To handle cases when an edge has weight=0, we must make sure that
77 # nonedges are not given the value 0 as well.
78 A = nx.to_numpy_array(
79 G, nodelist, multigraph_weight=min, weight=weight, nonedge=np.inf
80 )
81 n, m = A.shape
82 np.fill_diagonal(A, 0) # diagonal elements should be zero
83 for i in range(n):
84 # The second term has the same shape as A due to broadcasting
85 A = np.minimum(A, A[i, :][np.newaxis, :] + A[:, i][:, np.newaxis])
86 return A
87
88
89@nx._dispatchable(edge_attrs="weight")
90def floyd_warshall_predecessor_and_distance(G, weight="weight"):
91 """Find all-pairs shortest path lengths using Floyd's algorithm.
92
93 Parameters
94 ----------
95 G : NetworkX graph
96
97 weight: string, optional (default= 'weight')
98 Edge data key corresponding to the edge weight.
99
100 Returns
101 -------
102 predecessor,distance : dictionaries
103 Dictionaries, keyed by source and target, of predecessors and distances
104 in the shortest path.
105
106 Examples
107 --------
108 >>> G = nx.DiGraph()
109 >>> G.add_weighted_edges_from(
110 ... [
111 ... ("s", "u", 10),
112 ... ("s", "x", 5),
113 ... ("u", "v", 1),
114 ... ("u", "x", 2),
115 ... ("v", "y", 1),
116 ... ("x", "u", 3),
117 ... ("x", "v", 5),
118 ... ("x", "y", 2),
119 ... ("y", "s", 7),
120 ... ("y", "v", 6),
121 ... ]
122 ... )
123 >>> predecessors, _ = nx.floyd_warshall_predecessor_and_distance(G)
124 >>> print(nx.reconstruct_path("s", "v", predecessors))
125 ['s', 'x', 'u', 'v']
126
127 Notes
128 -----
129 Floyd's algorithm is appropriate for finding shortest paths
130 in dense graphs or graphs with negative weights when Dijkstra's algorithm
131 fails. This algorithm can still fail if there are negative cycles.
132 It has running time $O(n^3)$ with running space of $O(n^2)$.
133
134 See Also
135 --------
136 floyd_warshall
137 floyd_warshall_numpy
138 all_pairs_shortest_path
139 all_pairs_shortest_path_length
140 """
141 from collections import defaultdict
142
143 # dictionary-of-dictionaries representation for dist and pred
144 # use some defaultdict magick here
145 # for dist the default is the floating point inf value
146 dist = defaultdict(lambda: defaultdict(lambda: float("inf")))
147 for u in G:
148 dist[u][u] = 0
149 pred = defaultdict(dict)
150 # initialize path distance dictionary to be the adjacency matrix
151 # also set the distance to self to 0 (zero diagonal)
152 undirected = not G.is_directed()
153 for u, v, d in G.edges(data=True):
154 e_weight = d.get(weight, 1.0)
155 dist[u][v] = min(e_weight, dist[u][v])
156 pred[u][v] = u
157 if undirected:
158 dist[v][u] = min(e_weight, dist[v][u])
159 pred[v][u] = v
160 for w in G:
161 dist_w = dist[w] # save recomputation
162 for u in G:
163 dist_u = dist[u] # save recomputation
164 for v in G:
165 d = dist_u[w] + dist_w[v]
166 if dist_u[v] > d:
167 dist_u[v] = d
168 pred[u][v] = pred[w][v]
169 return dict(pred), dict(dist)
170
171
172@nx._dispatchable(graphs=None)
173def reconstruct_path(source, target, predecessors):
174 """Reconstruct a path from source to target using the predecessors
175 dict as returned by floyd_warshall_predecessor_and_distance
176
177 Parameters
178 ----------
179 source : node
180 Starting node for path
181
182 target : node
183 Ending node for path
184
185 predecessors: dictionary
186 Dictionary, keyed by source and target, of predecessors in the
187 shortest path, as returned by floyd_warshall_predecessor_and_distance
188
189 Returns
190 -------
191 path : list
192 A list of nodes containing the shortest path from source to target
193
194 If source and target are the same, an empty list is returned
195
196 Notes
197 -----
198 This function is meant to give more applicability to the
199 floyd_warshall_predecessor_and_distance function
200
201 See Also
202 --------
203 floyd_warshall_predecessor_and_distance
204 """
205 if source == target:
206 return []
207 prev = predecessors[source]
208 curr = prev[target]
209 path = [target, curr]
210 while curr != source:
211 curr = prev[curr]
212 path.append(curr)
213 return list(reversed(path))
214
215
216@nx._dispatchable(edge_attrs="weight")
217def floyd_warshall(G, weight="weight"):
218 """Find all-pairs shortest path lengths using Floyd's algorithm.
219
220 Parameters
221 ----------
222 G : NetworkX graph
223
224 weight: string, optional (default= 'weight')
225 Edge data key corresponding to the edge weight.
226
227
228 Returns
229 -------
230 distance : dict
231 A dictionary, keyed by source and target, of shortest paths distances
232 between nodes.
233
234 Examples
235 --------
236 >>> from pprint import pprint
237 >>> G = nx.DiGraph()
238 >>> G.add_weighted_edges_from(
239 ... [(0, 1, 5), (1, 2, 2), (2, 3, -3), (1, 3, 10), (3, 2, 8)]
240 ... )
241 >>> fw = nx.floyd_warshall(G, weight="weight")
242 >>> results = {a: dict(b) for a, b in fw.items()}
243 >>> pprint(results)
244 {0: {0: 0, 1: 5, 2: 7, 3: 4},
245 1: {0: inf, 1: 0, 2: 2, 3: -1},
246 2: {0: inf, 1: inf, 2: 0, 3: -3},
247 3: {0: inf, 1: inf, 2: 8, 3: 0}}
248
249 Notes
250 -----
251 Floyd's algorithm is appropriate for finding shortest paths
252 in dense graphs or graphs with negative weights when Dijkstra's algorithm
253 fails. This algorithm can still fail if there are negative cycles.
254 It has running time $O(n^3)$ with running space of $O(n^2)$.
255
256 See Also
257 --------
258 floyd_warshall_predecessor_and_distance
259 floyd_warshall_numpy
260 all_pairs_shortest_path
261 all_pairs_shortest_path_length
262 """
263 # could make this its own function to reduce memory costs
264 return floyd_warshall_predecessor_and_distance(G, weight=weight)[1]