Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/networkx/algorithms/shortest_paths/astar.py: 15%

59 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-10-20 07:00 +0000

1"""Shortest paths and path lengths using the A* ("A star") algorithm. 

2""" 

3from heapq import heappop, heappush 

4from itertools import count 

5 

6import networkx as nx 

7from networkx.algorithms.shortest_paths.weighted import _weight_function 

8 

9__all__ = ["astar_path", "astar_path_length"] 

10 

11 

12@nx._dispatch(edge_attrs="weight", preserve_node_attrs="heuristic") 

13def astar_path(G, source, target, heuristic=None, weight="weight"): 

14 """Returns a list of nodes in a shortest path between source and target 

15 using the A* ("A-star") algorithm. 

16 

17 There may be more than one shortest path. This returns only one. 

18 

19 Parameters 

20 ---------- 

21 G : NetworkX graph 

22 

23 source : node 

24 Starting node for path 

25 

26 target : node 

27 Ending node for path 

28 

29 heuristic : function 

30 A function to evaluate the estimate of the distance 

31 from the a node to the target. The function takes 

32 two nodes arguments and must return a number. 

33 If the heuristic is inadmissible (if it might 

34 overestimate the cost of reaching the goal from a node), 

35 the result may not be a shortest path. 

36 The algorithm does not support updating heuristic 

37 values for the same node due to caching the first 

38 heuristic calculation per node. 

39 

40 weight : string or function 

41 If this is a string, then edge weights will be accessed via the 

42 edge attribute with this key (that is, the weight of the edge 

43 joining `u` to `v` will be ``G.edges[u, v][weight]``). If no 

44 such edge attribute exists, the weight of the edge is assumed to 

45 be one. 

46 If this is a function, the weight of an edge is the value 

47 returned by the function. The function must accept exactly three 

48 positional arguments: the two endpoints of an edge and the 

49 dictionary of edge attributes for that edge. The function must 

50 return a number or None to indicate a hidden edge. 

51 

52 Raises 

53 ------ 

54 NetworkXNoPath 

55 If no path exists between source and target. 

56 

57 Examples 

58 -------- 

59 >>> G = nx.path_graph(5) 

60 >>> print(nx.astar_path(G, 0, 4)) 

61 [0, 1, 2, 3, 4] 

62 >>> G = nx.grid_graph(dim=[3, 3]) # nodes are two-tuples (x,y) 

63 >>> nx.set_edge_attributes(G, {e: e[1][0] * 2 for e in G.edges()}, "cost") 

64 >>> def dist(a, b): 

65 ... (x1, y1) = a 

66 ... (x2, y2) = b 

67 ... return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5 

68 >>> print(nx.astar_path(G, (0, 0), (2, 2), heuristic=dist, weight="cost")) 

69 [(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)] 

70 

71 Notes 

72 ----- 

73 Edge weight attributes must be numerical. 

74 Distances are calculated as sums of weighted edges traversed. 

75 

76 The weight function can be used to hide edges by returning None. 

77 So ``weight = lambda u, v, d: 1 if d['color']=="red" else None`` 

78 will find the shortest red path. 

79 

80 See Also 

81 -------- 

82 shortest_path, dijkstra_path 

83 

84 """ 

85 if source not in G or target not in G: 

86 msg = f"Either source {source} or target {target} is not in G" 

87 raise nx.NodeNotFound(msg) 

88 

89 if heuristic is None: 

90 # The default heuristic is h=0 - same as Dijkstra's algorithm 

91 def heuristic(u, v): 

92 return 0 

93 

94 push = heappush 

95 pop = heappop 

96 weight = _weight_function(G, weight) 

97 

98 G_succ = G._adj # For speed-up (and works for both directed and undirected graphs) 

99 

100 # The queue stores priority, node, cost to reach, and parent. 

101 # Uses Python heapq to keep in priority order. 

102 # Add a counter to the queue to prevent the underlying heap from 

103 # attempting to compare the nodes themselves. The hash breaks ties in the 

104 # priority and is guaranteed unique for all nodes in the graph. 

105 c = count() 

106 queue = [(0, next(c), source, 0, None)] 

107 

108 # Maps enqueued nodes to distance of discovered paths and the 

109 # computed heuristics to target. We avoid computing the heuristics 

110 # more than once and inserting the node into the queue too many times. 

111 enqueued = {} 

112 # Maps explored nodes to parent closest to the source. 

113 explored = {} 

114 

115 while queue: 

116 # Pop the smallest item from queue. 

117 _, __, curnode, dist, parent = pop(queue) 

118 

119 if curnode == target: 

120 path = [curnode] 

121 node = parent 

122 while node is not None: 

123 path.append(node) 

124 node = explored[node] 

125 path.reverse() 

126 return path 

127 

128 if curnode in explored: 

129 # Do not override the parent of starting node 

130 if explored[curnode] is None: 

131 continue 

132 

133 # Skip bad paths that were enqueued before finding a better one 

134 qcost, h = enqueued[curnode] 

135 if qcost < dist: 

136 continue 

137 

138 explored[curnode] = parent 

139 

140 for neighbor, w in G_succ[curnode].items(): 

141 cost = weight(curnode, neighbor, w) 

142 if cost is None: 

143 continue 

144 ncost = dist + cost 

145 if neighbor in enqueued: 

146 qcost, h = enqueued[neighbor] 

147 # if qcost <= ncost, a less costly path from the 

148 # neighbor to the source was already determined. 

149 # Therefore, we won't attempt to push this neighbor 

150 # to the queue 

151 if qcost <= ncost: 

152 continue 

153 else: 

154 h = heuristic(neighbor, target) 

155 enqueued[neighbor] = ncost, h 

156 push(queue, (ncost + h, next(c), neighbor, ncost, curnode)) 

157 

158 raise nx.NetworkXNoPath(f"Node {target} not reachable from {source}") 

159 

160 

161@nx._dispatch(edge_attrs="weight", preserve_node_attrs="heuristic") 

162def astar_path_length(G, source, target, heuristic=None, weight="weight"): 

163 """Returns the length of the shortest path between source and target using 

164 the A* ("A-star") algorithm. 

165 

166 Parameters 

167 ---------- 

168 G : NetworkX graph 

169 

170 source : node 

171 Starting node for path 

172 

173 target : node 

174 Ending node for path 

175 

176 heuristic : function 

177 A function to evaluate the estimate of the distance 

178 from the a node to the target. The function takes 

179 two nodes arguments and must return a number. 

180 If the heuristic is inadmissible (if it might 

181 overestimate the cost of reaching the goal from a node), 

182 the result may not be a shortest path. 

183 The algorithm does not support updating heuristic 

184 values for the same node due to caching the first 

185 heuristic calculation per node. 

186 

187 weight : string or function 

188 If this is a string, then edge weights will be accessed via the 

189 edge attribute with this key (that is, the weight of the edge 

190 joining `u` to `v` will be ``G.edges[u, v][weight]``). If no 

191 such edge attribute exists, the weight of the edge is assumed to 

192 be one. 

193 If this is a function, the weight of an edge is the value 

194 returned by the function. The function must accept exactly three 

195 positional arguments: the two endpoints of an edge and the 

196 dictionary of edge attributes for that edge. The function must 

197 return a number or None to indicate a hidden edge. 

198 Raises 

199 ------ 

200 NetworkXNoPath 

201 If no path exists between source and target. 

202 

203 See Also 

204 -------- 

205 astar_path 

206 

207 """ 

208 if source not in G or target not in G: 

209 msg = f"Either source {source} or target {target} is not in G" 

210 raise nx.NodeNotFound(msg) 

211 

212 weight = _weight_function(G, weight) 

213 path = astar_path(G, source, target, heuristic, weight) 

214 return sum(weight(u, v, G[u][v]) for u, v in zip(path[:-1], path[1:]))