hybrid.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. """
  2. Provides functions for finding and testing for locally `(k, l)`-connected
  3. graphs.
  4. """
  5. import copy
  6. import networkx as nx
  7. __all__ = ["kl_connected_subgraph", "is_kl_connected"]
  8. @nx._dispatchable(returns_graph=True)
  9. def kl_connected_subgraph(G, k, l, low_memory=False, same_as_graph=False):
  10. """Returns the maximum locally `(k, l)`-connected subgraph of `G`.
  11. A graph is locally `(k, l)`-connected if for each edge `(u, v)` in the
  12. graph there are at least `l` edge-disjoint paths of length at most `k`
  13. joining `u` to `v`.
  14. Parameters
  15. ----------
  16. G : NetworkX graph
  17. The graph in which to find a maximum locally `(k, l)`-connected
  18. subgraph.
  19. k : integer
  20. The maximum length of paths to consider. A higher number means a looser
  21. connectivity requirement.
  22. l : integer
  23. The number of edge-disjoint paths. A higher number means a stricter
  24. connectivity requirement.
  25. low_memory : bool
  26. If this is True, this function uses an algorithm that uses slightly
  27. more time but less memory.
  28. same_as_graph : bool
  29. If True then return a tuple of the form `(H, is_same)`,
  30. where `H` is the maximum locally `(k, l)`-connected subgraph and
  31. `is_same` is a Boolean representing whether `G` is locally `(k,
  32. l)`-connected (and hence, whether `H` is simply a copy of the input
  33. graph `G`).
  34. Returns
  35. -------
  36. NetworkX graph or two-tuple
  37. If `same_as_graph` is True, then this function returns a
  38. two-tuple as described above. Otherwise, it returns only the maximum
  39. locally `(k, l)`-connected subgraph.
  40. See also
  41. --------
  42. is_kl_connected
  43. References
  44. ----------
  45. .. [1] Chung, Fan and Linyuan Lu. "The Small World Phenomenon in Hybrid
  46. Power Law Graphs." *Complex Networks*. Springer Berlin Heidelberg,
  47. 2004. 89--104.
  48. """
  49. H = copy.deepcopy(G) # subgraph we construct by removing from G
  50. graphOK = True
  51. deleted_some = True # hack to start off the while loop
  52. while deleted_some:
  53. deleted_some = False
  54. # We use `for edge in list(H.edges()):` instead of
  55. # `for edge in H.edges():` because we edit the graph `H` in
  56. # the loop. Hence using an iterator will result in
  57. # `RuntimeError: dictionary changed size during iteration`
  58. for edge in list(H.edges()):
  59. (u, v) = edge
  60. # Get copy of graph needed for this search
  61. if low_memory:
  62. verts = {u, v}
  63. for i in range(k):
  64. for w in verts.copy():
  65. verts.update(G[w])
  66. G2 = G.subgraph(verts).copy()
  67. else:
  68. G2 = copy.deepcopy(G)
  69. ###
  70. path = [u, v]
  71. cnt = 0
  72. accept = 0
  73. while path:
  74. cnt += 1 # Found a path
  75. if cnt >= l:
  76. accept = 1
  77. break
  78. # record edges along this graph
  79. prev = u
  80. for w in path:
  81. if prev != w:
  82. G2.remove_edge(prev, w)
  83. prev = w
  84. # path = shortest_path(G2, u, v, k) # ??? should "Cutoff" be k+1?
  85. try:
  86. path = nx.shortest_path(G2, u, v) # ??? should "Cutoff" be k+1?
  87. except nx.NetworkXNoPath:
  88. path = False
  89. # No Other Paths
  90. if accept == 0:
  91. H.remove_edge(u, v)
  92. deleted_some = True
  93. if graphOK:
  94. graphOK = False
  95. # We looked through all edges and removed none of them.
  96. # So, H is the maximal (k,l)-connected subgraph of G
  97. if same_as_graph:
  98. return (H, graphOK)
  99. return H
  100. @nx._dispatchable
  101. def is_kl_connected(G, k, l, low_memory=False):
  102. """Returns True if and only if `G` is locally `(k, l)`-connected.
  103. A graph is locally `(k, l)`-connected if for each edge `(u, v)` in the
  104. graph there are at least `l` edge-disjoint paths of length at most `k`
  105. joining `u` to `v`.
  106. Parameters
  107. ----------
  108. G : NetworkX graph
  109. The graph to test for local `(k, l)`-connectedness.
  110. k : integer
  111. The maximum length of paths to consider. A higher number means a looser
  112. connectivity requirement.
  113. l : integer
  114. The number of edge-disjoint paths. A higher number means a stricter
  115. connectivity requirement.
  116. low_memory : bool
  117. If this is True, this function uses an algorithm that uses slightly
  118. more time but less memory.
  119. Returns
  120. -------
  121. bool
  122. Whether the graph is locally `(k, l)`-connected subgraph.
  123. See also
  124. --------
  125. kl_connected_subgraph
  126. References
  127. ----------
  128. .. [1] Chung, Fan and Linyuan Lu. "The Small World Phenomenon in Hybrid
  129. Power Law Graphs." *Complex Networks*. Springer Berlin Heidelberg,
  130. 2004. 89--104.
  131. """
  132. graphOK = True
  133. for edge in G.edges():
  134. (u, v) = edge
  135. # Get copy of graph needed for this search
  136. if low_memory:
  137. verts = {u, v}
  138. for i in range(k):
  139. [verts.update(G.neighbors(w)) for w in verts.copy()]
  140. G2 = G.subgraph(verts)
  141. else:
  142. G2 = copy.deepcopy(G)
  143. ###
  144. path = [u, v]
  145. cnt = 0
  146. accept = 0
  147. while path:
  148. cnt += 1 # Found a path
  149. if cnt >= l:
  150. accept = 1
  151. break
  152. # record edges along this graph
  153. prev = u
  154. for w in path:
  155. if w != prev:
  156. G2.remove_edge(prev, w)
  157. prev = w
  158. # path = shortest_path(G2, u, v, k) # ??? should "Cutoff" be k+1?
  159. try:
  160. path = nx.shortest_path(G2, u, v) # ??? should "Cutoff" be k+1?
  161. except nx.NetworkXNoPath:
  162. path = False
  163. # No Other Paths
  164. if accept == 0:
  165. graphOK = False
  166. break
  167. # return status
  168. return graphOK