diff --git a/CHANGES b/CHANGES index 886411295..290d98be5 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,7 @@ Pint Changelog - Add conductivity dimension. (#2112) - Add absorbance unit and dimension. (#2114) - Add membrane filtration flux and permeability dimensionality, and shorthand "LMH". (#2116) +- Fix find_shortest_path to use breadth first search (#2146) 0.24.4 (2024-11-07) diff --git a/pint/testsuite/test_util.py b/pint/testsuite/test_util.py index 0a6d357d0..3eb49a471 100644 --- a/pint/testsuite/test_util.py +++ b/pint/testsuite/test_util.py @@ -304,6 +304,21 @@ def test_shortest_path(self): p = find_shortest_path(g, 2, 1) assert p == [2, 1] + def test_shortest_path_densely_connected_2146(self): + import itertools + g = collections.defaultdict(set) + for i, j in itertools.combinations(range(42), 2): + g[i].add(j) + g[j].add(i) + p = find_shortest_path(g, 0, 39) + assert p == [0, 39] + p = find_shortest_path(g, 0, 41) + assert p == [0, 41] + p = find_shortest_path(g, 17, 2) + assert p == [17, 2] + p = find_shortest_path(g, 12, 12) + assert p == [12] + class TestMatrix: def test_matrix_to_string(self): diff --git a/pint/util.py b/pint/util.py index b10ba6d3a..fe3ab43db 100644 --- a/pint/util.py +++ b/pint/util.py @@ -16,6 +16,7 @@ import re import tokenize import types +from collections import deque from collections.abc import Callable, Generator, Hashable, Iterable, Iterator, Mapping from fractions import Fraction from functools import lru_cache, partial @@ -340,7 +341,7 @@ def solve_dependencies( def find_shortest_path( - graph: dict[TH, set[TH]], start: TH, end: TH, path: list[TH] | None = None + graph: dict[TH, set[TH]], start: TH, end: TH ): """Find shortest path between two nodes within a graph. @@ -353,32 +354,29 @@ def find_shortest_path( Starting node. end End node. - path - Path to prepend to the one found. - (default = None, empty path.) Returns ------- list[TH] The shortest path between two nodes. """ - path = (path or []) + [start] + path = [start] if start == end: return path - # TODO: raise ValueError when start not in graph - if start not in graph: - return None - - shortest = None - for node in graph[start]: - if node not in path: - newpath = find_shortest_path(graph, node, end, path) - if newpath: - if not shortest or len(newpath) < len(shortest): - shortest = newpath + fifo = deque() + fifo.append((start, path)) + visited = set() + while fifo: + node, path = fifo.popleft() + visited.add(node) + for adjascent_node in graph[node] - visited: + if adjascent_node == end: + return path + [adjascent_node] + else: + fifo.append((adjascent_node, path + [adjascent_node])) - return shortest + return None def find_connected_nodes(