test_geometric.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. import math
  2. import random
  3. from itertools import combinations
  4. import pytest
  5. import networkx as nx
  6. def l1dist(x, y):
  7. return sum(abs(a - b) for a, b in zip(x, y))
  8. class TestRandomGeometricGraph:
  9. """Unit tests for the :func:`~networkx.random_geometric_graph`
  10. function.
  11. """
  12. def test_number_of_nodes(self):
  13. G = nx.random_geometric_graph(50, 0.25, seed=42)
  14. assert len(G) == 50
  15. G = nx.random_geometric_graph(range(50), 0.25, seed=42)
  16. assert len(G) == 50
  17. def test_distances(self):
  18. """Tests that pairs of vertices adjacent if and only if they are
  19. within the prescribed radius.
  20. """
  21. # Use the Euclidean metric, the default according to the
  22. # documentation.
  23. G = nx.random_geometric_graph(50, 0.25)
  24. for u, v in combinations(G, 2):
  25. # Adjacent vertices must be within the given distance.
  26. if v in G[u]:
  27. assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
  28. # Nonadjacent vertices must be at greater distance.
  29. else:
  30. assert not math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
  31. def test_p(self):
  32. """Tests for providing an alternate distance metric to the
  33. generator.
  34. """
  35. # Use the L1 metric.
  36. G = nx.random_geometric_graph(50, 0.25, p=1)
  37. for u, v in combinations(G, 2):
  38. # Adjacent vertices must be within the given distance.
  39. if v in G[u]:
  40. assert l1dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
  41. # Nonadjacent vertices must be at greater distance.
  42. else:
  43. assert not l1dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
  44. def test_node_names(self):
  45. """Tests using values other than sequential numbers as node IDs."""
  46. import string
  47. nodes = list(string.ascii_lowercase)
  48. G = nx.random_geometric_graph(nodes, 0.25)
  49. assert len(G) == len(nodes)
  50. for u, v in combinations(G, 2):
  51. # Adjacent vertices must be within the given distance.
  52. if v in G[u]:
  53. assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
  54. # Nonadjacent vertices must be at greater distance.
  55. else:
  56. assert not math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
  57. class TestSoftRandomGeometricGraph:
  58. """Unit tests for the :func:`~networkx.soft_random_geometric_graph`
  59. function.
  60. """
  61. def test_number_of_nodes(self):
  62. G = nx.soft_random_geometric_graph(50, 0.25, seed=42)
  63. assert len(G) == 50
  64. G = nx.soft_random_geometric_graph(range(50), 0.25, seed=42)
  65. assert len(G) == 50
  66. def test_distances(self):
  67. """Tests that pairs of vertices adjacent if and only if they are
  68. within the prescribed radius.
  69. """
  70. # Use the Euclidean metric, the default according to the
  71. # documentation.
  72. G = nx.soft_random_geometric_graph(50, 0.25)
  73. for u, v in combinations(G, 2):
  74. # Adjacent vertices must be within the given distance.
  75. if v in G[u]:
  76. assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
  77. def test_p(self):
  78. """Tests for providing an alternate distance metric to the
  79. generator.
  80. """
  81. # Use the L1 metric.
  82. def dist(x, y):
  83. return sum(abs(a - b) for a, b in zip(x, y))
  84. G = nx.soft_random_geometric_graph(50, 0.25, p=1)
  85. for u, v in combinations(G, 2):
  86. # Adjacent vertices must be within the given distance.
  87. if v in G[u]:
  88. assert dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
  89. def test_node_names(self):
  90. """Tests using values other than sequential numbers as node IDs."""
  91. import string
  92. nodes = list(string.ascii_lowercase)
  93. G = nx.soft_random_geometric_graph(nodes, 0.25)
  94. assert len(G) == len(nodes)
  95. for u, v in combinations(G, 2):
  96. # Adjacent vertices must be within the given distance.
  97. if v in G[u]:
  98. assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
  99. def test_p_dist_default(self):
  100. """Tests default p_dict = 0.5 returns graph with edge count <= RGG with
  101. same n, radius, dim and positions
  102. """
  103. nodes = 50
  104. dim = 2
  105. pos = {v: [random.random() for i in range(dim)] for v in range(nodes)}
  106. RGG = nx.random_geometric_graph(50, 0.25, pos=pos)
  107. SRGG = nx.soft_random_geometric_graph(50, 0.25, pos=pos)
  108. assert len(SRGG.edges()) <= len(RGG.edges())
  109. def test_p_dist_zero(self):
  110. """Tests if p_dict = 0 returns disconnected graph with 0 edges"""
  111. def p_dist(dist):
  112. return 0
  113. G = nx.soft_random_geometric_graph(50, 0.25, p_dist=p_dist)
  114. assert len(G.edges) == 0
  115. def join(G, u, v, theta, alpha, metric):
  116. """Returns ``True`` if and only if the nodes whose attributes are
  117. ``du`` and ``dv`` should be joined, according to the threshold
  118. condition for geographical threshold graphs.
  119. ``G`` is an undirected NetworkX graph, and ``u`` and ``v`` are nodes
  120. in that graph. The nodes must have node attributes ``'pos'`` and
  121. ``'weight'``.
  122. ``metric`` is a distance metric.
  123. """
  124. du, dv = G.nodes[u], G.nodes[v]
  125. u_pos, v_pos = du["pos"], dv["pos"]
  126. u_weight, v_weight = du["weight"], dv["weight"]
  127. return (u_weight + v_weight) * metric(u_pos, v_pos) ** alpha >= theta
  128. class TestGeographicalThresholdGraph:
  129. """Unit tests for the :func:`~networkx.geographical_threshold_graph`
  130. function.
  131. """
  132. def test_number_of_nodes(self):
  133. G = nx.geographical_threshold_graph(50, 100, seed=42)
  134. assert len(G) == 50
  135. G = nx.geographical_threshold_graph(range(50), 100, seed=42)
  136. assert len(G) == 50
  137. def test_distances(self):
  138. """Tests that pairs of vertices adjacent if and only if their
  139. distances meet the given threshold.
  140. """
  141. # Use the Euclidean metric and alpha = -2
  142. # the default according to the documentation.
  143. G = nx.geographical_threshold_graph(50, 10)
  144. for u, v in combinations(G, 2):
  145. # Adjacent vertices must exceed the threshold.
  146. if v in G[u]:
  147. assert join(G, u, v, 10, -2, math.dist)
  148. # Nonadjacent vertices must not exceed the threshold.
  149. else:
  150. assert not join(G, u, v, 10, -2, math.dist)
  151. def test_metric(self):
  152. """Tests for providing an alternate distance metric to the
  153. generator.
  154. """
  155. # Use the L1 metric.
  156. G = nx.geographical_threshold_graph(50, 10, metric=l1dist)
  157. for u, v in combinations(G, 2):
  158. # Adjacent vertices must exceed the threshold.
  159. if v in G[u]:
  160. assert join(G, u, v, 10, -2, l1dist)
  161. # Nonadjacent vertices must not exceed the threshold.
  162. else:
  163. assert not join(G, u, v, 10, -2, l1dist)
  164. def test_p_dist_zero(self):
  165. """Tests if p_dict = 0 returns disconnected graph with 0 edges"""
  166. def p_dist(dist):
  167. return 0
  168. G = nx.geographical_threshold_graph(50, 1, p_dist=p_dist)
  169. assert len(G.edges) == 0
  170. class TestWaxmanGraph:
  171. """Unit tests for the :func:`~networkx.waxman_graph` function."""
  172. def test_number_of_nodes_1(self):
  173. G = nx.waxman_graph(50, 0.5, 0.1, seed=42)
  174. assert len(G) == 50
  175. G = nx.waxman_graph(range(50), 0.5, 0.1, seed=42)
  176. assert len(G) == 50
  177. def test_number_of_nodes_2(self):
  178. G = nx.waxman_graph(50, 0.5, 0.1, L=1)
  179. assert len(G) == 50
  180. G = nx.waxman_graph(range(50), 0.5, 0.1, L=1)
  181. assert len(G) == 50
  182. def test_metric(self):
  183. """Tests for providing an alternate distance metric to the
  184. generator.
  185. """
  186. # Use the L1 metric.
  187. G = nx.waxman_graph(50, 0.5, 0.1, metric=l1dist)
  188. assert len(G) == 50
  189. class TestNavigableSmallWorldGraph:
  190. def test_navigable_small_world(self):
  191. G = nx.navigable_small_world_graph(5, p=1, q=0, seed=42)
  192. gg = nx.grid_2d_graph(5, 5).to_directed()
  193. assert nx.is_isomorphic(G, gg)
  194. G = nx.navigable_small_world_graph(5, p=1, q=0, dim=3)
  195. gg = nx.grid_graph([5, 5, 5]).to_directed()
  196. assert nx.is_isomorphic(G, gg)
  197. G = nx.navigable_small_world_graph(5, p=1, q=0, dim=1)
  198. gg = nx.grid_graph([5]).to_directed()
  199. assert nx.is_isomorphic(G, gg)
  200. class TestThresholdedRandomGeometricGraph:
  201. """Unit tests for the :func:`~networkx.thresholded_random_geometric_graph`
  202. function.
  203. """
  204. def test_number_of_nodes(self):
  205. G = nx.thresholded_random_geometric_graph(50, 0.2, 0.1, seed=42)
  206. assert len(G) == 50
  207. G = nx.thresholded_random_geometric_graph(range(50), 0.2, 0.1)
  208. assert len(G) == 50
  209. def test_distances(self):
  210. """Tests that pairs of vertices adjacent if and only if they are
  211. within the prescribed radius.
  212. """
  213. # Use the Euclidean metric, the default according to the
  214. # documentation.
  215. G = nx.thresholded_random_geometric_graph(50, 0.25, 0.1)
  216. for u, v in combinations(G, 2):
  217. # Adjacent vertices must be within the given distance.
  218. if v in G[u]:
  219. assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
  220. def test_p(self):
  221. """Tests for providing an alternate distance metric to the
  222. generator.
  223. """
  224. # Use the L1 metric.
  225. def dist(x, y):
  226. return sum(abs(a - b) for a, b in zip(x, y))
  227. G = nx.thresholded_random_geometric_graph(50, 0.25, 0.1, p=1)
  228. for u, v in combinations(G, 2):
  229. # Adjacent vertices must be within the given distance.
  230. if v in G[u]:
  231. assert dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
  232. def test_node_names(self):
  233. """Tests using values other than sequential numbers as node IDs."""
  234. import string
  235. nodes = list(string.ascii_lowercase)
  236. G = nx.thresholded_random_geometric_graph(nodes, 0.25, 0.1)
  237. assert len(G) == len(nodes)
  238. for u, v in combinations(G, 2):
  239. # Adjacent vertices must be within the given distance.
  240. if v in G[u]:
  241. assert math.dist(G.nodes[u]["pos"], G.nodes[v]["pos"]) <= 0.25
  242. def test_theta(self):
  243. """Tests that pairs of vertices adjacent if and only if their sum
  244. weights exceeds the threshold parameter theta.
  245. """
  246. G = nx.thresholded_random_geometric_graph(50, 0.25, 0.1)
  247. for u, v in combinations(G, 2):
  248. # Adjacent vertices must be within the given distance.
  249. if v in G[u]:
  250. assert (G.nodes[u]["weight"] + G.nodes[v]["weight"]) >= 0.1
  251. def test_geometric_edges_raises_no_pos():
  252. G = nx.path_graph(3)
  253. msg = "All nodes in `G` must have a 'pos' attribute"
  254. with pytest.raises(nx.NetworkXError, match=msg):
  255. nx.geometric_edges(G, radius=1)