Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/networkx/algorithms/traversal/breadth_first_search.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4f3585e2f14b |
---|---|
1 """Basic algorithms for breadth-first searching the nodes of a graph.""" | |
2 import networkx as nx | |
3 from collections import deque | |
4 | |
5 __all__ = [ | |
6 "bfs_edges", | |
7 "bfs_tree", | |
8 "bfs_predecessors", | |
9 "bfs_successors", | |
10 "descendants_at_distance", | |
11 ] | |
12 | |
13 | |
14 def generic_bfs_edges(G, source, neighbors=None, depth_limit=None, sort_neighbors=None): | |
15 """Iterate over edges in a breadth-first search. | |
16 | |
17 The breadth-first search begins at `source` and enqueues the | |
18 neighbors of newly visited nodes specified by the `neighbors` | |
19 function. | |
20 | |
21 Parameters | |
22 ---------- | |
23 G : NetworkX graph | |
24 | |
25 source : node | |
26 Starting node for the breadth-first search; this function | |
27 iterates over only those edges in the component reachable from | |
28 this node. | |
29 | |
30 neighbors : function | |
31 A function that takes a newly visited node of the graph as input | |
32 and returns an *iterator* (not just a list) of nodes that are | |
33 neighbors of that node. If not specified, this is just the | |
34 ``G.neighbors`` method, but in general it can be any function | |
35 that returns an iterator over some or all of the neighbors of a | |
36 given node, in any order. | |
37 | |
38 depth_limit : int, optional(default=len(G)) | |
39 Specify the maximum search depth | |
40 | |
41 sort_neighbors : function | |
42 A function that takes the list of neighbors of given node as input, and | |
43 returns an *iterator* over these neighbors but with custom ordering. | |
44 | |
45 Yields | |
46 ------ | |
47 edge | |
48 Edges in the breadth-first search starting from `source`. | |
49 | |
50 Examples | |
51 -------- | |
52 >>> G = nx.path_graph(3) | |
53 >>> print(list(nx.bfs_edges(G, 0))) | |
54 [(0, 1), (1, 2)] | |
55 >>> print(list(nx.bfs_edges(G, source=0, depth_limit=1))) | |
56 [(0, 1)] | |
57 | |
58 Notes | |
59 ----- | |
60 This implementation is from `PADS`_, which was in the public domain | |
61 when it was first accessed in July, 2004. The modifications | |
62 to allow depth limits are based on the Wikipedia article | |
63 "`Depth-limited-search`_". | |
64 | |
65 .. _PADS: http://www.ics.uci.edu/~eppstein/PADS/BFS.py | |
66 .. _Depth-limited-search: https://en.wikipedia.org/wiki/Depth-limited_search | |
67 """ | |
68 if callable(sort_neighbors): | |
69 _neighbors = neighbors | |
70 neighbors = lambda node: iter(sort_neighbors(_neighbors(node))) | |
71 | |
72 visited = {source} | |
73 if depth_limit is None: | |
74 depth_limit = len(G) | |
75 queue = deque([(source, depth_limit, neighbors(source))]) | |
76 while queue: | |
77 parent, depth_now, children = queue[0] | |
78 try: | |
79 child = next(children) | |
80 if child not in visited: | |
81 yield parent, child | |
82 visited.add(child) | |
83 if depth_now > 1: | |
84 queue.append((child, depth_now - 1, neighbors(child))) | |
85 except StopIteration: | |
86 queue.popleft() | |
87 | |
88 | |
89 def bfs_edges(G, source, reverse=False, depth_limit=None, sort_neighbors=None): | |
90 """Iterate over edges in a breadth-first-search starting at source. | |
91 | |
92 Parameters | |
93 ---------- | |
94 G : NetworkX graph | |
95 | |
96 source : node | |
97 Specify starting node for breadth-first search; this function | |
98 iterates over only those edges in the component reachable from | |
99 this node. | |
100 | |
101 reverse : bool, optional | |
102 If True traverse a directed graph in the reverse direction | |
103 | |
104 depth_limit : int, optional(default=len(G)) | |
105 Specify the maximum search depth | |
106 | |
107 sort_neighbors : function | |
108 A function that takes the list of neighbors of given node as input, and | |
109 returns an *iterator* over these neighbors but with custom ordering. | |
110 | |
111 Returns | |
112 ------- | |
113 edges: generator | |
114 A generator of edges in the breadth-first-search. | |
115 | |
116 Examples | |
117 -------- | |
118 To get the edges in a breadth-first search:: | |
119 | |
120 >>> G = nx.path_graph(3) | |
121 >>> list(nx.bfs_edges(G, 0)) | |
122 [(0, 1), (1, 2)] | |
123 >>> list(nx.bfs_edges(G, source=0, depth_limit=1)) | |
124 [(0, 1)] | |
125 | |
126 To get the nodes in a breadth-first search order:: | |
127 | |
128 >>> G = nx.path_graph(3) | |
129 >>> root = 2 | |
130 >>> edges = nx.bfs_edges(G, root) | |
131 >>> nodes = [root] + [v for u, v in edges] | |
132 >>> nodes | |
133 [2, 1, 0] | |
134 | |
135 Notes | |
136 ----- | |
137 The naming of this function is very similar to edge_bfs. The difference | |
138 is that 'edge_bfs' yields edges even if they extend back to an already | |
139 explored node while 'bfs_edges' yields the edges of the tree that results | |
140 from a breadth-first-search (BFS) so no edges are reported if they extend | |
141 to already explored nodes. That means 'edge_bfs' reports all edges while | |
142 'bfs_edges' only reports those traversed by a node-based BFS. Yet another | |
143 description is that 'bfs_edges' reports the edges traversed during BFS | |
144 while 'edge_bfs' reports all edges in the order they are explored. | |
145 | |
146 Based on http://www.ics.uci.edu/~eppstein/PADS/BFS.py. | |
147 by D. Eppstein, July 2004. The modifications | |
148 to allow depth limits based on the Wikipedia article | |
149 "`Depth-limited-search`_". | |
150 | |
151 .. _Depth-limited-search: https://en.wikipedia.org/wiki/Depth-limited_search | |
152 | |
153 See Also | |
154 -------- | |
155 bfs_tree | |
156 dfs_edges | |
157 edge_bfs | |
158 | |
159 """ | |
160 if reverse and G.is_directed(): | |
161 successors = G.predecessors | |
162 else: | |
163 successors = G.neighbors | |
164 yield from generic_bfs_edges(G, source, successors, depth_limit, sort_neighbors) | |
165 | |
166 | |
167 def bfs_tree(G, source, reverse=False, depth_limit=None, sort_neighbors=None): | |
168 """Returns an oriented tree constructed from of a breadth-first-search | |
169 starting at source. | |
170 | |
171 Parameters | |
172 ---------- | |
173 G : NetworkX graph | |
174 | |
175 source : node | |
176 Specify starting node for breadth-first search | |
177 | |
178 reverse : bool, optional | |
179 If True traverse a directed graph in the reverse direction | |
180 | |
181 depth_limit : int, optional(default=len(G)) | |
182 Specify the maximum search depth | |
183 | |
184 sort_neighbors : function | |
185 A function that takes the list of neighbors of given node as input, and | |
186 returns an *iterator* over these neighbors but with custom ordering. | |
187 | |
188 Returns | |
189 ------- | |
190 T: NetworkX DiGraph | |
191 An oriented tree | |
192 | |
193 Examples | |
194 -------- | |
195 >>> G = nx.path_graph(3) | |
196 >>> print(list(nx.bfs_tree(G, 1).edges())) | |
197 [(1, 0), (1, 2)] | |
198 >>> H = nx.Graph() | |
199 >>> nx.add_path(H, [0, 1, 2, 3, 4, 5, 6]) | |
200 >>> nx.add_path(H, [2, 7, 8, 9, 10]) | |
201 >>> print(sorted(list(nx.bfs_tree(H, source=3, depth_limit=3).edges()))) | |
202 [(1, 0), (2, 1), (2, 7), (3, 2), (3, 4), (4, 5), (5, 6), (7, 8)] | |
203 | |
204 | |
205 Notes | |
206 ----- | |
207 Based on http://www.ics.uci.edu/~eppstein/PADS/BFS.py | |
208 by D. Eppstein, July 2004. The modifications | |
209 to allow depth limits based on the Wikipedia article | |
210 "`Depth-limited-search`_". | |
211 | |
212 .. _Depth-limited-search: https://en.wikipedia.org/wiki/Depth-limited_search | |
213 | |
214 See Also | |
215 -------- | |
216 dfs_tree | |
217 bfs_edges | |
218 edge_bfs | |
219 """ | |
220 T = nx.DiGraph() | |
221 T.add_node(source) | |
222 edges_gen = bfs_edges( | |
223 G, | |
224 source, | |
225 reverse=reverse, | |
226 depth_limit=depth_limit, | |
227 sort_neighbors=sort_neighbors, | |
228 ) | |
229 T.add_edges_from(edges_gen) | |
230 return T | |
231 | |
232 | |
233 def bfs_predecessors(G, source, depth_limit=None, sort_neighbors=None): | |
234 """Returns an iterator of predecessors in breadth-first-search from source. | |
235 | |
236 Parameters | |
237 ---------- | |
238 G : NetworkX graph | |
239 | |
240 source : node | |
241 Specify starting node for breadth-first search | |
242 | |
243 depth_limit : int, optional(default=len(G)) | |
244 Specify the maximum search depth | |
245 | |
246 sort_neighbors : function | |
247 A function that takes the list of neighbors of given node as input, and | |
248 returns an *iterator* over these neighbors but with custom ordering. | |
249 | |
250 Returns | |
251 ------- | |
252 pred: iterator | |
253 (node, predecessors) iterator where predecessors is the list of | |
254 predecessors of the node. | |
255 | |
256 Examples | |
257 -------- | |
258 >>> G = nx.path_graph(3) | |
259 >>> print(dict(nx.bfs_predecessors(G, 0))) | |
260 {1: 0, 2: 1} | |
261 >>> H = nx.Graph() | |
262 >>> H.add_edges_from([(0, 1), (0, 2), (1, 3), (1, 4), (2, 5), (2, 6)]) | |
263 >>> print(dict(nx.bfs_predecessors(H, 0))) | |
264 {1: 0, 2: 0, 3: 1, 4: 1, 5: 2, 6: 2} | |
265 >>> M = nx.Graph() | |
266 >>> nx.add_path(M, [0, 1, 2, 3, 4, 5, 6]) | |
267 >>> nx.add_path(M, [2, 7, 8, 9, 10]) | |
268 >>> print(sorted(nx.bfs_predecessors(M, source=1, depth_limit=3))) | |
269 [(0, 1), (2, 1), (3, 2), (4, 3), (7, 2), (8, 7)] | |
270 | |
271 | |
272 Notes | |
273 ----- | |
274 Based on http://www.ics.uci.edu/~eppstein/PADS/BFS.py | |
275 by D. Eppstein, July 2004. The modifications | |
276 to allow depth limits based on the Wikipedia article | |
277 "`Depth-limited-search`_". | |
278 | |
279 .. _Depth-limited-search: https://en.wikipedia.org/wiki/Depth-limited_search | |
280 | |
281 See Also | |
282 -------- | |
283 bfs_tree | |
284 bfs_edges | |
285 edge_bfs | |
286 """ | |
287 for s, t in bfs_edges( | |
288 G, source, depth_limit=depth_limit, sort_neighbors=sort_neighbors | |
289 ): | |
290 yield (t, s) | |
291 | |
292 | |
293 def bfs_successors(G, source, depth_limit=None, sort_neighbors=None): | |
294 """Returns an iterator of successors in breadth-first-search from source. | |
295 | |
296 Parameters | |
297 ---------- | |
298 G : NetworkX graph | |
299 | |
300 source : node | |
301 Specify starting node for breadth-first search | |
302 | |
303 depth_limit : int, optional(default=len(G)) | |
304 Specify the maximum search depth | |
305 | |
306 sort_neighbors : function | |
307 A function that takes the list of neighbors of given node as input, and | |
308 returns an *iterator* over these neighbors but with custom ordering. | |
309 | |
310 Returns | |
311 ------- | |
312 succ: iterator | |
313 (node, successors) iterator where successors is the list of | |
314 successors of the node. | |
315 | |
316 Examples | |
317 -------- | |
318 >>> G = nx.path_graph(3) | |
319 >>> print(dict(nx.bfs_successors(G, 0))) | |
320 {0: [1], 1: [2]} | |
321 >>> H = nx.Graph() | |
322 >>> H.add_edges_from([(0, 1), (0, 2), (1, 3), (1, 4), (2, 5), (2, 6)]) | |
323 >>> print(dict(nx.bfs_successors(H, 0))) | |
324 {0: [1, 2], 1: [3, 4], 2: [5, 6]} | |
325 >>> G = nx.Graph() | |
326 >>> nx.add_path(G, [0, 1, 2, 3, 4, 5, 6]) | |
327 >>> nx.add_path(G, [2, 7, 8, 9, 10]) | |
328 >>> print(dict(nx.bfs_successors(G, source=1, depth_limit=3))) | |
329 {1: [0, 2], 2: [3, 7], 3: [4], 7: [8]} | |
330 | |
331 | |
332 Notes | |
333 ----- | |
334 Based on http://www.ics.uci.edu/~eppstein/PADS/BFS.py | |
335 by D. Eppstein, July 2004.The modifications | |
336 to allow depth limits based on the Wikipedia article | |
337 "`Depth-limited-search`_". | |
338 | |
339 .. _Depth-limited-search: https://en.wikipedia.org/wiki/Depth-limited_search | |
340 | |
341 See Also | |
342 -------- | |
343 bfs_tree | |
344 bfs_edges | |
345 edge_bfs | |
346 """ | |
347 parent = source | |
348 children = [] | |
349 for p, c in bfs_edges( | |
350 G, source, depth_limit=depth_limit, sort_neighbors=sort_neighbors | |
351 ): | |
352 if p == parent: | |
353 children.append(c) | |
354 continue | |
355 yield (parent, children) | |
356 children = [c] | |
357 parent = p | |
358 yield (parent, children) | |
359 | |
360 | |
361 def descendants_at_distance(G, source, distance): | |
362 """Returns all nodes at a fixed `distance` from `source` in `G`. | |
363 | |
364 Parameters | |
365 ---------- | |
366 G : NetworkX DiGraph | |
367 A directed graph | |
368 source : node in `G` | |
369 distance : the distance of the wanted nodes from `source` | |
370 | |
371 Returns | |
372 ------- | |
373 set() | |
374 The descendants of `source` in `G` at the given `distance` from `source` | |
375 """ | |
376 if not G.has_node(source): | |
377 raise nx.NetworkXError(f"The node {source} is not in the graph.") | |
378 current_distance = 0 | |
379 queue = {source} | |
380 visited = {source} | |
381 | |
382 # this is basically BFS, except that the queue only stores the nodes at | |
383 # current_distance from source at each iteration | |
384 while queue: | |
385 if current_distance == distance: | |
386 return queue | |
387 | |
388 current_distance += 1 | |
389 | |
390 next_vertices = set() | |
391 for vertex in queue: | |
392 for child in G[vertex]: | |
393 if child not in visited: | |
394 visited.add(child) | |
395 next_vertices.add(child) | |
396 | |
397 queue = next_vertices | |
398 | |
399 return set() |