test_random_graphs.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. """Unit tests for the :mod:`networkx.generators.random_graphs` module."""
  2. import pytest
  3. import networkx as nx
  4. _gnp_generators = [
  5. nx.gnp_random_graph,
  6. nx.fast_gnp_random_graph,
  7. nx.binomial_graph,
  8. nx.erdos_renyi_graph,
  9. ]
  10. @pytest.mark.parametrize("generator", _gnp_generators)
  11. @pytest.mark.parametrize("directed", (True, False))
  12. def test_gnp_generators_negative_edge_probability(generator, directed):
  13. """If the edge probability `p` is <=0, the resulting graph should have no edges."""
  14. G = generator(10, -1.1, directed=directed)
  15. assert len(G) == 10
  16. assert G.number_of_edges() == 0
  17. assert G.is_directed() == directed
  18. @pytest.mark.parametrize("generator", _gnp_generators)
  19. @pytest.mark.parametrize(
  20. ("directed", "expected_num_edges"),
  21. [(False, 45), (True, 90)],
  22. )
  23. def test_gnp_generators_greater_than_1_edge_probability(
  24. generator, directed, expected_num_edges
  25. ):
  26. """If the edge probability `p` is >=1, the resulting graph should be complete."""
  27. G = generator(10, 1.1, directed=directed)
  28. assert len(G) == 10
  29. assert G.number_of_edges() == expected_num_edges
  30. assert G.is_directed() == directed
  31. @pytest.mark.parametrize("generator", _gnp_generators)
  32. @pytest.mark.parametrize("directed", (True, False))
  33. def test_gnp_generators_basic(generator, directed):
  34. """If the edge probability `p` is >0 and <1, test only the basic properties."""
  35. G = generator(10, 0.1, directed=directed)
  36. assert len(G) == 10
  37. assert G.is_directed() == directed
  38. @pytest.mark.parametrize("generator", _gnp_generators)
  39. def test_gnp_generators_for_p_close_to_1(generator):
  40. """If the edge probability `p` is close to 1, the resulting graph should have all edges."""
  41. runs = 100
  42. edges = sum(
  43. generator(10, 0.99999, directed=True).number_of_edges() for _ in range(runs)
  44. )
  45. assert abs(edges / float(runs) - 90) <= runs * 2.0 / 100
  46. @pytest.mark.parametrize("generator", _gnp_generators)
  47. @pytest.mark.parametrize("p", (0.2, 0.8))
  48. @pytest.mark.parametrize("directed", (True, False))
  49. def test_gnp_generators_edge_probability(generator, p, directed):
  50. """Test that gnp generators generate edges according to the their probability `p`."""
  51. runs = 5000
  52. n = 5
  53. edge_counts = [[0] * n for _ in range(n)]
  54. for i in range(runs):
  55. G = generator(n, p, directed=directed)
  56. for v, w in G.edges:
  57. edge_counts[v][w] += 1
  58. if not directed:
  59. edge_counts[w][v] += 1
  60. for v in range(n):
  61. for w in range(n):
  62. if v == w:
  63. # There should be no loops
  64. assert edge_counts[v][w] == 0
  65. else:
  66. # Each edge should have been generated with probability close to p
  67. assert abs(edge_counts[v][w] / float(runs) - p) <= 0.03
  68. @pytest.mark.parametrize(
  69. "generator", [nx.gnp_random_graph, nx.binomial_graph, nx.erdos_renyi_graph]
  70. )
  71. @pytest.mark.parametrize(
  72. ("seed", "directed", "expected_num_edges"),
  73. [(42, False, 1219), (42, True, 2454), (314, False, 1247), (314, True, 2476)],
  74. )
  75. def test_gnp_random_graph_aliases(generator, seed, directed, expected_num_edges):
  76. """Test that aliases give the same result with the same seed."""
  77. G = generator(100, 0.25, seed=seed, directed=directed)
  78. assert len(G) == 100
  79. assert G.number_of_edges() == expected_num_edges
  80. assert G.is_directed() == directed
  81. class TestGeneratorsRandom:
  82. def test_random_graph(self):
  83. seed = 42
  84. G = nx.gnm_random_graph(100, 20, seed)
  85. G = nx.gnm_random_graph(100, 20, seed, directed=True)
  86. G = nx.dense_gnm_random_graph(100, 20, seed)
  87. G = nx.watts_strogatz_graph(10, 2, 0.25, seed)
  88. assert len(G) == 10
  89. assert G.number_of_edges() == 10
  90. G = nx.connected_watts_strogatz_graph(10, 2, 0.1, tries=10, seed=seed)
  91. assert len(G) == 10
  92. assert G.number_of_edges() == 10
  93. pytest.raises(
  94. nx.NetworkXError, nx.connected_watts_strogatz_graph, 10, 2, 0.1, tries=0
  95. )
  96. G = nx.watts_strogatz_graph(10, 4, 0.25, seed)
  97. assert len(G) == 10
  98. assert G.number_of_edges() == 20
  99. G = nx.newman_watts_strogatz_graph(10, 2, 0.0, seed)
  100. assert len(G) == 10
  101. assert G.number_of_edges() == 10
  102. G = nx.newman_watts_strogatz_graph(10, 4, 0.25, seed)
  103. assert len(G) == 10
  104. assert G.number_of_edges() >= 20
  105. G = nx.barabasi_albert_graph(100, 1, seed)
  106. G = nx.barabasi_albert_graph(100, 3, seed)
  107. assert G.number_of_edges() == (97 * 3)
  108. G = nx.barabasi_albert_graph(100, 3, seed, nx.complete_graph(5))
  109. assert G.number_of_edges() == (10 + 95 * 3)
  110. G = nx.extended_barabasi_albert_graph(100, 1, 0, 0, seed)
  111. assert G.number_of_edges() == 99
  112. G = nx.extended_barabasi_albert_graph(100, 3, 0, 0, seed)
  113. assert G.number_of_edges() == 97 * 3
  114. G = nx.extended_barabasi_albert_graph(100, 1, 0, 0.5, seed)
  115. assert G.number_of_edges() == 99
  116. G = nx.extended_barabasi_albert_graph(100, 2, 0.5, 0, seed)
  117. assert G.number_of_edges() > 100 * 3
  118. assert G.number_of_edges() < 100 * 4
  119. G = nx.extended_barabasi_albert_graph(100, 2, 0.3, 0.3, seed)
  120. assert G.number_of_edges() > 100 * 2
  121. assert G.number_of_edges() < 100 * 4
  122. G = nx.powerlaw_cluster_graph(100, 1, 1.0, seed)
  123. G = nx.powerlaw_cluster_graph(100, 3, 0.0, seed)
  124. assert G.number_of_edges() == (97 * 3)
  125. G = nx.random_regular_graph(10, 20, seed)
  126. pytest.raises(nx.NetworkXError, nx.random_regular_graph, 3, 21)
  127. pytest.raises(nx.NetworkXError, nx.random_regular_graph, 33, 21)
  128. constructor = [(10, 20, 0.8), (20, 40, 0.8)]
  129. G = nx.random_shell_graph(constructor, seed)
  130. def is_caterpillar(g):
  131. """
  132. A tree is a caterpillar iff all nodes of degree >=3 are surrounded
  133. by at most two nodes of degree two or greater.
  134. ref: http://mathworld.wolfram.com/CaterpillarGraph.html
  135. """
  136. deg_over_3 = [n for n in g if g.degree(n) >= 3]
  137. for n in deg_over_3:
  138. nbh_deg_over_2 = [nbh for nbh in g.neighbors(n) if g.degree(nbh) >= 2]
  139. if not len(nbh_deg_over_2) <= 2:
  140. return False
  141. return True
  142. def is_lobster(g):
  143. """
  144. A tree is a lobster if it has the property that the removal of leaf
  145. nodes leaves a caterpillar graph (Gallian 2007)
  146. ref: http://mathworld.wolfram.com/LobsterGraph.html
  147. """
  148. non_leafs = [n for n in g if g.degree(n) > 1]
  149. return is_caterpillar(g.subgraph(non_leafs))
  150. G = nx.random_lobster(10, 0.1, 0.5, seed)
  151. assert max(G.degree(n) for n in G.nodes()) > 3
  152. assert is_lobster(G)
  153. pytest.raises(nx.NetworkXError, nx.random_lobster, 10, 0.1, 1, seed)
  154. pytest.raises(nx.NetworkXError, nx.random_lobster, 10, 1, 1, seed)
  155. pytest.raises(nx.NetworkXError, nx.random_lobster, 10, 1, 0.5, seed)
  156. # docstring says this should be a caterpillar
  157. G = nx.random_lobster(10, 0.1, 0.0, seed)
  158. assert is_caterpillar(G)
  159. # difficult to find seed that requires few tries
  160. seq = nx.random_powerlaw_tree_sequence(10, 3, seed=14, tries=1)
  161. G = nx.random_powerlaw_tree(10, 3, seed=14, tries=1)
  162. def test_dual_barabasi_albert(self, m1=1, m2=4, p=0.5):
  163. """
  164. Tests that the dual BA random graph generated behaves consistently.
  165. Tests the exceptions are raised as expected.
  166. The graphs generation are repeated several times to prevent lucky shots
  167. """
  168. seeds = [42, 314, 2718]
  169. initial_graph = nx.complete_graph(10)
  170. for seed in seeds:
  171. # This should be BA with m = m1
  172. BA1 = nx.barabasi_albert_graph(100, m1, seed)
  173. DBA1 = nx.dual_barabasi_albert_graph(100, m1, m2, 1, seed)
  174. assert BA1.edges() == DBA1.edges()
  175. # This should be BA with m = m2
  176. BA2 = nx.barabasi_albert_graph(100, m2, seed)
  177. DBA2 = nx.dual_barabasi_albert_graph(100, m1, m2, 0, seed)
  178. assert BA2.edges() == DBA2.edges()
  179. BA3 = nx.barabasi_albert_graph(100, m1, seed)
  180. DBA3 = nx.dual_barabasi_albert_graph(100, m1, m1, p, seed)
  181. # We can't compare edges here since randomness is "consumed" when drawing
  182. # between m1 and m2
  183. assert BA3.size() == DBA3.size()
  184. DBA = nx.dual_barabasi_albert_graph(100, m1, m2, p, seed, initial_graph)
  185. BA1 = nx.barabasi_albert_graph(100, m1, seed, initial_graph)
  186. BA2 = nx.barabasi_albert_graph(100, m2, seed, initial_graph)
  187. assert (
  188. min(BA1.size(), BA2.size()) <= DBA.size() <= max(BA1.size(), BA2.size())
  189. )
  190. # Testing exceptions
  191. dbag = nx.dual_barabasi_albert_graph
  192. pytest.raises(nx.NetworkXError, dbag, m1, m1, m2, 0)
  193. pytest.raises(nx.NetworkXError, dbag, m2, m1, m2, 0)
  194. pytest.raises(nx.NetworkXError, dbag, 100, m1, m2, -0.5)
  195. pytest.raises(nx.NetworkXError, dbag, 100, m1, m2, 1.5)
  196. initial = nx.complete_graph(max(m1, m2) - 1)
  197. pytest.raises(nx.NetworkXError, dbag, 100, m1, m2, p, initial_graph=initial)
  198. def test_extended_barabasi_albert(self, m=2):
  199. """
  200. Tests that the extended BA random graph generated behaves consistently.
  201. Tests the exceptions are raised as expected.
  202. The graphs generation are repeated several times to prevent lucky-shots
  203. """
  204. seeds = [42, 314, 2718]
  205. for seed in seeds:
  206. BA_model = nx.barabasi_albert_graph(100, m, seed)
  207. BA_model_edges = BA_model.number_of_edges()
  208. # This behaves just like BA, the number of edges must be the same
  209. G1 = nx.extended_barabasi_albert_graph(100, m, 0, 0, seed)
  210. assert G1.size() == BA_model_edges
  211. # More than twice more edges should have been added
  212. G1 = nx.extended_barabasi_albert_graph(100, m, 0.8, 0, seed)
  213. assert G1.size() > BA_model_edges * 2
  214. # Only edge rewiring, so the number of edges less than original
  215. G2 = nx.extended_barabasi_albert_graph(100, m, 0, 0.8, seed)
  216. assert G2.size() == BA_model_edges
  217. # Mixed scenario: less edges than G1 and more edges than G2
  218. G3 = nx.extended_barabasi_albert_graph(100, m, 0.3, 0.3, seed)
  219. assert G3.size() > G2.size()
  220. assert G3.size() < G1.size()
  221. # Testing exceptions
  222. ebag = nx.extended_barabasi_albert_graph
  223. pytest.raises(nx.NetworkXError, ebag, m, m, 0, 0)
  224. pytest.raises(nx.NetworkXError, ebag, 1, 0.5, 0, 0)
  225. pytest.raises(nx.NetworkXError, ebag, 100, 2, 0.5, 0.5)
  226. def test_random_zero_regular_graph(self):
  227. """Tests that a 0-regular graph has the correct number of nodes and
  228. edges.
  229. """
  230. seed = 42
  231. G = nx.random_regular_graph(0, 10, seed)
  232. assert len(G) == 10
  233. assert G.number_of_edges() == 0
  234. def test_gnm(self):
  235. G = nx.gnm_random_graph(10, 3)
  236. assert len(G) == 10
  237. assert G.number_of_edges() == 3
  238. G = nx.gnm_random_graph(10, 3, seed=42)
  239. assert len(G) == 10
  240. assert G.number_of_edges() == 3
  241. G = nx.gnm_random_graph(10, 100)
  242. assert len(G) == 10
  243. assert G.number_of_edges() == 45
  244. G = nx.gnm_random_graph(10, 100, directed=True)
  245. assert len(G) == 10
  246. assert G.number_of_edges() == 90
  247. G = nx.gnm_random_graph(10, -1.1)
  248. assert len(G) == 10
  249. assert G.number_of_edges() == 0
  250. def test_watts_strogatz_big_k(self):
  251. # Test to make sure than n <= k
  252. pytest.raises(nx.NetworkXError, nx.watts_strogatz_graph, 10, 11, 0.25)
  253. pytest.raises(nx.NetworkXError, nx.newman_watts_strogatz_graph, 10, 11, 0.25)
  254. # could create an infinite loop, now doesn't
  255. # infinite loop used to occur when a node has degree n-1 and needs to rewire
  256. nx.watts_strogatz_graph(10, 9, 0.25, seed=0)
  257. nx.newman_watts_strogatz_graph(10, 9, 0.5, seed=0)
  258. # Test k==n scenario
  259. nx.watts_strogatz_graph(10, 10, 0.25, seed=0)
  260. nx.newman_watts_strogatz_graph(10, 10, 0.25, seed=0)
  261. def test_random_kernel_graph(self):
  262. def integral(u, w, z):
  263. return c * (z - w)
  264. def root(u, w, r):
  265. return r / c + w
  266. c = 1
  267. graph = nx.random_kernel_graph(1000, integral, root)
  268. graph = nx.random_kernel_graph(1000, integral, root, seed=42)
  269. assert len(graph) == 1000