boundary.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. """Routines to find the boundary of a set of nodes.
  2. An edge boundary is a set of edges, each of which has exactly one
  3. endpoint in a given set of nodes (or, in the case of directed graphs,
  4. the set of edges whose source node is in the set).
  5. A node boundary of a set *S* of nodes is the set of (out-)neighbors of
  6. nodes in *S* that are outside *S*.
  7. """
  8. from itertools import chain
  9. import networkx as nx
  10. __all__ = ["edge_boundary", "node_boundary"]
  11. @nx._dispatch
  12. def edge_boundary(G, nbunch1, nbunch2=None, data=False, keys=False, default=None):
  13. """Returns the edge boundary of `nbunch1`.
  14. The *edge boundary* of a set *S* with respect to a set *T* is the
  15. set of edges (*u*, *v*) such that *u* is in *S* and *v* is in *T*.
  16. If *T* is not specified, it is assumed to be the set of all nodes
  17. not in *S*.
  18. Parameters
  19. ----------
  20. G : NetworkX graph
  21. nbunch1 : iterable
  22. Iterable of nodes in the graph representing the set of nodes
  23. whose edge boundary will be returned. (This is the set *S* from
  24. the definition above.)
  25. nbunch2 : iterable
  26. Iterable of nodes representing the target (or "exterior") set of
  27. nodes. (This is the set *T* from the definition above.) If not
  28. specified, this is assumed to be the set of all nodes in `G`
  29. not in `nbunch1`.
  30. keys : bool
  31. This parameter has the same meaning as in
  32. :meth:`MultiGraph.edges`.
  33. data : bool or object
  34. This parameter has the same meaning as in
  35. :meth:`MultiGraph.edges`.
  36. default : object
  37. This parameter has the same meaning as in
  38. :meth:`MultiGraph.edges`.
  39. Returns
  40. -------
  41. iterator
  42. An iterator over the edges in the boundary of `nbunch1` with
  43. respect to `nbunch2`. If `keys`, `data`, or `default`
  44. are specified and `G` is a multigraph, then edges are returned
  45. with keys and/or data, as in :meth:`MultiGraph.edges`.
  46. Examples
  47. --------
  48. >>> G = nx.wheel_graph(6)
  49. When nbunch2=None:
  50. >>> list(nx.edge_boundary(G, (1, 3)))
  51. [(1, 0), (1, 2), (1, 5), (3, 0), (3, 2), (3, 4)]
  52. When nbunch2 is given:
  53. >>> list(nx.edge_boundary(G, (1, 3), (2, 0)))
  54. [(1, 0), (1, 2), (3, 0), (3, 2)]
  55. Notes
  56. -----
  57. Any element of `nbunch` that is not in the graph `G` will be
  58. ignored.
  59. `nbunch1` and `nbunch2` are usually meant to be disjoint, but in
  60. the interest of speed and generality, that is not required here.
  61. """
  62. nset1 = {n for n in nbunch1 if n in G}
  63. # Here we create an iterator over edges incident to nodes in the set
  64. # `nset1`. The `Graph.edges()` method does not provide a guarantee
  65. # on the orientation of the edges, so our algorithm below must
  66. # handle the case in which exactly one orientation, either (u, v) or
  67. # (v, u), appears in this iterable.
  68. if G.is_multigraph():
  69. edges = G.edges(nset1, data=data, keys=keys, default=default)
  70. else:
  71. edges = G.edges(nset1, data=data, default=default)
  72. # If `nbunch2` is not provided, then it is assumed to be the set
  73. # complement of `nbunch1`. For the sake of efficiency, this is
  74. # implemented by using the `not in` operator, instead of by creating
  75. # an additional set and using the `in` operator.
  76. if nbunch2 is None:
  77. return (e for e in edges if (e[0] in nset1) ^ (e[1] in nset1))
  78. nset2 = set(nbunch2)
  79. return (
  80. e
  81. for e in edges
  82. if (e[0] in nset1 and e[1] in nset2) or (e[1] in nset1 and e[0] in nset2)
  83. )
  84. @nx._dispatch()
  85. def node_boundary(G, nbunch1, nbunch2=None):
  86. """Returns the node boundary of `nbunch1`.
  87. The *node boundary* of a set *S* with respect to a set *T* is the
  88. set of nodes *v* in *T* such that for some *u* in *S*, there is an
  89. edge joining *u* to *v*. If *T* is not specified, it is assumed to
  90. be the set of all nodes not in *S*.
  91. Parameters
  92. ----------
  93. G : NetworkX graph
  94. nbunch1 : iterable
  95. Iterable of nodes in the graph representing the set of nodes
  96. whose node boundary will be returned. (This is the set *S* from
  97. the definition above.)
  98. nbunch2 : iterable
  99. Iterable of nodes representing the target (or "exterior") set of
  100. nodes. (This is the set *T* from the definition above.) If not
  101. specified, this is assumed to be the set of all nodes in `G`
  102. not in `nbunch1`.
  103. Returns
  104. -------
  105. set
  106. The node boundary of `nbunch1` with respect to `nbunch2`.
  107. Examples
  108. --------
  109. >>> G = nx.wheel_graph(6)
  110. When nbunch2=None:
  111. >>> list(nx.node_boundary(G, (3, 4)))
  112. [0, 2, 5]
  113. When nbunch2 is given:
  114. >>> list(nx.node_boundary(G, (3, 4), (0, 1, 5)))
  115. [0, 5]
  116. Notes
  117. -----
  118. Any element of `nbunch` that is not in the graph `G` will be
  119. ignored.
  120. `nbunch1` and `nbunch2` are usually meant to be disjoint, but in
  121. the interest of speed and generality, that is not required here.
  122. """
  123. nset1 = {n for n in nbunch1 if n in G}
  124. bdy = set(chain.from_iterable(G[v] for v in nset1)) - nset1
  125. # If `nbunch2` is not specified, it is assumed to be the set
  126. # complement of `nbunch1`.
  127. if nbunch2 is not None:
  128. bdy &= set(nbunch2)
  129. return bdy