test_misc.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. import random
  2. from copy import copy
  3. import pytest
  4. import networkx as nx
  5. from networkx.utils import (
  6. PythonRandomInterface,
  7. PythonRandomViaNumpyBits,
  8. arbitrary_element,
  9. create_py_random_state,
  10. create_random_state,
  11. dict_to_numpy_array,
  12. discrete_sequence,
  13. edges_equal,
  14. flatten,
  15. groups,
  16. make_list_of_ints,
  17. pairwise,
  18. powerlaw_sequence,
  19. )
  20. from networkx.utils.misc import _dict_to_numpy_array1, _dict_to_numpy_array2
  21. nested_depth = (
  22. 1,
  23. 2,
  24. (3, 4, ((5, 6, (7,), (8, (9, 10), 11), (12, 13, (14, 15)), 16), 17), 18, 19),
  25. 20,
  26. )
  27. nested_set = {
  28. (1, 2, 3, 4),
  29. (5, 6, 7, 8, 9),
  30. (10, 11, (12, 13, 14), (15, 16, 17, 18)),
  31. 19,
  32. 20,
  33. }
  34. nested_mixed = [
  35. 1,
  36. (2, 3, {4, (5, 6), 7}, [8, 9]),
  37. {10: "foo", 11: "bar", (12, 13): "baz"},
  38. {(14, 15): "qwe", 16: "asd"},
  39. (17, (18, "19"), 20),
  40. ]
  41. @pytest.mark.parametrize("result", [None, [], ["existing"], ["existing1", "existing2"]])
  42. @pytest.mark.parametrize("nested", [nested_depth, nested_mixed, nested_set])
  43. def test_flatten(nested, result):
  44. if result is None:
  45. val = flatten(nested, result)
  46. assert len(val) == 20
  47. else:
  48. _result = copy(result) # because pytest passes parameters as is
  49. nexisting = len(_result)
  50. val = flatten(nested, _result)
  51. assert len(val) == len(_result) == 20 + nexisting
  52. assert issubclass(type(val), tuple)
  53. def test_make_list_of_ints():
  54. mylist = [1, 2, 3.0, 42, -2]
  55. assert make_list_of_ints(mylist) is mylist
  56. assert make_list_of_ints(mylist) == mylist
  57. assert isinstance(make_list_of_ints(mylist)[2], int)
  58. pytest.raises(nx.NetworkXError, make_list_of_ints, [1, 2, 3, "kermit"])
  59. pytest.raises(nx.NetworkXError, make_list_of_ints, [1, 2, 3.1])
  60. def test_random_number_distribution():
  61. # smoke test only
  62. z = powerlaw_sequence(20, exponent=2.5)
  63. z = discrete_sequence(20, distribution=[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3])
  64. class TestNumpyArray:
  65. @classmethod
  66. def setup_class(cls):
  67. global np
  68. np = pytest.importorskip("numpy")
  69. def test_numpy_to_list_of_ints(self):
  70. a = np.array([1, 2, 3], dtype=np.int64)
  71. b = np.array([1.0, 2, 3])
  72. c = np.array([1.1, 2, 3])
  73. assert isinstance(make_list_of_ints(a), list)
  74. assert make_list_of_ints(b) == list(b)
  75. B = make_list_of_ints(b)
  76. assert isinstance(B[0], int)
  77. pytest.raises(nx.NetworkXError, make_list_of_ints, c)
  78. def test__dict_to_numpy_array1(self):
  79. d = {"a": 1, "b": 2}
  80. a = _dict_to_numpy_array1(d, mapping={"a": 0, "b": 1})
  81. np.testing.assert_allclose(a, np.array([1, 2]))
  82. a = _dict_to_numpy_array1(d, mapping={"b": 0, "a": 1})
  83. np.testing.assert_allclose(a, np.array([2, 1]))
  84. a = _dict_to_numpy_array1(d)
  85. np.testing.assert_allclose(a.sum(), 3)
  86. def test__dict_to_numpy_array2(self):
  87. d = {"a": {"a": 1, "b": 2}, "b": {"a": 10, "b": 20}}
  88. mapping = {"a": 1, "b": 0}
  89. a = _dict_to_numpy_array2(d, mapping=mapping)
  90. np.testing.assert_allclose(a, np.array([[20, 10], [2, 1]]))
  91. a = _dict_to_numpy_array2(d)
  92. np.testing.assert_allclose(a.sum(), 33)
  93. def test_dict_to_numpy_array_a(self):
  94. d = {"a": {"a": 1, "b": 2}, "b": {"a": 10, "b": 20}}
  95. mapping = {"a": 0, "b": 1}
  96. a = dict_to_numpy_array(d, mapping=mapping)
  97. np.testing.assert_allclose(a, np.array([[1, 2], [10, 20]]))
  98. mapping = {"a": 1, "b": 0}
  99. a = dict_to_numpy_array(d, mapping=mapping)
  100. np.testing.assert_allclose(a, np.array([[20, 10], [2, 1]]))
  101. a = _dict_to_numpy_array2(d)
  102. np.testing.assert_allclose(a.sum(), 33)
  103. def test_dict_to_numpy_array_b(self):
  104. d = {"a": 1, "b": 2}
  105. mapping = {"a": 0, "b": 1}
  106. a = dict_to_numpy_array(d, mapping=mapping)
  107. np.testing.assert_allclose(a, np.array([1, 2]))
  108. a = _dict_to_numpy_array1(d)
  109. np.testing.assert_allclose(a.sum(), 3)
  110. def test_pairwise():
  111. nodes = range(4)
  112. node_pairs = [(0, 1), (1, 2), (2, 3)]
  113. node_pairs_cycle = node_pairs + [(3, 0)]
  114. assert list(pairwise(nodes)) == node_pairs
  115. assert list(pairwise(iter(nodes))) == node_pairs
  116. assert list(pairwise(nodes, cyclic=True)) == node_pairs_cycle
  117. empty_iter = iter(())
  118. assert list(pairwise(empty_iter)) == []
  119. empty_iter = iter(())
  120. assert list(pairwise(empty_iter, cyclic=True)) == []
  121. def test_groups():
  122. many_to_one = dict(zip("abcde", [0, 0, 1, 1, 2]))
  123. actual = groups(many_to_one)
  124. expected = {0: {"a", "b"}, 1: {"c", "d"}, 2: {"e"}}
  125. assert actual == expected
  126. assert {} == groups({})
  127. def test_create_random_state():
  128. np = pytest.importorskip("numpy")
  129. rs = np.random.RandomState
  130. assert isinstance(create_random_state(1), rs)
  131. assert isinstance(create_random_state(None), rs)
  132. assert isinstance(create_random_state(np.random), rs)
  133. assert isinstance(create_random_state(rs(1)), rs)
  134. # Support for numpy.random.Generator
  135. rng = np.random.default_rng()
  136. assert isinstance(create_random_state(rng), np.random.Generator)
  137. pytest.raises(ValueError, create_random_state, "a")
  138. assert np.all(rs(1).rand(10) == create_random_state(1).rand(10))
  139. def test_create_py_random_state():
  140. pyrs = random.Random
  141. assert isinstance(create_py_random_state(1), pyrs)
  142. assert isinstance(create_py_random_state(None), pyrs)
  143. assert isinstance(create_py_random_state(pyrs(1)), pyrs)
  144. pytest.raises(ValueError, create_py_random_state, "a")
  145. np = pytest.importorskip("numpy")
  146. rs = np.random.RandomState
  147. rng = np.random.default_rng(1000)
  148. rng_explicit = np.random.Generator(np.random.SFC64())
  149. old_nprs = PythonRandomInterface
  150. nprs = PythonRandomViaNumpyBits
  151. assert isinstance(create_py_random_state(np.random), nprs)
  152. assert isinstance(create_py_random_state(rs(1)), old_nprs)
  153. assert isinstance(create_py_random_state(rng), nprs)
  154. assert isinstance(create_py_random_state(rng_explicit), nprs)
  155. # test default rng input
  156. old_nprs_instance = old_nprs()
  157. nprs_instance = nprs()
  158. assert isinstance(old_nprs_instance, old_nprs)
  159. assert isinstance(nprs_instance, nprs)
  160. assert create_py_random_state(old_nprs_instance) == old_nprs_instance
  161. assert create_py_random_state(nprs_instance) == nprs_instance
  162. # VeryLargeIntegers Smoke test (they raise error for np.random)
  163. int64max = 9223372036854775807 # from np.iinfo(np.int64).max
  164. for r in (rng, rs(1)):
  165. prs = create_py_random_state(r)
  166. prs.randrange(3, int64max + 5)
  167. prs.randint(3, int64max + 5)
  168. def test_PythonRandomInterface_RandomState():
  169. np = pytest.importorskip("numpy")
  170. seed = 42
  171. rs = np.random.RandomState
  172. rng = PythonRandomInterface(rs(seed))
  173. rs42 = rs(seed)
  174. # make sure these functions are same as expected outcome
  175. assert rng.randrange(3, 5) == rs42.randint(3, 5)
  176. assert rng.randrange(2) == rs42.randint(0, 2)
  177. assert rng.uniform(1, 10) == rs42.uniform(1, 10)
  178. assert rng.choice([1, 2, 3]) == rs42.choice([1, 2, 3])
  179. assert rng.gauss(0, 1) == rs42.normal(0, 1)
  180. assert rng.expovariate(1.5) == rs42.exponential(1 / 1.5)
  181. assert rng.paretovariate(2) == rs42.pareto(2)
  182. assert np.all(rng.shuffle([1, 2, 3]) == rs42.shuffle([1, 2, 3]))
  183. assert np.all(
  184. rng.sample([1, 2, 3], 2) == rs42.choice([1, 2, 3], (2,), replace=False)
  185. )
  186. assert np.all(
  187. [rng.randint(3, 5) for _ in range(100)]
  188. == [rs42.randint(3, 6) for _ in range(100)]
  189. )
  190. assert rng.random() == rs42.random_sample()
  191. def test_PythonRandomInterface_Generator():
  192. np = pytest.importorskip("numpy")
  193. seed = 42
  194. rng = np.random.default_rng(seed)
  195. pri = PythonRandomInterface(np.random.default_rng(seed))
  196. # make sure these functions are same as expected outcome
  197. assert pri.randrange(3, 5) == rng.integers(3, 5)
  198. assert pri.randrange(2) == rng.integers(0, 2)
  199. assert pri.uniform(1, 10) == rng.uniform(1, 10)
  200. assert pri.choice([1, 2, 3]) == rng.choice([1, 2, 3])
  201. assert pri.gauss(0, 1) == rng.normal(0, 1)
  202. assert pri.expovariate(1.5) == rng.exponential(1 / 1.5)
  203. assert pri.paretovariate(2) == rng.pareto(2)
  204. assert np.all(pri.shuffle([1, 2, 3]) == rng.shuffle([1, 2, 3]))
  205. assert np.all(
  206. pri.sample([1, 2, 3], 2) == rng.choice([1, 2, 3], (2,), replace=False)
  207. )
  208. assert np.all(
  209. [pri.randint(3, 5) for _ in range(100)]
  210. == [rng.integers(3, 6) for _ in range(100)]
  211. )
  212. assert pri.random() == rng.random()
  213. @pytest.mark.parametrize(
  214. ("iterable_type", "expected"), ((list, 1), (tuple, 1), (str, "["), (set, 1))
  215. )
  216. def test_arbitrary_element(iterable_type, expected):
  217. iterable = iterable_type([1, 2, 3])
  218. assert arbitrary_element(iterable) == expected
  219. @pytest.mark.parametrize(
  220. "iterator",
  221. ((i for i in range(3)), iter([1, 2, 3])), # generator
  222. )
  223. def test_arbitrary_element_raises(iterator):
  224. """Value error is raised when input is an iterator."""
  225. with pytest.raises(ValueError, match="from an iterator"):
  226. arbitrary_element(iterator)
  227. @pytest.mark.parametrize("n", [5, 10, 20])
  228. @pytest.mark.parametrize("gen", [nx.complete_graph, nx.path_graph, nx.cycle_graph])
  229. @pytest.mark.parametrize("create_using", [nx.Graph, nx.DiGraph])
  230. def test_edges_equal(n, gen, create_using):
  231. """Test whether edges_equal properly compares edges without attribute data."""
  232. G = gen(n, create_using=create_using)
  233. H = gen(n, create_using=create_using)
  234. assert edges_equal(G.edges(), H.edges(), directed=G.is_directed())
  235. assert edges_equal(H.edges(), G.edges(), directed=H.is_directed())
  236. H.remove_edge(0, 1)
  237. assert edges_equal(H.edges(), H.edges(), directed=H.is_directed())
  238. assert not edges_equal(G.edges(), H.edges(), directed=G.is_directed())
  239. assert not edges_equal(H.edges(), G.edges(), directed=H.is_directed())
  240. @pytest.mark.parametrize("n", [5, 10, 20])
  241. @pytest.mark.parametrize("gen", [nx.complete_graph, nx.path_graph, nx.cycle_graph])
  242. @pytest.mark.parametrize("create_using", [nx.MultiGraph, nx.MultiDiGraph])
  243. def test_edges_equal_multiedge(n, gen, create_using):
  244. """Test whether ``edges_equal`` properly compares edges in multigraphs."""
  245. G = gen(n, create_using=create_using)
  246. H = gen(n, create_using=create_using)
  247. G_edges = list(G.edges())
  248. G.add_edges_from(G_edges)
  249. H.add_edges_from(G_edges)
  250. assert edges_equal(G.edges(), H.edges(), directed=G.is_directed())
  251. H.remove_edge(0, 1)
  252. assert edges_equal(H.edges(), H.edges(), directed=H.is_directed())
  253. assert not edges_equal(G.edges(), H.edges(), directed=G.is_directed())
  254. @pytest.mark.parametrize("n", [5, 10, 20])
  255. @pytest.mark.parametrize("gen", [nx.complete_graph, nx.path_graph, nx.cycle_graph])
  256. @pytest.mark.parametrize("weight", [1, 2, 3])
  257. def test_edges_equal_weighted(n, gen, weight):
  258. """Test whether ``edges_equal`` properly compares edges with weight data."""
  259. G = gen(n)
  260. H = gen(n)
  261. G_edges = list(G.edges())
  262. G.add_weighted_edges_from((*e, weight) for e in G_edges)
  263. assert edges_equal(G.edges(), G.edges())
  264. H.add_weighted_edges_from((*e, weight + 1) for e in G_edges)
  265. assert edges_equal(H.edges(), H.edges())
  266. assert not edges_equal(G.edges(data=True), H.edges(data=True))
  267. def test_edges_equal_data():
  268. """Test whether ``edges_equal`` properly compares edges with attribute dictionaries."""
  269. G = nx.path_graph(3)
  270. H = nx.path_graph(3)
  271. I = nx.path_graph(3, create_using=nx.MultiGraph)
  272. attrs = {(0, 1): {"attr1": 20, "attr2": "nothing"}, (1, 2): {"attr2": 3}}
  273. nx.set_edge_attributes(G, attrs)
  274. assert edges_equal(G.edges(data=True), G.edges(data=True))
  275. assert not edges_equal(G.edges(data=True), G.edges())
  276. nx.set_edge_attributes(H, attrs)
  277. assert edges_equal(G.edges(), H.edges())
  278. assert edges_equal(G.edges(data=True), H.edges(data=True))
  279. H[0][1]["attr2"] = "something"
  280. assert edges_equal(G.edges(), H.edges())
  281. assert not edges_equal(G.edges(data=True), H.edges(data=True))
  282. def test_edges_equal_multigraph_data():
  283. """Test whether ``edges_equal`` properly compares edges with attribute dictionaries in ``MultiGraphs``."""
  284. G = nx.path_graph(3, create_using=nx.MultiGraph)
  285. I = nx.path_graph(3, create_using=nx.MultiGraph)
  286. G.add_edge(0, 1, 0, attr1="blue")
  287. G.add_edge(1, 2, 1, attr2="green")
  288. I.add_edge(0, 1, 0, attr1="blue")
  289. I.add_edge(0, 1, 1, attr2="green")
  290. assert edges_equal(G.edges(data=True), G.edges(data=True))
  291. assert not edges_equal(G.edges(), I.edges())
  292. assert not edges_equal(G.edges(data=True), I.edges(data=True))
  293. assert not edges_equal(G.edges(keys=True), I.edges(keys=True))
  294. assert not edges_equal(G.edges(keys=True, data=True), I.edges(keys=True, data=True))
  295. def test_edges_equal_directed():
  296. """Test whether ``edges_equal`` properly compares directed edges."""
  297. G = nx.DiGraph([(0, 1)])
  298. I = nx.DiGraph([(1, 0)])
  299. assert edges_equal(G.edges(), I.edges(), directed=False)
  300. assert not edges_equal(G.edges(), I.edges(), directed=True)
  301. def test_edges_equal_directed_data():
  302. """Test whether ``edges_equal`` properly compares directed edges with attribute dictionaries."""
  303. G = nx.DiGraph()
  304. I = nx.DiGraph()
  305. G.add_edge(0, 1, attr1="blue")
  306. I.add_edge(0, 1, attr1="blue")
  307. assert edges_equal(G.edges(data=True), G.edges(data=True), directed=True)
  308. I.add_edge(1, 2, attr2="green")
  309. assert not edges_equal(G.edges(data=True), I.edges(data=True), directed=True)
  310. G.add_edge(1, 2, attr2="green")
  311. assert edges_equal(G.edges(data=True), I.edges(data=True), directed=True)
  312. G.remove_edge(1, 2)
  313. G.add_edge(2, 1, attr2="green")
  314. assert edges_equal(G.edges(data=True), I.edges(data=True), directed=False)
  315. assert not edges_equal(G.edges(data=True), I.edges(data=True), directed=True)