beamsearch.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. """Basic algorithms for breadth-first searching the nodes of a graph."""
  2. import networkx as nx
  3. __all__ = ["bfs_beam_edges"]
  4. @nx._dispatchable
  5. def bfs_beam_edges(G, source, value, width=None):
  6. """Iterates over edges in a beam search.
  7. The beam search is a generalized breadth-first search in which only
  8. the "best" *w* neighbors of the current node are enqueued, where *w*
  9. is the beam width and "best" is an application-specific
  10. heuristic. In general, a beam search with a small beam width might
  11. not visit each node in the graph.
  12. .. note::
  13. With the default value of ``width=None`` or `width` greater than the
  14. maximum degree of the graph, this function equates to a slower
  15. version of `~networkx.algorithms.traversal.breadth_first_search.bfs_edges`.
  16. All nodes will be visited, though the order of the reported edges may
  17. vary. In such cases, `value` has no effect - consider using `bfs_edges`
  18. directly instead.
  19. Parameters
  20. ----------
  21. G : NetworkX graph
  22. source : node
  23. Starting node for the breadth-first search; this function
  24. iterates over only those edges in the component reachable from
  25. this node.
  26. value : function
  27. A function that takes a node of the graph as input and returns a
  28. real number indicating how "good" it is. A higher value means it
  29. is more likely to be visited sooner during the search. When
  30. visiting a new node, only the `width` neighbors with the highest
  31. `value` are enqueued (in decreasing order of `value`).
  32. width : int (default = None)
  33. The beam width for the search. This is the number of neighbors
  34. (ordered by `value`) to enqueue when visiting each new node.
  35. Yields
  36. ------
  37. edge
  38. Edges in the beam search starting from `source`, given as a pair
  39. of nodes.
  40. Examples
  41. --------
  42. To give nodes with, for example, a higher centrality precedence
  43. during the search, set the `value` function to return the centrality
  44. value of the node:
  45. >>> G = nx.karate_club_graph()
  46. >>> centrality = nx.eigenvector_centrality(G)
  47. >>> list(nx.bfs_beam_edges(G, source=0, value=centrality.get, width=3))
  48. [(0, 2), (0, 1), (0, 8), (2, 32), (1, 13), (8, 33)]
  49. """
  50. if width is None:
  51. width = len(G)
  52. def successors(v):
  53. """Returns a list of the best neighbors of a node.
  54. `v` is a node in the graph `G`.
  55. The "best" neighbors are chosen according to the `value`
  56. function (higher is better). Only the `width` best neighbors of
  57. `v` are returned.
  58. """
  59. # TODO The Python documentation states that for small values, it
  60. # is better to use `heapq.nlargest`. We should determine the
  61. # threshold at which its better to use `heapq.nlargest()`
  62. # instead of `sorted()[:]` and apply that optimization here.
  63. #
  64. # If `width` is greater than the number of neighbors of `v`, all
  65. # neighbors are returned by the semantics of slicing in
  66. # Python. This occurs in the special case that the user did not
  67. # specify a `width`: in this case all neighbors are always
  68. # returned, so this is just a (slower) implementation of
  69. # `bfs_edges(G, source)` but with a sorted enqueue step.
  70. return iter(sorted(G.neighbors(v), key=value, reverse=True)[:width])
  71. yield from nx.generic_bfs_edges(G, source, successors)