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()