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