test_connectivity.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. import itertools
  2. import pytest
  3. import networkx as nx
  4. from networkx.algorithms import flow
  5. from networkx.algorithms.connectivity import (
  6. local_edge_connectivity,
  7. local_node_connectivity,
  8. )
  9. flow_funcs = [
  10. flow.boykov_kolmogorov,
  11. flow.dinitz,
  12. flow.edmonds_karp,
  13. flow.preflow_push,
  14. flow.shortest_augmenting_path,
  15. ]
  16. # helper functions for tests
  17. def _generate_no_biconnected(max_attempts=50):
  18. attempts = 0
  19. while True:
  20. G = nx.fast_gnp_random_graph(100, 0.0575, seed=42)
  21. if nx.is_connected(G) and not nx.is_biconnected(G):
  22. attempts = 0
  23. yield G
  24. else:
  25. if attempts >= max_attempts:
  26. msg = f"Tried {max_attempts} times: no suitable Graph."
  27. raise Exception(msg)
  28. else:
  29. attempts += 1
  30. def test_average_connectivity():
  31. # figure 1 from:
  32. # Beineke, L., O. Oellermann, and R. Pippert (2002). The average
  33. # connectivity of a graph. Discrete mathematics 252(1-3), 31-45
  34. # http://www.sciencedirect.com/science/article/pii/S0012365X01001807
  35. G1 = nx.path_graph(3)
  36. G1.add_edges_from([(1, 3), (1, 4)])
  37. G2 = nx.path_graph(3)
  38. G2.add_edges_from([(1, 3), (1, 4), (0, 3), (0, 4), (3, 4)])
  39. G3 = nx.Graph()
  40. for flow_func in flow_funcs:
  41. kwargs = {"flow_func": flow_func}
  42. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  43. assert nx.average_node_connectivity(G1, **kwargs) == 1, errmsg
  44. assert nx.average_node_connectivity(G2, **kwargs) == 2.2, errmsg
  45. assert nx.average_node_connectivity(G3, **kwargs) == 0, errmsg
  46. def test_average_connectivity_directed():
  47. G = nx.DiGraph([(1, 3), (1, 4), (1, 5)])
  48. for flow_func in flow_funcs:
  49. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  50. assert nx.average_node_connectivity(G) == 0.25, errmsg
  51. def test_articulation_points():
  52. Ggen = _generate_no_biconnected()
  53. for flow_func in flow_funcs:
  54. for i in range(3):
  55. G = next(Ggen)
  56. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  57. assert nx.node_connectivity(G, flow_func=flow_func) == 1, errmsg
  58. def test_brandes_erlebach():
  59. # Figure 1 chapter 7: Connectivity
  60. # http://www.informatik.uni-augsburg.de/thi/personen/kammer/Graph_Connectivity.pdf
  61. G = nx.Graph()
  62. G.add_edges_from(
  63. [
  64. (1, 2),
  65. (1, 3),
  66. (1, 4),
  67. (1, 5),
  68. (2, 3),
  69. (2, 6),
  70. (3, 4),
  71. (3, 6),
  72. (4, 6),
  73. (4, 7),
  74. (5, 7),
  75. (6, 8),
  76. (6, 9),
  77. (7, 8),
  78. (7, 10),
  79. (8, 11),
  80. (9, 10),
  81. (9, 11),
  82. (10, 11),
  83. ]
  84. )
  85. for flow_func in flow_funcs:
  86. kwargs = {"flow_func": flow_func}
  87. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  88. assert 3 == local_edge_connectivity(G, 1, 11, **kwargs), errmsg
  89. assert 3 == nx.edge_connectivity(G, 1, 11, **kwargs), errmsg
  90. assert 2 == local_node_connectivity(G, 1, 11, **kwargs), errmsg
  91. assert 2 == nx.node_connectivity(G, 1, 11, **kwargs), errmsg
  92. assert 2 == nx.edge_connectivity(G, **kwargs), errmsg
  93. assert 2 == nx.node_connectivity(G, **kwargs), errmsg
  94. if flow_func is flow.preflow_push:
  95. assert 3 == nx.edge_connectivity(G, 1, 11, cutoff=2, **kwargs), errmsg
  96. else:
  97. assert 2 == nx.edge_connectivity(G, 1, 11, cutoff=2, **kwargs), errmsg
  98. def test_white_harary_1():
  99. # Figure 1b white and harary (2001)
  100. # https://doi.org/10.1111/0081-1750.00098
  101. # A graph with high adhesion (edge connectivity) and low cohesion
  102. # (vertex connectivity)
  103. G = nx.disjoint_union(nx.complete_graph(4), nx.complete_graph(4))
  104. G.remove_node(7)
  105. for i in range(4, 7):
  106. G.add_edge(0, i)
  107. G = nx.disjoint_union(G, nx.complete_graph(4))
  108. G.remove_node(G.order() - 1)
  109. for i in range(7, 10):
  110. G.add_edge(0, i)
  111. for flow_func in flow_funcs:
  112. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  113. assert 1 == nx.node_connectivity(G, flow_func=flow_func), errmsg
  114. assert 3 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
  115. def test_white_harary_2():
  116. # Figure 8 white and harary (2001)
  117. # https://doi.org/10.1111/0081-1750.00098
  118. G = nx.disjoint_union(nx.complete_graph(4), nx.complete_graph(4))
  119. G.add_edge(0, 4)
  120. # kappa <= lambda <= delta
  121. assert 3 == min(nx.core_number(G).values())
  122. for flow_func in flow_funcs:
  123. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  124. assert 1 == nx.node_connectivity(G, flow_func=flow_func), errmsg
  125. assert 1 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
  126. def test_complete_graphs():
  127. for n in range(5, 20, 5):
  128. for flow_func in flow_funcs:
  129. G = nx.complete_graph(n)
  130. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  131. assert n - 1 == nx.node_connectivity(G, flow_func=flow_func), errmsg
  132. assert n - 1 == nx.node_connectivity(
  133. G.to_directed(), flow_func=flow_func
  134. ), errmsg
  135. assert n - 1 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
  136. assert n - 1 == nx.edge_connectivity(
  137. G.to_directed(), flow_func=flow_func
  138. ), errmsg
  139. def test_empty_graphs():
  140. for k in range(5, 25, 5):
  141. G = nx.empty_graph(k)
  142. for flow_func in flow_funcs:
  143. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  144. assert 0 == nx.node_connectivity(G, flow_func=flow_func), errmsg
  145. assert 0 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
  146. def test_petersen():
  147. G = nx.petersen_graph()
  148. for flow_func in flow_funcs:
  149. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  150. assert 3 == nx.node_connectivity(G, flow_func=flow_func), errmsg
  151. assert 3 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
  152. def test_tutte():
  153. G = nx.tutte_graph()
  154. for flow_func in flow_funcs:
  155. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  156. assert 3 == nx.node_connectivity(G, flow_func=flow_func), errmsg
  157. assert 3 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
  158. def test_dodecahedral():
  159. G = nx.dodecahedral_graph()
  160. for flow_func in flow_funcs:
  161. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  162. assert 3 == nx.node_connectivity(G, flow_func=flow_func), errmsg
  163. assert 3 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
  164. def test_octahedral():
  165. G = nx.octahedral_graph()
  166. for flow_func in flow_funcs:
  167. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  168. assert 4 == nx.node_connectivity(G, flow_func=flow_func), errmsg
  169. assert 4 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
  170. def test_icosahedral():
  171. G = nx.icosahedral_graph()
  172. for flow_func in flow_funcs:
  173. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  174. assert 5 == nx.node_connectivity(G, flow_func=flow_func), errmsg
  175. assert 5 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
  176. def test_missing_source():
  177. G = nx.path_graph(4)
  178. for flow_func in flow_funcs:
  179. pytest.raises(
  180. nx.NetworkXError, nx.node_connectivity, G, 10, 1, flow_func=flow_func
  181. )
  182. def test_missing_target():
  183. G = nx.path_graph(4)
  184. for flow_func in flow_funcs:
  185. pytest.raises(
  186. nx.NetworkXError, nx.node_connectivity, G, 1, 10, flow_func=flow_func
  187. )
  188. def test_edge_missing_source():
  189. G = nx.path_graph(4)
  190. for flow_func in flow_funcs:
  191. pytest.raises(
  192. nx.NetworkXError, nx.edge_connectivity, G, 10, 1, flow_func=flow_func
  193. )
  194. def test_edge_missing_target():
  195. G = nx.path_graph(4)
  196. for flow_func in flow_funcs:
  197. pytest.raises(
  198. nx.NetworkXError, nx.edge_connectivity, G, 1, 10, flow_func=flow_func
  199. )
  200. def test_not_weakly_connected():
  201. G = nx.DiGraph()
  202. nx.add_path(G, [1, 2, 3])
  203. nx.add_path(G, [4, 5])
  204. for flow_func in flow_funcs:
  205. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  206. assert nx.node_connectivity(G) == 0, errmsg
  207. assert nx.edge_connectivity(G) == 0, errmsg
  208. def test_not_connected():
  209. G = nx.Graph()
  210. nx.add_path(G, [1, 2, 3])
  211. nx.add_path(G, [4, 5])
  212. for flow_func in flow_funcs:
  213. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  214. assert nx.node_connectivity(G) == 0, errmsg
  215. assert nx.edge_connectivity(G) == 0, errmsg
  216. def test_directed_edge_connectivity():
  217. G = nx.cycle_graph(10, create_using=nx.DiGraph()) # only one direction
  218. D = nx.cycle_graph(10).to_directed() # 2 reciprocal edges
  219. for flow_func in flow_funcs:
  220. errmsg = f"Assertion failed in function: {flow_func.__name__}"
  221. assert 1 == nx.edge_connectivity(G, flow_func=flow_func), errmsg
  222. assert 1 == local_edge_connectivity(G, 1, 4, flow_func=flow_func), errmsg
  223. assert 1 == nx.edge_connectivity(G, 1, 4, flow_func=flow_func), errmsg
  224. assert 2 == nx.edge_connectivity(D, flow_func=flow_func), errmsg
  225. assert 2 == local_edge_connectivity(D, 1, 4, flow_func=flow_func), errmsg
  226. assert 2 == nx.edge_connectivity(D, 1, 4, flow_func=flow_func), errmsg
  227. def test_cutoff():
  228. G = nx.complete_graph(5)
  229. for local_func in [local_edge_connectivity, local_node_connectivity]:
  230. for flow_func in flow_funcs:
  231. if flow_func is flow.preflow_push:
  232. # cutoff is not supported by preflow_push
  233. continue
  234. for cutoff in [3, 2, 1]:
  235. result = local_func(G, 0, 4, flow_func=flow_func, cutoff=cutoff)
  236. assert cutoff == result, f"cutoff error in {flow_func.__name__}"
  237. def test_invalid_auxiliary():
  238. G = nx.complete_graph(5)
  239. pytest.raises(nx.NetworkXError, local_node_connectivity, G, 0, 3, auxiliary=G)
  240. def test_interface_only_source():
  241. G = nx.complete_graph(5)
  242. for interface_func in [nx.node_connectivity, nx.edge_connectivity]:
  243. pytest.raises(nx.NetworkXError, interface_func, G, s=0)
  244. def test_interface_only_target():
  245. G = nx.complete_graph(5)
  246. for interface_func in [nx.node_connectivity, nx.edge_connectivity]:
  247. pytest.raises(nx.NetworkXError, interface_func, G, t=3)
  248. def test_edge_connectivity_flow_vs_stoer_wagner():
  249. graph_funcs = [nx.icosahedral_graph, nx.octahedral_graph, nx.dodecahedral_graph]
  250. for graph_func in graph_funcs:
  251. G = graph_func()
  252. assert nx.stoer_wagner(G)[0] == nx.edge_connectivity(G)
  253. class TestAllPairsNodeConnectivity:
  254. @classmethod
  255. def setup_class(cls):
  256. cls.path = nx.path_graph(7)
  257. cls.directed_path = nx.path_graph(7, create_using=nx.DiGraph())
  258. cls.cycle = nx.cycle_graph(7)
  259. cls.directed_cycle = nx.cycle_graph(7, create_using=nx.DiGraph())
  260. cls.gnp = nx.gnp_random_graph(30, 0.1, seed=42)
  261. cls.directed_gnp = nx.gnp_random_graph(30, 0.1, directed=True, seed=42)
  262. cls.K20 = nx.complete_graph(20)
  263. cls.K10 = nx.complete_graph(10)
  264. cls.K5 = nx.complete_graph(5)
  265. cls.G_list = [
  266. cls.path,
  267. cls.directed_path,
  268. cls.cycle,
  269. cls.directed_cycle,
  270. cls.gnp,
  271. cls.directed_gnp,
  272. cls.K10,
  273. cls.K5,
  274. cls.K20,
  275. ]
  276. def test_cycles(self):
  277. K_undir = nx.all_pairs_node_connectivity(self.cycle)
  278. for source in K_undir:
  279. for target, k in K_undir[source].items():
  280. assert k == 2
  281. K_dir = nx.all_pairs_node_connectivity(self.directed_cycle)
  282. for source in K_dir:
  283. for target, k in K_dir[source].items():
  284. assert k == 1
  285. def test_complete(self):
  286. for G in [self.K10, self.K5, self.K20]:
  287. K = nx.all_pairs_node_connectivity(G)
  288. for source in K:
  289. for target, k in K[source].items():
  290. assert k == len(G) - 1
  291. def test_paths(self):
  292. K_undir = nx.all_pairs_node_connectivity(self.path)
  293. for source in K_undir:
  294. for target, k in K_undir[source].items():
  295. assert k == 1
  296. K_dir = nx.all_pairs_node_connectivity(self.directed_path)
  297. for source in K_dir:
  298. for target, k in K_dir[source].items():
  299. if source < target:
  300. assert k == 1
  301. else:
  302. assert k == 0
  303. def test_all_pairs_connectivity_nbunch(self):
  304. G = nx.complete_graph(5)
  305. nbunch = [0, 2, 3]
  306. C = nx.all_pairs_node_connectivity(G, nbunch=nbunch)
  307. assert len(C) == len(nbunch)
  308. def test_all_pairs_connectivity_icosahedral(self):
  309. G = nx.icosahedral_graph()
  310. C = nx.all_pairs_node_connectivity(G)
  311. assert all(5 == C[u][v] for u, v in itertools.combinations(G, 2))
  312. def test_all_pairs_connectivity(self):
  313. G = nx.Graph()
  314. nodes = [0, 1, 2, 3]
  315. nx.add_path(G, nodes)
  316. A = {n: {} for n in G}
  317. for u, v in itertools.combinations(nodes, 2):
  318. A[u][v] = A[v][u] = nx.node_connectivity(G, u, v)
  319. C = nx.all_pairs_node_connectivity(G)
  320. assert sorted((k, sorted(v)) for k, v in A.items()) == sorted(
  321. (k, sorted(v)) for k, v in C.items()
  322. )
  323. def test_all_pairs_connectivity_directed(self):
  324. G = nx.DiGraph()
  325. nodes = [0, 1, 2, 3]
  326. nx.add_path(G, nodes)
  327. A = {n: {} for n in G}
  328. for u, v in itertools.permutations(nodes, 2):
  329. A[u][v] = nx.node_connectivity(G, u, v)
  330. C = nx.all_pairs_node_connectivity(G)
  331. assert sorted((k, sorted(v)) for k, v in A.items()) == sorted(
  332. (k, sorted(v)) for k, v in C.items()
  333. )
  334. def test_all_pairs_connectivity_nbunch_combinations(self):
  335. G = nx.complete_graph(5)
  336. nbunch = [0, 2, 3]
  337. A = {n: {} for n in nbunch}
  338. for u, v in itertools.combinations(nbunch, 2):
  339. A[u][v] = A[v][u] = nx.node_connectivity(G, u, v)
  340. C = nx.all_pairs_node_connectivity(G, nbunch=nbunch)
  341. assert sorted((k, sorted(v)) for k, v in A.items()) == sorted(
  342. (k, sorted(v)) for k, v in C.items()
  343. )
  344. def test_all_pairs_connectivity_nbunch_iter(self):
  345. G = nx.complete_graph(5)
  346. nbunch = [0, 2, 3]
  347. A = {n: {} for n in nbunch}
  348. for u, v in itertools.combinations(nbunch, 2):
  349. A[u][v] = A[v][u] = nx.node_connectivity(G, u, v)
  350. C = nx.all_pairs_node_connectivity(G, nbunch=iter(nbunch))
  351. assert sorted((k, sorted(v)) for k, v in A.items()) == sorted(
  352. (k, sorted(v)) for k, v in C.items()
  353. )