1"""
2Compute the shortest paths and path lengths between nodes in the graph.
3
4These algorithms work with undirected and directed graphs.
5
6"""
7
8import networkx as nx
9
10__all__ = [
11 "shortest_path",
12 "all_shortest_paths",
13 "single_source_all_shortest_paths",
14 "all_pairs_all_shortest_paths",
15 "shortest_path_length",
16 "average_shortest_path_length",
17 "has_path",
18]
19
20
21@nx._dispatchable
22def has_path(G, source, target):
23 """Returns *True* if *G* has a path from *source* to *target*.
24
25 Parameters
26 ----------
27 G : NetworkX graph
28
29 source : node
30 Starting node for path
31
32 target : node
33 Ending node for path
34 """
35 try:
36 nx.shortest_path(G, source, target)
37 except nx.NetworkXNoPath:
38 return False
39 return True
40
41
42@nx._dispatchable(edge_attrs="weight")
43def shortest_path(G, source=None, target=None, weight=None, method="dijkstra"):
44 """Compute shortest paths in the graph.
45
46 Parameters
47 ----------
48 G : NetworkX graph
49
50 source : node, optional
51 Starting node for path. If not specified, compute shortest
52 paths for each possible starting node.
53
54 target : node, optional
55 Ending node for path. If not specified, compute shortest
56 paths to all possible nodes.
57
58 weight : None, string or function, optional (default = None)
59 If None, every edge has weight/distance/cost 1.
60 If a string, use this edge attribute as the edge weight.
61 Any edge attribute not present defaults to 1.
62 If this is a function, the weight of an edge is the value
63 returned by the function. The function must accept exactly
64 three positional arguments: the two endpoints of an edge and
65 the dictionary of edge attributes for that edge.
66 The function must return a number.
67
68 method : string, optional (default = 'dijkstra')
69 The algorithm to use to compute the path.
70 Supported options: 'dijkstra', 'bellman-ford'.
71 Other inputs produce a ValueError.
72 If `weight` is None, unweighted graph methods are used, and this
73 suggestion is ignored.
74
75 Returns
76 -------
77 path: list or dictionary or iterator
78 All returned paths include both the source and target in the path.
79
80 If the source and target are both specified, return a single list
81 of nodes in a shortest path from the source to the target.
82
83 If only the source is specified, return a dictionary keyed by
84 targets with a list of nodes in a shortest path from the source
85 to one of the targets.
86
87 If only the target is specified, return a dictionary keyed by
88 sources with a list of nodes in a shortest path from one of the
89 sources to the target.
90
91 If neither the source nor target are specified, return an iterator
92 over (source, dictionary) where dictionary is keyed by target to
93 list of nodes in a shortest path from the source to the target.
94
95 Raises
96 ------
97 NodeNotFound
98 If `source` is not in `G`.
99
100 ValueError
101 If `method` is not among the supported options.
102
103 NetworkXNoPath
104 If `source` and `target` are specified but no path exists between them.
105
106 Examples
107 --------
108 >>> G = nx.path_graph(5)
109 >>> print(nx.shortest_path(G, source=0, target=4))
110 [0, 1, 2, 3, 4]
111 >>> p = nx.shortest_path(G, source=0) # target not specified
112 >>> p[3] # shortest path from source=0 to target=3
113 [0, 1, 2, 3]
114 >>> p = nx.shortest_path(G, target=4) # source not specified
115 >>> p[1] # shortest path from source=1 to target=4
116 [1, 2, 3, 4]
117 >>> p = dict(nx.shortest_path(G)) # source, target not specified
118 >>> p[2][4] # shortest path from source=2 to target=4
119 [2, 3, 4]
120
121 Notes
122 -----
123 There may be more than one shortest path between a source and target.
124 This returns only one of them.
125
126 See Also
127 --------
128 all_pairs_shortest_path
129 all_pairs_dijkstra_path
130 all_pairs_bellman_ford_path
131 single_source_shortest_path
132 single_source_dijkstra_path
133 single_source_bellman_ford_path
134 """
135 if method not in ("dijkstra", "bellman-ford"):
136 # so we don't need to check in each branch later
137 raise ValueError(f"method not supported: {method}")
138 method = "unweighted" if weight is None else method
139 if source is None:
140 if target is None:
141 # Find paths between all pairs. Iterator of dicts.
142 if method == "unweighted":
143 paths = nx.all_pairs_shortest_path(G)
144 elif method == "dijkstra":
145 paths = nx.all_pairs_dijkstra_path(G, weight=weight)
146 else: # method == 'bellman-ford':
147 paths = nx.all_pairs_bellman_ford_path(G, weight=weight)
148 else:
149 # Find paths from all nodes co-accessible to the target.
150 if G.is_directed():
151 G = G.reverse(copy=False)
152 if method == "unweighted":
153 paths = nx.single_source_shortest_path(G, target)
154 elif method == "dijkstra":
155 paths = nx.single_source_dijkstra_path(G, target, weight=weight)
156 else: # method == 'bellman-ford':
157 paths = nx.single_source_bellman_ford_path(G, target, weight=weight)
158 # Now flip the paths so they go from a source to the target.
159 for target in paths:
160 paths[target] = list(reversed(paths[target]))
161 else:
162 if target is None:
163 # Find paths to all nodes accessible from the source.
164 if method == "unweighted":
165 paths = nx.single_source_shortest_path(G, source)
166 elif method == "dijkstra":
167 paths = nx.single_source_dijkstra_path(G, source, weight=weight)
168 else: # method == 'bellman-ford':
169 paths = nx.single_source_bellman_ford_path(G, source, weight=weight)
170 else:
171 # Find shortest source-target path.
172 if method == "unweighted":
173 paths = nx.bidirectional_shortest_path(G, source, target)
174 elif method == "dijkstra":
175 _, paths = nx.bidirectional_dijkstra(G, source, target, weight)
176 else: # method == 'bellman-ford':
177 paths = nx.bellman_ford_path(G, source, target, weight)
178 return paths
179
180
181@nx._dispatchable(edge_attrs="weight")
182def shortest_path_length(G, source=None, target=None, weight=None, method="dijkstra"):
183 """Compute shortest path lengths in the graph.
184
185 Parameters
186 ----------
187 G : NetworkX graph
188
189 source : node, optional
190 Starting node for path.
191 If not specified, compute shortest path lengths using all nodes as
192 source nodes.
193
194 target : node, optional
195 Ending node for path.
196 If not specified, compute shortest path lengths using all nodes as
197 target nodes.
198
199 weight : None, string or function, optional (default = None)
200 If None, every edge has weight/distance/cost 1.
201 If a string, use this edge attribute as the edge weight.
202 Any edge attribute not present defaults to 1.
203 If this is a function, the weight of an edge is the value
204 returned by the function. The function must accept exactly
205 three positional arguments: the two endpoints of an edge and
206 the dictionary of edge attributes for that edge.
207 The function must return a number.
208
209 method : string, optional (default = 'dijkstra')
210 The algorithm to use to compute the path length.
211 Supported options: 'dijkstra', 'bellman-ford'.
212 Other inputs produce a ValueError.
213 If `weight` is None, unweighted graph methods are used, and this
214 suggestion is ignored.
215
216 Returns
217 -------
218 length: number or iterator
219 If the source and target are both specified, return the length of
220 the shortest path from the source to the target.
221
222 If only the source is specified, return a dict keyed by target
223 to the shortest path length from the source to that target.
224
225 If only the target is specified, return a dict keyed by source
226 to the shortest path length from that source to the target.
227
228 If neither the source nor target are specified, return an iterator
229 over (source, dictionary) where dictionary is keyed by target to
230 shortest path length from source to that target.
231
232 Raises
233 ------
234 NodeNotFound
235 If `source` is not in `G`.
236
237 NetworkXNoPath
238 If no path exists between source and target.
239
240 ValueError
241 If `method` is not among the supported options.
242
243 Examples
244 --------
245 >>> G = nx.path_graph(5)
246 >>> nx.shortest_path_length(G, source=0, target=4)
247 4
248 >>> p = nx.shortest_path_length(G, source=0) # target not specified
249 >>> p[4]
250 4
251 >>> p = nx.shortest_path_length(G, target=4) # source not specified
252 >>> p[0]
253 4
254 >>> p = dict(nx.shortest_path_length(G)) # source,target not specified
255 >>> p[0][4]
256 4
257
258 Notes
259 -----
260 The length of the path is always 1 less than the number of nodes involved
261 in the path since the length measures the number of edges followed.
262
263 For digraphs this returns the shortest directed path length. To find path
264 lengths in the reverse direction use G.reverse(copy=False) first to flip
265 the edge orientation.
266
267 See Also
268 --------
269 all_pairs_shortest_path_length
270 all_pairs_dijkstra_path_length
271 all_pairs_bellman_ford_path_length
272 single_source_shortest_path_length
273 single_source_dijkstra_path_length
274 single_source_bellman_ford_path_length
275 """
276 if method not in ("dijkstra", "bellman-ford"):
277 # so we don't need to check in each branch later
278 raise ValueError(f"method not supported: {method}")
279 method = "unweighted" if weight is None else method
280 if source is None:
281 if target is None:
282 # Find paths between all pairs.
283 if method == "unweighted":
284 paths = nx.all_pairs_shortest_path_length(G)
285 elif method == "dijkstra":
286 paths = nx.all_pairs_dijkstra_path_length(G, weight=weight)
287 else: # method == 'bellman-ford':
288 paths = nx.all_pairs_bellman_ford_path_length(G, weight=weight)
289 else:
290 # Find paths from all nodes co-accessible to the target.
291 if G.is_directed():
292 G = G.reverse(copy=False)
293 if method == "unweighted":
294 path_length = nx.single_source_shortest_path_length
295 paths = path_length(G, target)
296 elif method == "dijkstra":
297 path_length = nx.single_source_dijkstra_path_length
298 paths = path_length(G, target, weight=weight)
299 else: # method == 'bellman-ford':
300 path_length = nx.single_source_bellman_ford_path_length
301 paths = path_length(G, target, weight=weight)
302 else:
303 if target is None:
304 # Find paths to all nodes accessible from the source.
305 if method == "unweighted":
306 paths = nx.single_source_shortest_path_length(G, source)
307 elif method == "dijkstra":
308 path_length = nx.single_source_dijkstra_path_length
309 paths = path_length(G, source, weight=weight)
310 else: # method == 'bellman-ford':
311 path_length = nx.single_source_bellman_ford_path_length
312 paths = path_length(G, source, weight=weight)
313 else:
314 # Find shortest source-target path.
315 if method == "unweighted":
316 p = nx.bidirectional_shortest_path(G, source, target)
317 paths = len(p) - 1
318 elif method == "dijkstra":
319 paths = nx.dijkstra_path_length(G, source, target, weight)
320 else: # method == 'bellman-ford':
321 paths = nx.bellman_ford_path_length(G, source, target, weight)
322 return paths
323
324
325@nx._dispatchable(edge_attrs="weight")
326def average_shortest_path_length(G, weight=None, method=None):
327 r"""Returns the average shortest path length.
328
329 The average shortest path length is
330
331 .. math::
332
333 a =\sum_{\substack{s,t \in V \\ s\neq t}} \frac{d(s, t)}{n(n-1)}
334
335 where `V` is the set of nodes in `G`,
336 `d(s, t)` is the shortest path from `s` to `t`,
337 and `n` is the number of nodes in `G`.
338
339 .. versionchanged:: 3.0
340 An exception is raised for directed graphs that are not strongly
341 connected.
342
343 Parameters
344 ----------
345 G : NetworkX graph
346
347 weight : None, string or function, optional (default = None)
348 If None, every edge has weight/distance/cost 1.
349 If a string, use this edge attribute as the edge weight.
350 Any edge attribute not present defaults to 1.
351 If this is a function, the weight of an edge is the value
352 returned by the function. The function must accept exactly
353 three positional arguments: the two endpoints of an edge and
354 the dictionary of edge attributes for that edge.
355 The function must return a number.
356
357 method : string, optional (default = 'unweighted' or 'dijkstra')
358 The algorithm to use to compute the path lengths.
359 Supported options are 'unweighted', 'dijkstra', 'bellman-ford',
360 'floyd-warshall' and 'floyd-warshall-numpy'.
361 Other method values produce a ValueError.
362 The default method is 'unweighted' if `weight` is None,
363 otherwise the default method is 'dijkstra'.
364
365 Raises
366 ------
367 NetworkXPointlessConcept
368 If `G` is the null graph (that is, the graph on zero nodes).
369
370 NetworkXError
371 If `G` is not connected (or not strongly connected, in the case
372 of a directed graph).
373
374 ValueError
375 If `method` is not among the supported options.
376
377 Examples
378 --------
379 >>> G = nx.path_graph(5)
380 >>> nx.average_shortest_path_length(G)
381 2.0
382
383 For disconnected graphs, you can compute the average shortest path
384 length for each component
385
386 >>> G = nx.Graph([(1, 2), (3, 4)])
387 >>> for C in (G.subgraph(c).copy() for c in nx.connected_components(G)):
388 ... print(nx.average_shortest_path_length(C))
389 1.0
390 1.0
391
392 """
393 single_source_methods = ["unweighted", "dijkstra", "bellman-ford"]
394 all_pairs_methods = ["floyd-warshall", "floyd-warshall-numpy"]
395 supported_methods = single_source_methods + all_pairs_methods
396
397 if method is None:
398 method = "unweighted" if weight is None else "dijkstra"
399 if method not in supported_methods:
400 raise ValueError(f"method not supported: {method}")
401
402 n = len(G)
403 # For the special case of the null graph, raise an exception, since
404 # there are no paths in the null graph.
405 if n == 0:
406 msg = (
407 "the null graph has no paths, thus there is no average shortest path length"
408 )
409 raise nx.NetworkXPointlessConcept(msg)
410 # For the special case of the trivial graph, return zero immediately.
411 if n == 1:
412 return 0
413 # Shortest path length is undefined if the graph is not strongly connected.
414 if G.is_directed() and not nx.is_strongly_connected(G):
415 raise nx.NetworkXError("Graph is not strongly connected.")
416 # Shortest path length is undefined if the graph is not connected.
417 if not G.is_directed() and not nx.is_connected(G):
418 raise nx.NetworkXError("Graph is not connected.")
419
420 # Compute all-pairs shortest paths.
421 def path_length(v):
422 if method == "unweighted":
423 return nx.single_source_shortest_path_length(G, v)
424 elif method == "dijkstra":
425 return nx.single_source_dijkstra_path_length(G, v, weight=weight)
426 elif method == "bellman-ford":
427 return nx.single_source_bellman_ford_path_length(G, v, weight=weight)
428
429 if method in single_source_methods:
430 # Sum the distances for each (ordered) pair of source and target node.
431 s = sum(l for u in G for l in path_length(u).values())
432 else:
433 if method == "floyd-warshall":
434 all_pairs = nx.floyd_warshall(G, weight=weight)
435 s = sum(sum(t.values()) for t in all_pairs.values())
436 elif method == "floyd-warshall-numpy":
437 s = float(nx.floyd_warshall_numpy(G, weight=weight).sum())
438 return s / (n * (n - 1))
439
440
441@nx._dispatchable(edge_attrs="weight")
442def all_shortest_paths(G, source, target, weight=None, method="dijkstra"):
443 """Compute all shortest simple paths in the graph.
444
445 Parameters
446 ----------
447 G : NetworkX graph
448
449 source : node
450 Starting node for path.
451
452 target : node
453 Ending node for path.
454
455 weight : None, string or function, optional (default = None)
456 If None, every edge has weight/distance/cost 1.
457 If a string, use this edge attribute as the edge weight.
458 Any edge attribute not present defaults to 1.
459 If this is a function, the weight of an edge is the value
460 returned by the function. The function must accept exactly
461 three positional arguments: the two endpoints of an edge and
462 the dictionary of edge attributes for that edge.
463 The function must return a number.
464
465 method : string, optional (default = 'dijkstra')
466 The algorithm to use to compute the path lengths.
467 Supported options: 'dijkstra', 'bellman-ford'.
468 Other inputs produce a ValueError.
469 If `weight` is None, unweighted graph methods are used, and this
470 suggestion is ignored.
471
472 Returns
473 -------
474 paths : generator of lists
475 A generator of all paths between source and target.
476
477 Raises
478 ------
479 ValueError
480 If `method` is not among the supported options.
481
482 NetworkXNoPath
483 If `target` cannot be reached from `source`.
484
485 Examples
486 --------
487 >>> G = nx.Graph()
488 >>> nx.add_path(G, [0, 1, 2])
489 >>> nx.add_path(G, [0, 10, 2])
490 >>> print([p for p in nx.all_shortest_paths(G, source=0, target=2)])
491 [[0, 1, 2], [0, 10, 2]]
492
493 Notes
494 -----
495 There may be many shortest paths between the source and target. If G
496 contains zero-weight cycles, this function will not produce all shortest
497 paths because doing so would produce infinitely many paths of unbounded
498 length -- instead, we only produce the shortest simple paths.
499
500 See Also
501 --------
502 shortest_path
503 single_source_shortest_path
504 all_pairs_shortest_path
505 """
506 method = "unweighted" if weight is None else method
507 if method == "unweighted":
508 pred = nx.predecessor(G, source)
509 elif method == "dijkstra":
510 pred, dist = nx.dijkstra_predecessor_and_distance(G, source, weight=weight)
511 elif method == "bellman-ford":
512 pred, dist = nx.bellman_ford_predecessor_and_distance(G, source, weight=weight)
513 else:
514 raise ValueError(f"method not supported: {method}")
515
516 return _build_paths_from_predecessors({source}, target, pred)
517
518
519@nx._dispatchable(edge_attrs="weight")
520def single_source_all_shortest_paths(G, source, weight=None, method="dijkstra"):
521 """Compute all shortest simple paths from the given source in the graph.
522
523 Parameters
524 ----------
525 G : NetworkX graph
526
527 source : node
528 Starting node for path.
529
530 weight : None, string or function, optional (default = None)
531 If None, every edge has weight/distance/cost 1.
532 If a string, use this edge attribute as the edge weight.
533 Any edge attribute not present defaults to 1.
534 If this is a function, the weight of an edge is the value
535 returned by the function. The function must accept exactly
536 three positional arguments: the two endpoints of an edge and
537 the dictionary of edge attributes for that edge.
538 The function must return a number.
539
540 method : string, optional (default = 'dijkstra')
541 The algorithm to use to compute the path lengths.
542 Supported options: 'dijkstra', 'bellman-ford'.
543 Other inputs produce a ValueError.
544 If `weight` is None, unweighted graph methods are used, and this
545 suggestion is ignored.
546
547 Returns
548 -------
549 paths : generator of dictionary
550 A generator of all paths between source and all nodes in the graph.
551
552 Raises
553 ------
554 ValueError
555 If `method` is not among the supported options.
556
557 Examples
558 --------
559 >>> G = nx.Graph()
560 >>> nx.add_path(G, [0, 1, 2, 3, 0])
561 >>> dict(nx.single_source_all_shortest_paths(G, source=0))
562 {0: [[0]], 1: [[0, 1]], 3: [[0, 3]], 2: [[0, 1, 2], [0, 3, 2]]}
563
564 Notes
565 -----
566 There may be many shortest paths between the source and target. If G
567 contains zero-weight cycles, this function will not produce all shortest
568 paths because doing so would produce infinitely many paths of unbounded
569 length -- instead, we only produce the shortest simple paths.
570
571 See Also
572 --------
573 shortest_path
574 all_shortest_paths
575 single_source_shortest_path
576 all_pairs_shortest_path
577 all_pairs_all_shortest_paths
578 """
579 method = "unweighted" if weight is None else method
580 if method == "unweighted":
581 pred = nx.predecessor(G, source)
582 elif method == "dijkstra":
583 pred, dist = nx.dijkstra_predecessor_and_distance(G, source, weight=weight)
584 elif method == "bellman-ford":
585 pred, dist = nx.bellman_ford_predecessor_and_distance(G, source, weight=weight)
586 else:
587 raise ValueError(f"method not supported: {method}")
588 for n in pred:
589 yield n, list(_build_paths_from_predecessors({source}, n, pred))
590
591
592@nx._dispatchable(edge_attrs="weight")
593def all_pairs_all_shortest_paths(G, weight=None, method="dijkstra"):
594 """Compute all shortest paths between all nodes.
595
596 Parameters
597 ----------
598 G : NetworkX graph
599
600 weight : None, string or function, optional (default = None)
601 If None, every edge has weight/distance/cost 1.
602 If a string, use this edge attribute as the edge weight.
603 Any edge attribute not present defaults to 1.
604 If this is a function, the weight of an edge is the value
605 returned by the function. The function must accept exactly
606 three positional arguments: the two endpoints of an edge and
607 the dictionary of edge attributes for that edge.
608 The function must return a number.
609
610 method : string, optional (default = 'dijkstra')
611 The algorithm to use to compute the path lengths.
612 Supported options: 'dijkstra', 'bellman-ford'.
613 Other inputs produce a ValueError.
614 If `weight` is None, unweighted graph methods are used, and this
615 suggestion is ignored.
616
617 Returns
618 -------
619 paths : generator of dictionary
620 Dictionary of arrays, keyed by source and target, of all shortest paths.
621
622 Raises
623 ------
624 ValueError
625 If `method` is not among the supported options.
626
627 Examples
628 --------
629 >>> G = nx.cycle_graph(4)
630 >>> dict(nx.all_pairs_all_shortest_paths(G))[0][2]
631 [[0, 1, 2], [0, 3, 2]]
632 >>> dict(nx.all_pairs_all_shortest_paths(G))[0][3]
633 [[0, 3]]
634
635 Notes
636 -----
637 There may be multiple shortest paths with equal lengths. Unlike
638 all_pairs_shortest_path, this method returns all shortest paths.
639
640 See Also
641 --------
642 all_pairs_shortest_path
643 single_source_all_shortest_paths
644 """
645 for n in G:
646 yield (
647 n,
648 dict(single_source_all_shortest_paths(G, n, weight=weight, method=method)),
649 )
650
651
652def _build_paths_from_predecessors(sources, target, pred):
653 """Compute all simple paths to target, given the predecessors found in
654 pred, terminating when any source in sources is found.
655
656 Parameters
657 ----------
658 sources : set
659 Starting nodes for path.
660
661 target : node
662 Ending node for path.
663
664 pred : dict
665 A dictionary of predecessor lists, keyed by node
666
667 Returns
668 -------
669 paths : generator of lists
670 A generator of all paths between source and target.
671
672 Raises
673 ------
674 NetworkXNoPath
675 If `target` cannot be reached from `source`.
676
677 Notes
678 -----
679 There may be many paths between the sources and target. If there are
680 cycles among the predecessors, this function will not produce all
681 possible paths because doing so would produce infinitely many paths
682 of unbounded length -- instead, we only produce simple paths.
683
684 See Also
685 --------
686 shortest_path
687 single_source_shortest_path
688 all_pairs_shortest_path
689 all_shortest_paths
690 bellman_ford_path
691 """
692 if target not in pred:
693 raise nx.NetworkXNoPath(f"Target {target} cannot be reached from given sources")
694
695 seen = {target}
696 stack = [[target, 0]]
697 top = 0
698 while top >= 0:
699 node, i = stack[top]
700 if node in sources:
701 yield [p for p, n in reversed(stack[: top + 1])]
702 if len(pred[node]) > i:
703 stack[top][1] = i + 1
704 next = pred[node][i]
705 if next in seen:
706 continue
707 else:
708 seen.add(next)
709 top += 1
710 if top == len(stack):
711 stack.append([next, 0])
712 else:
713 stack[top][:] = [next, 0]
714 else:
715 seen.discard(node)
716 top -= 1