/lib/python2.7/site-packages/networkx/algorithms/components/biconnected.py

https://github.com/jaythaceo/Python-Beginner · Python · 414 lines · 99 code · 6 blank · 309 comment · 30 complexity · 637a3816358f28114708e1a9ea64e621 MD5 · raw file

  1. # -*- coding: utf-8 -*-
  2. """
  3. Biconnected components and articulation points.
  4. """
  5. # Copyright (C) 2011-2013 by
  6. # Aric Hagberg <hagberg@lanl.gov>
  7. # Dan Schult <dschult@colgate.edu>
  8. # Pieter Swart <swart@lanl.gov>
  9. # All rights reserved.
  10. # BSD license.
  11. from itertools import chain
  12. import networkx as nx
  13. from networkx.utils.decorators import not_implemented_for
  14. __author__ = '\n'.join(['Jordi Torrents <jtorrents@milnou.net>',
  15. 'Dan Schult <dschult@colgate.edu>',
  16. 'Aric Hagberg <aric.hagberg@gmail.com>'])
  17. __all__ = ['biconnected_components',
  18. 'biconnected_component_edges',
  19. 'biconnected_component_subgraphs',
  20. 'is_biconnected',
  21. 'articulation_points',
  22. ]
  23. @not_implemented_for('directed')
  24. def is_biconnected(G):
  25. """Return True if the graph is biconnected, False otherwise.
  26. A graph is biconnected if, and only if, it cannot be disconnected by
  27. removing only one node (and all edges incident on that node). If
  28. removing a node increases the number of disconnected components
  29. in the graph, that node is called an articulation point, or cut
  30. vertex. A biconnected graph has no articulation points.
  31. Parameters
  32. ----------
  33. G : NetworkX Graph
  34. An undirected graph.
  35. Returns
  36. -------
  37. biconnected : bool
  38. True if the graph is biconnected, False otherwise.
  39. Raises
  40. ------
  41. NetworkXNotImplemented :
  42. If the input graph is not undirected.
  43. Examples
  44. --------
  45. >>> G=nx.path_graph(4)
  46. >>> print(nx.is_biconnected(G))
  47. False
  48. >>> G.add_edge(0,3)
  49. >>> print(nx.is_biconnected(G))
  50. True
  51. See Also
  52. --------
  53. biconnected_components,
  54. articulation_points,
  55. biconnected_component_edges,
  56. biconnected_component_subgraphs
  57. Notes
  58. -----
  59. The algorithm to find articulation points and biconnected
  60. components is implemented using a non-recursive depth-first-search
  61. (DFS) that keeps track of the highest level that back edges reach
  62. in the DFS tree. A node `n` is an articulation point if, and only
  63. if, there exists a subtree rooted at `n` such that there is no
  64. back edge from any successor of `n` that links to a predecessor of
  65. `n` in the DFS tree. By keeping track of all the edges traversed
  66. by the DFS we can obtain the biconnected components because all
  67. edges of a bicomponent will be traversed consecutively between
  68. articulation points.
  69. References
  70. ----------
  71. .. [1] Hopcroft, J.; Tarjan, R. (1973).
  72. "Efficient algorithms for graph manipulation".
  73. Communications of the ACM 16: 372378. doi:10.1145/362248.362272
  74. """
  75. bcc = list(biconnected_components(G))
  76. if not bcc: # No bicomponents (it could be an empty graph)
  77. return False
  78. return len(bcc[0]) == len(G)
  79. @not_implemented_for('directed')
  80. def biconnected_component_edges(G):
  81. """Return a generator of lists of edges, one list for each biconnected
  82. component of the input graph.
  83. Biconnected components are maximal subgraphs such that the removal of a
  84. node (and all edges incident on that node) will not disconnect the
  85. subgraph. Note that nodes may be part of more than one biconnected
  86. component. Those nodes are articulation points, or cut vertices. However,
  87. each edge belongs to one, and only one, biconnected component.
  88. Notice that by convention a dyad is considered a biconnected component.
  89. Parameters
  90. ----------
  91. G : NetworkX Graph
  92. An undirected graph.
  93. Returns
  94. -------
  95. edges : generator of lists
  96. Generator of lists of edges, one list for each bicomponent.
  97. Raises
  98. ------
  99. NetworkXNotImplemented :
  100. If the input graph is not undirected.
  101. Examples
  102. --------
  103. >>> G = nx.barbell_graph(4,2)
  104. >>> print(nx.is_biconnected(G))
  105. False
  106. >>> components = nx.biconnected_component_edges(G)
  107. >>> G.add_edge(2,8)
  108. >>> print(nx.is_biconnected(G))
  109. True
  110. >>> components = nx.biconnected_component_edges(G)
  111. See Also
  112. --------
  113. is_biconnected,
  114. biconnected_components,
  115. articulation_points,
  116. biconnected_component_subgraphs
  117. Notes
  118. -----
  119. The algorithm to find articulation points and biconnected
  120. components is implemented using a non-recursive depth-first-search
  121. (DFS) that keeps track of the highest level that back edges reach
  122. in the DFS tree. A node `n` is an articulation point if, and only
  123. if, there exists a subtree rooted at `n` such that there is no
  124. back edge from any successor of `n` that links to a predecessor of
  125. `n` in the DFS tree. By keeping track of all the edges traversed
  126. by the DFS we can obtain the biconnected components because all
  127. edges of a bicomponent will be traversed consecutively between
  128. articulation points.
  129. References
  130. ----------
  131. .. [1] Hopcroft, J.; Tarjan, R. (1973).
  132. "Efficient algorithms for graph manipulation".
  133. Communications of the ACM 16: 372378. doi:10.1145/362248.362272
  134. """
  135. for comp in _biconnected_dfs(G,components=True):
  136. yield comp
  137. @not_implemented_for('directed')
  138. def biconnected_components(G):
  139. """Return a generator of sets of nodes, one set for each biconnected
  140. component of the graph
  141. Biconnected components are maximal subgraphs such that the removal of a
  142. node (and all edges incident on that node) will not disconnect the
  143. subgraph. Note that nodes may be part of more than one biconnected
  144. component. Those nodes are articulation points, or cut vertices. The
  145. removal of articulation points will increase the number of connected
  146. components of the graph.
  147. Notice that by convention a dyad is considered a biconnected component.
  148. Parameters
  149. ----------
  150. G : NetworkX Graph
  151. An undirected graph.
  152. Returns
  153. -------
  154. nodes : generator
  155. Generator of sets of nodes, one set for each biconnected component.
  156. Raises
  157. ------
  158. NetworkXNotImplemented :
  159. If the input graph is not undirected.
  160. Examples
  161. --------
  162. >>> G = nx.barbell_graph(4,2)
  163. >>> print(nx.is_biconnected(G))
  164. False
  165. >>> components = nx.biconnected_components(G)
  166. >>> G.add_edge(2,8)
  167. >>> print(nx.is_biconnected(G))
  168. True
  169. >>> components = nx.biconnected_components(G)
  170. See Also
  171. --------
  172. is_biconnected,
  173. articulation_points,
  174. biconnected_component_edges,
  175. biconnected_component_subgraphs
  176. Notes
  177. -----
  178. The algorithm to find articulation points and biconnected
  179. components is implemented using a non-recursive depth-first-search
  180. (DFS) that keeps track of the highest level that back edges reach
  181. in the DFS tree. A node `n` is an articulation point if, and only
  182. if, there exists a subtree rooted at `n` such that there is no
  183. back edge from any successor of `n` that links to a predecessor of
  184. `n` in the DFS tree. By keeping track of all the edges traversed
  185. by the DFS we can obtain the biconnected components because all
  186. edges of a bicomponent will be traversed consecutively between
  187. articulation points.
  188. References
  189. ----------
  190. .. [1] Hopcroft, J.; Tarjan, R. (1973).
  191. "Efficient algorithms for graph manipulation".
  192. Communications of the ACM 16: 372378. doi:10.1145/362248.362272
  193. """
  194. for comp in _biconnected_dfs(G,components=True):
  195. yield set(chain.from_iterable(comp))
  196. @not_implemented_for('directed')
  197. def biconnected_component_subgraphs(G, copy=True):
  198. """Return a generator of graphs, one graph for each biconnected component
  199. of the input graph.
  200. Biconnected components are maximal subgraphs such that the removal of a
  201. node (and all edges incident on that node) will not disconnect the
  202. subgraph. Note that nodes may be part of more than one biconnected
  203. component. Those nodes are articulation points, or cut vertices. The
  204. removal of articulation points will increase the number of connected
  205. components of the graph.
  206. Notice that by convention a dyad is considered a biconnected component.
  207. Parameters
  208. ----------
  209. G : NetworkX Graph
  210. An undirected graph.
  211. Returns
  212. -------
  213. graphs : generator
  214. Generator of graphs, one graph for each biconnected component.
  215. Raises
  216. ------
  217. NetworkXNotImplemented :
  218. If the input graph is not undirected.
  219. Examples
  220. --------
  221. >>> G = nx.barbell_graph(4,2)
  222. >>> print(nx.is_biconnected(G))
  223. False
  224. >>> subgraphs = list(nx.biconnected_component_subgraphs(G))
  225. See Also
  226. --------
  227. is_biconnected,
  228. articulation_points,
  229. biconnected_component_edges,
  230. biconnected_components
  231. Notes
  232. -----
  233. The algorithm to find articulation points and biconnected
  234. components is implemented using a non-recursive depth-first-search
  235. (DFS) that keeps track of the highest level that back edges reach
  236. in the DFS tree. A node `n` is an articulation point if, and only
  237. if, there exists a subtree rooted at `n` such that there is no
  238. back edge from any successor of `n` that links to a predecessor of
  239. `n` in the DFS tree. By keeping track of all the edges traversed
  240. by the DFS we can obtain the biconnected components because all
  241. edges of a bicomponent will be traversed consecutively between
  242. articulation points.
  243. Graph, node, and edge attributes are copied to the subgraphs.
  244. References
  245. ----------
  246. .. [1] Hopcroft, J.; Tarjan, R. (1973).
  247. "Efficient algorithms for graph manipulation".
  248. Communications of the ACM 16: 372378. doi:10.1145/362248.362272
  249. """
  250. for comp_nodes in biconnected_components(G):
  251. if copy:
  252. yield G.subgraph(comp_nodes).copy()
  253. else:
  254. yield G.subgraph(comp_nodes)
  255. @not_implemented_for('directed')
  256. def articulation_points(G):
  257. """Return a generator of articulation points, or cut vertices, of a graph.
  258. An articulation point or cut vertex is any node whose removal (along with
  259. all its incident edges) increases the number of connected components of
  260. a graph. An undirected connected graph without articulation points is
  261. biconnected. Articulation points belong to more than one biconnected
  262. component of a graph.
  263. Notice that by convention a dyad is considered a biconnected component.
  264. Parameters
  265. ----------
  266. G : NetworkX Graph
  267. An undirected graph.
  268. Returns
  269. -------
  270. articulation points : generator
  271. generator of nodes
  272. Raises
  273. ------
  274. NetworkXNotImplemented :
  275. If the input graph is not undirected.
  276. Examples
  277. --------
  278. >>> G = nx.barbell_graph(4,2)
  279. >>> print(nx.is_biconnected(G))
  280. False
  281. >>> list(nx.articulation_points(G))
  282. [6, 5, 4, 3]
  283. >>> G.add_edge(2,8)
  284. >>> print(nx.is_biconnected(G))
  285. True
  286. >>> list(nx.articulation_points(G))
  287. []
  288. See Also
  289. --------
  290. is_biconnected,
  291. biconnected_components,
  292. biconnected_component_edges,
  293. biconnected_component_subgraphs
  294. Notes
  295. -----
  296. The algorithm to find articulation points and biconnected
  297. components is implemented using a non-recursive depth-first-search
  298. (DFS) that keeps track of the highest level that back edges reach
  299. in the DFS tree. A node `n` is an articulation point if, and only
  300. if, there exists a subtree rooted at `n` such that there is no
  301. back edge from any successor of `n` that links to a predecessor of
  302. `n` in the DFS tree. By keeping track of all the edges traversed
  303. by the DFS we can obtain the biconnected components because all
  304. edges of a bicomponent will be traversed consecutively between
  305. articulation points.
  306. References
  307. ----------
  308. .. [1] Hopcroft, J.; Tarjan, R. (1973).
  309. "Efficient algorithms for graph manipulation".
  310. Communications of the ACM 16: 372378. doi:10.1145/362248.362272
  311. """
  312. return _biconnected_dfs(G,components=False)
  313. @not_implemented_for('directed')
  314. def _biconnected_dfs(G, components=True):
  315. # depth-first search algorithm to generate articulation points
  316. # and biconnected components
  317. visited = set()
  318. for start in G:
  319. if start in visited:
  320. continue
  321. discovery = {start:0} # "time" of first discovery of node during search
  322. low = {start:0}
  323. root_children = 0
  324. visited.add(start)
  325. edge_stack = []
  326. stack = [(start, start, iter(G[start]))]
  327. while stack:
  328. grandparent, parent, children = stack[-1]
  329. try:
  330. child = next(children)
  331. if grandparent == child:
  332. continue
  333. if child in visited:
  334. if discovery[child] <= discovery[parent]: # back edge
  335. low[parent] = min(low[parent],discovery[child])
  336. if components:
  337. edge_stack.append((parent,child))
  338. else:
  339. low[child] = discovery[child] = len(discovery)
  340. visited.add(child)
  341. stack.append((parent, child, iter(G[child])))
  342. if components:
  343. edge_stack.append((parent,child))
  344. except StopIteration:
  345. stack.pop()
  346. if len(stack) > 1:
  347. if low[parent] >= discovery[grandparent]:
  348. if components:
  349. ind = edge_stack.index((grandparent,parent))
  350. yield edge_stack[ind:]
  351. edge_stack=edge_stack[:ind]
  352. else:
  353. yield grandparent
  354. low[grandparent] = min(low[parent], low[grandparent])
  355. elif stack: # length 1 so grandparent is root
  356. root_children += 1
  357. if components:
  358. ind = edge_stack.index((grandparent,parent))
  359. yield edge_stack[ind:]
  360. if not components:
  361. # root node is articulation point if it has more than 1 child
  362. if root_children > 1:
  363. yield start