dense.py 7.2 KB

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