test_cuts.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. import pytest
  2. import networkx as nx
  3. from networkx.algorithms import flow
  4. from networkx.algorithms.connectivity import minimum_st_edge_cut, minimum_st_node_cut
  5. from networkx.utils import arbitrary_element
  6. flow_funcs = [
  7. flow.boykov_kolmogorov,
  8. flow.dinitz,
  9. flow.edmonds_karp,
  10. flow.preflow_push,
  11. flow.shortest_augmenting_path,
  12. ]
  13. # Tests for node and edge cutsets
  14. def _generate_no_biconnected(max_attempts=50):
  15. attempts = 0
  16. while True:
  17. G = nx.fast_gnp_random_graph(100, 0.0575, seed=42)
  18. if nx.is_connected(G) and not nx.is_biconnected(G):
  19. attempts = 0
  20. yield G
  21. else:
  22. if attempts >= max_attempts:
  23. msg = f"Tried {attempts} times: no suitable Graph."
  24. raise Exception(msg)
  25. else:
  26. attempts += 1
  27. def test_articulation_points():
  28. Ggen = _generate_no_biconnected()
  29. for flow_func in flow_funcs:
  30. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  31. for i in range(1): # change 1 to 3 or more for more realizations.
  32. G = next(Ggen)
  33. cut = nx.minimum_node_cut(G, flow_func=flow_func)
  34. assert len(cut) == 1, errmsg
  35. assert cut.pop() in set(nx.articulation_points(G)), errmsg
  36. def test_brandes_erlebach_book():
  37. # Figure 1 chapter 7: Connectivity
  38. # http://www.informatik.uni-augsburg.de/thi/personen/kammer/Graph_Connectivity.pdf
  39. G = nx.Graph()
  40. G.add_edges_from(
  41. [
  42. (1, 2),
  43. (1, 3),
  44. (1, 4),
  45. (1, 5),
  46. (2, 3),
  47. (2, 6),
  48. (3, 4),
  49. (3, 6),
  50. (4, 6),
  51. (4, 7),
  52. (5, 7),
  53. (6, 8),
  54. (6, 9),
  55. (7, 8),
  56. (7, 10),
  57. (8, 11),
  58. (9, 10),
  59. (9, 11),
  60. (10, 11),
  61. ]
  62. )
  63. for flow_func in flow_funcs:
  64. kwargs = {"flow_func": flow_func}
  65. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  66. # edge cutsets
  67. assert 3 == len(nx.minimum_edge_cut(G, 1, 11, **kwargs)), errmsg
  68. edge_cut = nx.minimum_edge_cut(G, **kwargs)
  69. # Node 5 has only two edges
  70. assert 2 == len(edge_cut), errmsg
  71. H = G.copy()
  72. H.remove_edges_from(edge_cut)
  73. assert not nx.is_connected(H), errmsg
  74. # node cuts
  75. assert {6, 7} == minimum_st_node_cut(G, 1, 11, **kwargs), errmsg
  76. assert {6, 7} == nx.minimum_node_cut(G, 1, 11, **kwargs), errmsg
  77. node_cut = nx.minimum_node_cut(G, **kwargs)
  78. assert 2 == len(node_cut), errmsg
  79. H = G.copy()
  80. H.remove_nodes_from(node_cut)
  81. assert not nx.is_connected(H), errmsg
  82. def test_white_harary_paper():
  83. # Figure 1b white and harary (2001)
  84. # https://doi.org/10.1111/0081-1750.00098
  85. # A graph with high adhesion (edge connectivity) and low cohesion
  86. # (node connectivity)
  87. G = nx.disjoint_union(nx.complete_graph(4), nx.complete_graph(4))
  88. G.remove_node(7)
  89. for i in range(4, 7):
  90. G.add_edge(0, i)
  91. G = nx.disjoint_union(G, nx.complete_graph(4))
  92. G.remove_node(G.order() - 1)
  93. for i in range(7, 10):
  94. G.add_edge(0, i)
  95. for flow_func in flow_funcs:
  96. kwargs = {"flow_func": flow_func}
  97. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  98. # edge cuts
  99. edge_cut = nx.minimum_edge_cut(G, **kwargs)
  100. assert 3 == len(edge_cut), errmsg
  101. H = G.copy()
  102. H.remove_edges_from(edge_cut)
  103. assert not nx.is_connected(H), errmsg
  104. # node cuts
  105. node_cut = nx.minimum_node_cut(G, **kwargs)
  106. assert {0} == node_cut, errmsg
  107. H = G.copy()
  108. H.remove_nodes_from(node_cut)
  109. assert not nx.is_connected(H), errmsg
  110. def test_petersen_cutset():
  111. G = nx.petersen_graph()
  112. for flow_func in flow_funcs:
  113. kwargs = {"flow_func": flow_func}
  114. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  115. # edge cuts
  116. edge_cut = nx.minimum_edge_cut(G, **kwargs)
  117. assert 3 == len(edge_cut), errmsg
  118. H = G.copy()
  119. H.remove_edges_from(edge_cut)
  120. assert not nx.is_connected(H), errmsg
  121. # node cuts
  122. node_cut = nx.minimum_node_cut(G, **kwargs)
  123. assert 3 == len(node_cut), errmsg
  124. H = G.copy()
  125. H.remove_nodes_from(node_cut)
  126. assert not nx.is_connected(H), errmsg
  127. def test_octahedral_cutset():
  128. G = nx.octahedral_graph()
  129. for flow_func in flow_funcs:
  130. kwargs = {"flow_func": flow_func}
  131. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  132. # edge cuts
  133. edge_cut = nx.minimum_edge_cut(G, **kwargs)
  134. assert 4 == len(edge_cut), errmsg
  135. H = G.copy()
  136. H.remove_edges_from(edge_cut)
  137. assert not nx.is_connected(H), errmsg
  138. # node cuts
  139. node_cut = nx.minimum_node_cut(G, **kwargs)
  140. assert 4 == len(node_cut), errmsg
  141. H = G.copy()
  142. H.remove_nodes_from(node_cut)
  143. assert not nx.is_connected(H), errmsg
  144. def test_icosahedral_cutset():
  145. G = nx.icosahedral_graph()
  146. for flow_func in flow_funcs:
  147. kwargs = {"flow_func": flow_func}
  148. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  149. # edge cuts
  150. edge_cut = nx.minimum_edge_cut(G, **kwargs)
  151. assert 5 == len(edge_cut), errmsg
  152. H = G.copy()
  153. H.remove_edges_from(edge_cut)
  154. assert not nx.is_connected(H), errmsg
  155. # node cuts
  156. node_cut = nx.minimum_node_cut(G, **kwargs)
  157. assert 5 == len(node_cut), errmsg
  158. H = G.copy()
  159. H.remove_nodes_from(node_cut)
  160. assert not nx.is_connected(H), errmsg
  161. def test_node_cutset_exception():
  162. G = nx.Graph()
  163. G.add_edges_from([(1, 2), (3, 4)])
  164. for flow_func in flow_funcs:
  165. pytest.raises(nx.NetworkXError, nx.minimum_node_cut, G, flow_func=flow_func)
  166. def test_node_cutset_random_graphs():
  167. for flow_func in flow_funcs:
  168. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  169. for i in range(3):
  170. G = nx.fast_gnp_random_graph(50, 0.25, seed=42)
  171. if not nx.is_connected(G):
  172. ccs = iter(nx.connected_components(G))
  173. start = arbitrary_element(next(ccs))
  174. G.add_edges_from((start, arbitrary_element(c)) for c in ccs)
  175. cutset = nx.minimum_node_cut(G, flow_func=flow_func)
  176. assert nx.node_connectivity(G) == len(cutset), errmsg
  177. G.remove_nodes_from(cutset)
  178. assert not nx.is_connected(G), errmsg
  179. def test_edge_cutset_random_graphs():
  180. for flow_func in flow_funcs:
  181. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  182. for i in range(3):
  183. G = nx.fast_gnp_random_graph(50, 0.25, seed=42)
  184. if not nx.is_connected(G):
  185. ccs = iter(nx.connected_components(G))
  186. start = arbitrary_element(next(ccs))
  187. G.add_edges_from((start, arbitrary_element(c)) for c in ccs)
  188. cutset = nx.minimum_edge_cut(G, flow_func=flow_func)
  189. assert nx.edge_connectivity(G) == len(cutset), errmsg
  190. G.remove_edges_from(cutset)
  191. assert not nx.is_connected(G), errmsg
  192. def test_empty_graphs():
  193. G = nx.Graph()
  194. D = nx.DiGraph()
  195. for interface_func in [nx.minimum_node_cut, nx.minimum_edge_cut]:
  196. for flow_func in flow_funcs:
  197. pytest.raises(
  198. nx.NetworkXPointlessConcept, interface_func, G, flow_func=flow_func
  199. )
  200. pytest.raises(
  201. nx.NetworkXPointlessConcept, interface_func, D, flow_func=flow_func
  202. )
  203. def test_unbounded():
  204. G = nx.complete_graph(5)
  205. for flow_func in flow_funcs:
  206. assert 4 == len(minimum_st_edge_cut(G, 1, 4, flow_func=flow_func))
  207. def test_missing_source():
  208. G = nx.path_graph(4)
  209. for interface_func in [nx.minimum_edge_cut, nx.minimum_node_cut]:
  210. for flow_func in flow_funcs:
  211. pytest.raises(
  212. nx.NetworkXError, interface_func, G, 10, 1, flow_func=flow_func
  213. )
  214. def test_missing_target():
  215. G = nx.path_graph(4)
  216. for interface_func in [nx.minimum_edge_cut, nx.minimum_node_cut]:
  217. for flow_func in flow_funcs:
  218. pytest.raises(
  219. nx.NetworkXError, interface_func, G, 1, 10, flow_func=flow_func
  220. )
  221. def test_not_weakly_connected():
  222. G = nx.DiGraph()
  223. nx.add_path(G, [1, 2, 3])
  224. nx.add_path(G, [4, 5])
  225. for interface_func in [nx.minimum_edge_cut, nx.minimum_node_cut]:
  226. for flow_func in flow_funcs:
  227. pytest.raises(nx.NetworkXError, interface_func, G, flow_func=flow_func)
  228. def test_not_connected():
  229. G = nx.Graph()
  230. nx.add_path(G, [1, 2, 3])
  231. nx.add_path(G, [4, 5])
  232. for interface_func in [nx.minimum_edge_cut, nx.minimum_node_cut]:
  233. for flow_func in flow_funcs:
  234. pytest.raises(nx.NetworkXError, interface_func, G, flow_func=flow_func)
  235. def tests_min_cut_complete():
  236. G = nx.complete_graph(5)
  237. for interface_func in [nx.minimum_edge_cut, nx.minimum_node_cut]:
  238. for flow_func in flow_funcs:
  239. assert 4 == len(interface_func(G, flow_func=flow_func))
  240. def tests_min_cut_complete_directed():
  241. G = nx.complete_graph(5)
  242. G = G.to_directed()
  243. for interface_func in [nx.minimum_edge_cut, nx.minimum_node_cut]:
  244. for flow_func in flow_funcs:
  245. assert 4 == len(interface_func(G, flow_func=flow_func))
  246. def tests_minimum_st_node_cut():
  247. G = nx.Graph()
  248. G.add_nodes_from([0, 1, 2, 3, 7, 8, 11, 12])
  249. G.add_edges_from([(7, 11), (1, 11), (1, 12), (12, 8), (0, 1)])
  250. nodelist = minimum_st_node_cut(G, 7, 11)
  251. assert nodelist == {}
  252. def test_invalid_auxiliary():
  253. G = nx.complete_graph(5)
  254. pytest.raises(nx.NetworkXError, minimum_st_node_cut, G, 0, 3, auxiliary=G)
  255. def test_interface_only_source():
  256. G = nx.complete_graph(5)
  257. for interface_func in [nx.minimum_node_cut, nx.minimum_edge_cut]:
  258. pytest.raises(nx.NetworkXError, interface_func, G, s=0)
  259. def test_interface_only_target():
  260. G = nx.complete_graph(5)
  261. for interface_func in [nx.minimum_node_cut, nx.minimum_edge_cut]:
  262. pytest.raises(nx.NetworkXError, interface_func, G, t=3)