test_convert_scipy.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. import pytest
  2. import networkx as nx
  3. from networkx.utils import graphs_equal
  4. np = pytest.importorskip("numpy")
  5. sp = pytest.importorskip("scipy")
  6. class TestConvertScipy:
  7. def setup_method(self):
  8. self.G1 = nx.barbell_graph(10, 3)
  9. self.G2 = nx.cycle_graph(10, create_using=nx.DiGraph)
  10. self.G3 = self.create_weighted(nx.Graph())
  11. self.G4 = self.create_weighted(nx.DiGraph())
  12. def test_exceptions(self):
  13. class G:
  14. format = None
  15. pytest.raises(nx.NetworkXError, nx.to_networkx_graph, G)
  16. def create_weighted(self, G):
  17. g = nx.cycle_graph(4)
  18. e = list(g.edges())
  19. source = [u for u, v in e]
  20. dest = [v for u, v in e]
  21. weight = [s + 10 for s in source]
  22. ex = zip(source, dest, weight)
  23. G.add_weighted_edges_from(ex)
  24. return G
  25. def identity_conversion(self, G, A, create_using):
  26. GG = nx.from_scipy_sparse_array(A, create_using=create_using)
  27. assert nx.is_isomorphic(G, GG)
  28. GW = nx.to_networkx_graph(A, create_using=create_using)
  29. assert nx.is_isomorphic(G, GW)
  30. GI = nx.empty_graph(0, create_using).__class__(A)
  31. assert nx.is_isomorphic(G, GI)
  32. ACSR = A.tocsr()
  33. GI = nx.empty_graph(0, create_using).__class__(ACSR)
  34. assert nx.is_isomorphic(G, GI)
  35. ACOO = A.tocoo()
  36. GI = nx.empty_graph(0, create_using).__class__(ACOO)
  37. assert nx.is_isomorphic(G, GI)
  38. ACSC = A.tocsc()
  39. GI = nx.empty_graph(0, create_using).__class__(ACSC)
  40. assert nx.is_isomorphic(G, GI)
  41. AD = A.todense()
  42. GI = nx.empty_graph(0, create_using).__class__(AD)
  43. assert nx.is_isomorphic(G, GI)
  44. AA = A.toarray()
  45. GI = nx.empty_graph(0, create_using).__class__(AA)
  46. assert nx.is_isomorphic(G, GI)
  47. def test_shape(self):
  48. "Conversion from non-square sparse array."
  49. A = sp.sparse.lil_array([[1, 2, 3], [4, 5, 6]])
  50. pytest.raises(nx.NetworkXError, nx.from_scipy_sparse_array, A)
  51. def test_identity_graph_matrix(self):
  52. "Conversion from graph to sparse matrix to graph."
  53. A = nx.to_scipy_sparse_array(self.G1)
  54. self.identity_conversion(self.G1, A, nx.Graph())
  55. def test_identity_digraph_matrix(self):
  56. "Conversion from digraph to sparse matrix to digraph."
  57. A = nx.to_scipy_sparse_array(self.G2)
  58. self.identity_conversion(self.G2, A, nx.DiGraph())
  59. def test_identity_weighted_graph_matrix(self):
  60. """Conversion from weighted graph to sparse matrix to weighted graph."""
  61. A = nx.to_scipy_sparse_array(self.G3)
  62. self.identity_conversion(self.G3, A, nx.Graph())
  63. def test_identity_weighted_digraph_matrix(self):
  64. """Conversion from weighted digraph to sparse matrix to weighted digraph."""
  65. A = nx.to_scipy_sparse_array(self.G4)
  66. self.identity_conversion(self.G4, A, nx.DiGraph())
  67. def test_nodelist(self):
  68. """Conversion from graph to sparse matrix to graph with nodelist."""
  69. P4 = nx.path_graph(4)
  70. P3 = nx.path_graph(3)
  71. nodelist = list(P3.nodes())
  72. A = nx.to_scipy_sparse_array(P4, nodelist=nodelist)
  73. GA = nx.Graph(A)
  74. assert nx.is_isomorphic(GA, P3)
  75. pytest.raises(nx.NetworkXError, nx.to_scipy_sparse_array, P3, nodelist=[])
  76. # Test nodelist duplicates.
  77. long_nl = nodelist + [0]
  78. pytest.raises(nx.NetworkXError, nx.to_scipy_sparse_array, P3, nodelist=long_nl)
  79. # Test nodelist contains non-nodes
  80. non_nl = [-1, 0, 1, 2]
  81. pytest.raises(nx.NetworkXError, nx.to_scipy_sparse_array, P3, nodelist=non_nl)
  82. def test_weight_keyword(self):
  83. WP4 = nx.Graph()
  84. WP4.add_edges_from((n, n + 1, {"weight": 0.5, "other": 0.3}) for n in range(3))
  85. P4 = nx.path_graph(4)
  86. A = nx.to_scipy_sparse_array(P4)
  87. np.testing.assert_equal(
  88. A.todense(), nx.to_scipy_sparse_array(WP4, weight=None).todense()
  89. )
  90. np.testing.assert_equal(
  91. 0.5 * A.todense(), nx.to_scipy_sparse_array(WP4).todense()
  92. )
  93. np.testing.assert_equal(
  94. 0.3 * A.todense(), nx.to_scipy_sparse_array(WP4, weight="other").todense()
  95. )
  96. def test_format_keyword(self):
  97. WP4 = nx.Graph()
  98. WP4.add_edges_from((n, n + 1, {"weight": 0.5, "other": 0.3}) for n in range(3))
  99. P4 = nx.path_graph(4)
  100. A = nx.to_scipy_sparse_array(P4, format="csr")
  101. np.testing.assert_equal(
  102. A.todense(), nx.to_scipy_sparse_array(WP4, weight=None).todense()
  103. )
  104. A = nx.to_scipy_sparse_array(P4, format="csc")
  105. np.testing.assert_equal(
  106. A.todense(), nx.to_scipy_sparse_array(WP4, weight=None).todense()
  107. )
  108. A = nx.to_scipy_sparse_array(P4, format="coo")
  109. np.testing.assert_equal(
  110. A.todense(), nx.to_scipy_sparse_array(WP4, weight=None).todense()
  111. )
  112. A = nx.to_scipy_sparse_array(P4, format="bsr")
  113. np.testing.assert_equal(
  114. A.todense(), nx.to_scipy_sparse_array(WP4, weight=None).todense()
  115. )
  116. A = nx.to_scipy_sparse_array(P4, format="lil")
  117. np.testing.assert_equal(
  118. A.todense(), nx.to_scipy_sparse_array(WP4, weight=None).todense()
  119. )
  120. A = nx.to_scipy_sparse_array(P4, format="dia")
  121. np.testing.assert_equal(
  122. A.todense(), nx.to_scipy_sparse_array(WP4, weight=None).todense()
  123. )
  124. A = nx.to_scipy_sparse_array(P4, format="dok")
  125. np.testing.assert_equal(
  126. A.todense(), nx.to_scipy_sparse_array(WP4, weight=None).todense()
  127. )
  128. def test_format_keyword_raise(self):
  129. with pytest.raises(nx.NetworkXError):
  130. WP4 = nx.Graph()
  131. WP4.add_edges_from(
  132. (n, n + 1, {"weight": 0.5, "other": 0.3}) for n in range(3)
  133. )
  134. P4 = nx.path_graph(4)
  135. nx.to_scipy_sparse_array(P4, format="any_other")
  136. def test_null_raise(self):
  137. with pytest.raises(nx.NetworkXError):
  138. nx.to_scipy_sparse_array(nx.Graph())
  139. def test_empty(self):
  140. G = nx.Graph()
  141. G.add_node(1)
  142. M = nx.to_scipy_sparse_array(G)
  143. np.testing.assert_equal(M.toarray(), np.array([[0]]))
  144. def test_ordering(self):
  145. G = nx.DiGraph()
  146. G.add_edge(1, 2)
  147. G.add_edge(2, 3)
  148. G.add_edge(3, 1)
  149. M = nx.to_scipy_sparse_array(G, nodelist=[3, 2, 1])
  150. np.testing.assert_equal(
  151. M.toarray(), np.array([[0, 0, 1], [1, 0, 0], [0, 1, 0]])
  152. )
  153. def test_selfloop_graph(self):
  154. G = nx.Graph([(1, 1)])
  155. M = nx.to_scipy_sparse_array(G)
  156. np.testing.assert_equal(M.toarray(), np.array([[1]]))
  157. G.add_edges_from([(2, 3), (3, 4)])
  158. M = nx.to_scipy_sparse_array(G, nodelist=[2, 3, 4])
  159. np.testing.assert_equal(
  160. M.toarray(), np.array([[0, 1, 0], [1, 0, 1], [0, 1, 0]])
  161. )
  162. def test_selfloop_digraph(self):
  163. G = nx.DiGraph([(1, 1)])
  164. M = nx.to_scipy_sparse_array(G)
  165. np.testing.assert_equal(M.toarray(), np.array([[1]]))
  166. G.add_edges_from([(2, 3), (3, 4)])
  167. M = nx.to_scipy_sparse_array(G, nodelist=[2, 3, 4])
  168. np.testing.assert_equal(
  169. M.toarray(), np.array([[0, 1, 0], [0, 0, 1], [0, 0, 0]])
  170. )
  171. def test_from_scipy_sparse_array_parallel_edges(self):
  172. """Tests that the :func:`networkx.from_scipy_sparse_array` function
  173. interprets integer weights as the number of parallel edges when
  174. creating a multigraph.
  175. """
  176. A = sp.sparse.csr_array([[1, 1], [1, 2]])
  177. # First, with a simple graph, each integer entry in the adjacency
  178. # matrix is interpreted as the weight of a single edge in the graph.
  179. expected = nx.DiGraph()
  180. edges = [(0, 0), (0, 1), (1, 0)]
  181. expected.add_weighted_edges_from([(u, v, 1) for (u, v) in edges])
  182. expected.add_edge(1, 1, weight=2)
  183. actual = nx.from_scipy_sparse_array(
  184. A, parallel_edges=True, create_using=nx.DiGraph
  185. )
  186. assert graphs_equal(actual, expected)
  187. actual = nx.from_scipy_sparse_array(
  188. A, parallel_edges=False, create_using=nx.DiGraph
  189. )
  190. assert graphs_equal(actual, expected)
  191. # Now each integer entry in the adjacency matrix is interpreted as the
  192. # number of parallel edges in the graph if the appropriate keyword
  193. # argument is specified.
  194. edges = [(0, 0), (0, 1), (1, 0), (1, 1), (1, 1)]
  195. expected = nx.MultiDiGraph()
  196. expected.add_weighted_edges_from([(u, v, 1) for (u, v) in edges])
  197. actual = nx.from_scipy_sparse_array(
  198. A, parallel_edges=True, create_using=nx.MultiDiGraph
  199. )
  200. assert graphs_equal(actual, expected)
  201. expected = nx.MultiDiGraph()
  202. expected.add_edges_from(set(edges), weight=1)
  203. # The sole self-loop (edge 0) on vertex 1 should have weight 2.
  204. expected[1][1][0]["weight"] = 2
  205. actual = nx.from_scipy_sparse_array(
  206. A, parallel_edges=False, create_using=nx.MultiDiGraph
  207. )
  208. assert graphs_equal(actual, expected)
  209. def test_symmetric(self):
  210. """Tests that a symmetric matrix has edges added only once to an
  211. undirected multigraph when using
  212. :func:`networkx.from_scipy_sparse_array`.
  213. """
  214. A = sp.sparse.csr_array([[0, 1], [1, 0]])
  215. G = nx.from_scipy_sparse_array(A, create_using=nx.MultiGraph)
  216. expected = nx.MultiGraph()
  217. expected.add_edge(0, 1, weight=1)
  218. assert graphs_equal(G, expected)
  219. @pytest.mark.parametrize("sparse_format", ("csr", "csc", "dok"))
  220. def test_from_scipy_sparse_array_formats(sparse_format):
  221. """Test all formats supported by _generate_weighted_edges."""
  222. # trinode complete graph with non-uniform edge weights
  223. expected = nx.Graph()
  224. expected.add_edges_from(
  225. [
  226. (0, 1, {"weight": 3}),
  227. (0, 2, {"weight": 2}),
  228. (1, 0, {"weight": 3}),
  229. (1, 2, {"weight": 1}),
  230. (2, 0, {"weight": 2}),
  231. (2, 1, {"weight": 1}),
  232. ]
  233. )
  234. A = sp.sparse.coo_array([[0, 3, 2], [3, 0, 1], [2, 1, 0]]).asformat(sparse_format)
  235. assert graphs_equal(expected, nx.from_scipy_sparse_array(A))