test_construct.py 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154
  1. """test sparse matrix construction functions"""
  2. import numpy as np
  3. from numpy import array
  4. from numpy.testing import (assert_equal, assert_,
  5. assert_array_equal, assert_array_almost_equal_nulp)
  6. import pytest
  7. from pytest import raises as assert_raises
  8. from scipy._lib._testutils import check_free_memory
  9. from scipy.sparse import (csr_matrix, coo_matrix,
  10. csr_array, coo_array,
  11. csc_array, bsr_array,
  12. dia_array, dok_array,
  13. lil_array, csc_matrix,
  14. bsr_matrix, dia_matrix,
  15. lil_matrix, sparray, spmatrix,
  16. _construct as construct)
  17. from scipy.sparse._construct import rand as sprand
  18. sparse_formats = ['csr','csc','coo','bsr','dia','lil','dok']
  19. #TODO check whether format=XXX is respected
  20. def _sprandn(m, n, density=0.01, format="coo", dtype=None, rng=None):
  21. # Helper function for testing.
  22. rng = np.random.default_rng(rng)
  23. data_rvs = rng.standard_normal
  24. return construct.random(m, n, density, format, dtype, rng, data_rvs)
  25. def _sprandn_array(m, n, density=0.01, format="coo", dtype=None, rng=None):
  26. # Helper function for testing.
  27. rng = np.random.default_rng(rng)
  28. data_sampler = rng.standard_normal
  29. return construct.random_array((m, n), density=density, format=format, dtype=dtype,
  30. rng=rng, data_sampler=data_sampler)
  31. class TestConstructUtils:
  32. @pytest.mark.parametrize("cls", [
  33. csc_array, csr_array, coo_array, bsr_array,
  34. dia_array, dok_array, lil_array
  35. ])
  36. def test_singleton_array_constructor(self, cls):
  37. with pytest.raises(
  38. ValueError,
  39. match=(
  40. 'scipy sparse array classes do not support '
  41. 'instantiation from a scalar'
  42. )
  43. ):
  44. cls(0)
  45. @pytest.mark.parametrize("cls", [
  46. csc_matrix, csr_matrix, coo_matrix,
  47. bsr_matrix, dia_matrix, lil_matrix
  48. ])
  49. def test_singleton_matrix_constructor(self, cls):
  50. """
  51. This test is for backwards compatibility post scipy 1.13.
  52. The behavior observed here is what is to be expected
  53. with the older matrix classes. This test comes with the
  54. exception of dok_matrix, which was not working pre scipy1.12
  55. (unlike the rest of these).
  56. """
  57. assert cls(0).shape == (1, 1)
  58. def test_spdiags(self):
  59. diags1 = array([[1, 2, 3, 4, 5]])
  60. diags2 = array([[1, 2, 3, 4, 5],
  61. [6, 7, 8, 9,10]])
  62. diags3 = array([[1, 2, 3, 4, 5],
  63. [6, 7, 8, 9,10],
  64. [11,12,13,14,15]])
  65. cases = []
  66. cases.append((diags1, 0, 1, 1, [[1]]))
  67. cases.append((diags1, [0], 1, 1, [[1]]))
  68. cases.append((diags1, [0], 2, 1, [[1],[0]]))
  69. cases.append((diags1, [0], 1, 2, [[1,0]]))
  70. cases.append((diags1, [1], 1, 2, [[0,2]]))
  71. cases.append((diags1,[-1], 1, 2, [[0,0]]))
  72. cases.append((diags1, [0], 2, 2, [[1,0],[0,2]]))
  73. cases.append((diags1,[-1], 2, 2, [[0,0],[1,0]]))
  74. cases.append((diags1, [3], 2, 2, [[0,0],[0,0]]))
  75. cases.append((diags1, [0], 3, 4, [[1,0,0,0],[0,2,0,0],[0,0,3,0]]))
  76. cases.append((diags1, [1], 3, 4, [[0,2,0,0],[0,0,3,0],[0,0,0,4]]))
  77. cases.append((diags1, [2], 3, 5, [[0,0,3,0,0],[0,0,0,4,0],[0,0,0,0,5]]))
  78. cases.append((diags2, [0,2], 3, 3, [[1,0,8],[0,2,0],[0,0,3]]))
  79. cases.append((diags2, [-1,0], 3, 4, [[6,0,0,0],[1,7,0,0],[0,2,8,0]]))
  80. cases.append((diags2, [2,-3], 6, 6, [[0,0,3,0,0,0],
  81. [0,0,0,4,0,0],
  82. [0,0,0,0,5,0],
  83. [6,0,0,0,0,0],
  84. [0,7,0,0,0,0],
  85. [0,0,8,0,0,0]]))
  86. cases.append((diags3, [-1,0,1], 6, 6, [[6,12, 0, 0, 0, 0],
  87. [1, 7,13, 0, 0, 0],
  88. [0, 2, 8,14, 0, 0],
  89. [0, 0, 3, 9,15, 0],
  90. [0, 0, 0, 4,10, 0],
  91. [0, 0, 0, 0, 5, 0]]))
  92. cases.append((diags3, [-4,2,-1], 6, 5, [[0, 0, 8, 0, 0],
  93. [11, 0, 0, 9, 0],
  94. [0,12, 0, 0,10],
  95. [0, 0,13, 0, 0],
  96. [1, 0, 0,14, 0],
  97. [0, 2, 0, 0,15]]))
  98. cases.append((diags3, [-1, 1, 2], len(diags3[0]), len(diags3[0]),
  99. [[0, 7, 13, 0, 0],
  100. [1, 0, 8, 14, 0],
  101. [0, 2, 0, 9, 15],
  102. [0, 0, 3, 0, 10],
  103. [0, 0, 0, 4, 0]]))
  104. for d, o, m, n, result in cases:
  105. if len(d[0]) == m and m == n:
  106. assert_equal(construct.spdiags(d, o).toarray(), result)
  107. assert_equal(construct.spdiags(d, o, m, n).toarray(), result)
  108. assert_equal(construct.spdiags(d, o, (m, n)).toarray(), result)
  109. def test_diags(self):
  110. a = array([1.0, 2.0, 3.0, 4.0, 5.0])
  111. b = array([6.0, 7.0, 8.0, 9.0, 10.0])
  112. c = array([11.0, 12.0, 13.0, 14.0, 15.0])
  113. cases = []
  114. cases.append((a[:1], 0, (1, 1), [[1]]))
  115. cases.append(([a[:1]], [0], (1, 1), [[1]]))
  116. cases.append(([a[:1]], [0], (2, 1), [[1],[0]]))
  117. cases.append(([a[:1]], [0], (1, 2), [[1,0]]))
  118. cases.append(([a[:1]], [1], (1, 2), [[0,1]]))
  119. cases.append(([a[:2]], [0], (2, 2), [[1,0],[0,2]]))
  120. cases.append(([a[:1]],[-1], (2, 2), [[0,0],[1,0]]))
  121. cases.append(([a[:3]], [0], (3, 4), [[1,0,0,0],[0,2,0,0],[0,0,3,0]]))
  122. cases.append(([a[:3]], [1], (3, 4), [[0,1,0,0],[0,0,2,0],[0,0,0,3]]))
  123. cases.append(([a[:1]], [-2], (3, 5), [[0,0,0,0,0],[0,0,0,0,0],[1,0,0,0,0]]))
  124. cases.append(([a[:2]], [-1], (3, 5), [[0,0,0,0,0],[1,0,0,0,0],[0,2,0,0,0]]))
  125. cases.append(([a[:3]], [0], (3, 5), [[1,0,0,0,0],[0,2,0,0,0],[0,0,3,0,0]]))
  126. cases.append(([a[:3]], [1], (3, 5), [[0,1,0,0,0],[0,0,2,0,0],[0,0,0,3,0]]))
  127. cases.append(([a[:3]], [2], (3, 5), [[0,0,1,0,0],[0,0,0,2,0],[0,0,0,0,3]]))
  128. cases.append(([a[:2]], [3], (3, 5), [[0,0,0,1,0],[0,0,0,0,2],[0,0,0,0,0]]))
  129. cases.append(([a[:1]], [4], (3, 5), [[0,0,0,0,1],[0,0,0,0,0],[0,0,0,0,0]]))
  130. cases.append(([a[:1]], [-4], (5, 3), [[0,0,0],[0,0,0],[0,0,0],[0,0,0],[1,0,0]]))
  131. cases.append(([a[:2]], [-3], (5, 3), [[0,0,0],[0,0,0],[0,0,0],[1,0,0],[0,2,0]]))
  132. cases.append(([a[:3]], [-2], (5, 3), [[0,0,0],[0,0,0],[1,0,0],[0,2,0],[0,0,3]]))
  133. cases.append(([a[:3]], [-1], (5, 3), [[0,0,0],[1,0,0],[0,2,0],[0,0,3],[0,0,0]]))
  134. cases.append(([a[:3]], [0], (5, 3), [[1,0,0],[0,2,0],[0,0,3],[0,0,0],[0,0,0]]))
  135. cases.append(([a[:2]], [1], (5, 3), [[0,1,0],[0,0,2],[0,0,0],[0,0,0],[0,0,0]]))
  136. cases.append(([a[:1]], [2], (5, 3), [[0,0,1],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]))
  137. cases.append(([a[:3],b[:1]], [0,2], (3, 3), [[1,0,6],[0,2,0],[0,0,3]]))
  138. cases.append(([a[:2],b[:3]], [-1,0], (3, 4), [[6,0,0,0],[1,7,0,0],[0,2,8,0]]))
  139. cases.append(([a[:4],b[:3]], [2,-3], (6, 6), [[0,0,1,0,0,0],
  140. [0,0,0,2,0,0],
  141. [0,0,0,0,3,0],
  142. [6,0,0,0,0,4],
  143. [0,7,0,0,0,0],
  144. [0,0,8,0,0,0]]))
  145. cases.append(([a[:4],b,c[:4]], [-1,0,1], (5, 5), [[6,11, 0, 0, 0],
  146. [1, 7,12, 0, 0],
  147. [0, 2, 8,13, 0],
  148. [0, 0, 3, 9,14],
  149. [0, 0, 0, 4,10]]))
  150. cases.append(([a[:2],b[:3],c], [-4,2,-1], (6, 5), [[0, 0, 6, 0, 0],
  151. [11, 0, 0, 7, 0],
  152. [0,12, 0, 0, 8],
  153. [0, 0,13, 0, 0],
  154. [1, 0, 0,14, 0],
  155. [0, 2, 0, 0,15]]))
  156. # too long arrays are OK
  157. cases.append(([a], [0], (1, 1), [[1]]))
  158. cases.append(([a[:3],b], [0,2], (3, 3), [[1, 0, 6], [0, 2, 0], [0, 0, 3]]))
  159. cases.append((
  160. np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),
  161. [0,-1],
  162. (3, 3),
  163. [[1, 0, 0], [4, 2, 0], [0, 5, 3]]
  164. ))
  165. # scalar case: broadcasting
  166. cases.append(([1.0,-2.0,1.0], [1,0,-1], (3, 3), [[-2, 1, 0],
  167. [1, -2, 1],
  168. [0, 1, -2]]))
  169. for d, o, shape, result in cases:
  170. err_msg = f"{d!r} {o!r} {shape!r} {result!r}"
  171. assert_equal(construct.diags(d, offsets=o, shape=shape).toarray(),
  172. result, err_msg=err_msg)
  173. if (shape[0] == shape[1]
  174. and hasattr(d[0], '__len__')
  175. and len(d[0]) <= max(shape)):
  176. # should be able to find the shape automatically
  177. assert_equal(construct.diags(d, offsets=o).toarray(),
  178. result, err_msg=err_msg)
  179. def test_diags_default(self):
  180. a = array([1.0, 2.0, 3.0, 4.0, 5.0])
  181. assert_equal(construct.diags(a).toarray(), np.diag(a))
  182. def test_diags_default_bad(self):
  183. a = array([[1, 2, 3, 4, 5], [2, 3, 4, 5, 6]])
  184. assert_raises(ValueError, construct.diags, a)
  185. def test_diags_bad(self):
  186. a = array([1.0, 2.0, 3.0, 4.0, 5.0])
  187. b = array([6.0, 7.0, 8.0, 9.0, 10.0])
  188. c = array([11.0, 12.0, 13.0, 14.0, 15.0])
  189. cases = []
  190. cases.append(([a[:0]], 0, (1, 1)))
  191. cases.append(([a[:4],b,c[:3]], [-1,0,1], (5, 5)))
  192. cases.append(([a[:2],c,b[:3]], [-4,2,-1], (6, 5)))
  193. cases.append(([a[:2],c,b[:3]], [-4,2,-1], None))
  194. cases.append(([], [-4,2,-1], None))
  195. cases.append(([1.0], [-5], (4, 4)))
  196. cases.append(([a], 0, None))
  197. for d, o, shape in cases:
  198. assert_raises(ValueError, construct.diags, d, offsets=o, shape=shape)
  199. assert_raises(TypeError, construct.diags, [[None]], offsets=[0])
  200. def test_diags_vs_diag(self):
  201. # Check that
  202. #
  203. # diags([a, b, ...], [i, j, ...]) == diag(a, i) + diag(b, j) + ...
  204. #
  205. rng = np.random.RandomState(1234)
  206. for n_diags in [1, 2, 3, 4, 5, 10]:
  207. n = 1 + n_diags//2 + rng.randint(0, 10)
  208. offsets = np.arange(-n+1, n-1)
  209. rng.shuffle(offsets)
  210. offsets = offsets[:n_diags]
  211. diagonals = [rng.rand(n - abs(q)) for q in offsets]
  212. mat = construct.diags(diagonals, offsets=offsets)
  213. dense_mat = sum([np.diag(x, j) for x, j in zip(diagonals, offsets)])
  214. assert_array_almost_equal_nulp(mat.toarray(), dense_mat)
  215. if len(offsets) == 1:
  216. mat = construct.diags(diagonals[0], offsets=offsets[0])
  217. dense_mat = np.diag(diagonals[0], offsets[0])
  218. assert_array_almost_equal_nulp(mat.toarray(), dense_mat)
  219. def test_diags_dtype(self):
  220. x = construct.diags([2.2], offsets=[0], shape=(2, 2), dtype=int)
  221. assert_equal(x.dtype, int)
  222. assert_equal(x.toarray(), [[2, 0], [0, 2]])
  223. def test_diags_one_diagonal(self):
  224. d = [0.0, 1.0, 2.0, 3.0, 4.0]
  225. for k in range(-5, 6):
  226. assert_equal(construct.diags(d, offsets=k).toarray(),
  227. construct.diags([d], offsets=[k]).toarray())
  228. def test_diags_empty(self):
  229. x = construct.diags([])
  230. assert_equal(x.shape, (0, 0))
  231. @pytest.mark.parametrize("identity", [construct.identity, construct.eye_array])
  232. def test_identity(self, identity):
  233. assert_equal(identity(1).toarray(), [[1]])
  234. assert_equal(identity(2).toarray(), [[1,0],[0,1]])
  235. I = identity(3, dtype='int8', format='dia')
  236. assert_equal(I.dtype, np.dtype('int8'))
  237. assert_equal(I.format, 'dia')
  238. for fmt in sparse_formats:
  239. I = identity(3, format=fmt)
  240. assert_equal(I.format, fmt)
  241. assert_equal(I.toarray(), [[1,0,0],[0,1,0],[0,0,1]])
  242. @pytest.mark.parametrize("eye", [construct.eye, construct.eye_array])
  243. def test_eye(self, eye):
  244. assert_equal(eye(1,1).toarray(), [[1]])
  245. assert_equal(eye(2,3).toarray(), [[1,0,0],[0,1,0]])
  246. assert_equal(eye(3,2).toarray(), [[1,0],[0,1],[0,0]])
  247. assert_equal(eye(3,3).toarray(), [[1,0,0],[0,1,0],[0,0,1]])
  248. assert_equal(eye(3,3,dtype='int16').dtype, np.dtype('int16'))
  249. for m in [3, 5]:
  250. for n in [3, 5]:
  251. for k in range(-5,6):
  252. # scipy.sparse.eye deviates from np.eye here. np.eye will
  253. # create arrays of all 0's when the diagonal offset is
  254. # greater than the size of the array. For sparse arrays
  255. # this makes less sense, especially as it results in dia
  256. # arrays with negative diagonals. Therefore sp.sparse.eye
  257. # validates that diagonal offsets fall within the shape of
  258. # the array. See gh-18555.
  259. if (k > 0 and k > n) or (k < 0 and abs(k) > m):
  260. with pytest.raises(
  261. ValueError, match="Offset.*out of bounds"
  262. ):
  263. eye(m, n, k=k)
  264. else:
  265. assert_equal(
  266. eye(m, n, k=k).toarray(),
  267. np.eye(m, n, k=k)
  268. )
  269. if m == n:
  270. assert_equal(
  271. eye(m, k=k).toarray(),
  272. np.eye(m, n, k=k)
  273. )
  274. @pytest.mark.parametrize("eye", [construct.eye, construct.eye_array])
  275. def test_eye_one(self, eye):
  276. assert_equal(eye(1).toarray(), [[1]])
  277. assert_equal(eye(2).toarray(), [[1,0],[0,1]])
  278. I = eye(3, dtype='int8', format='dia')
  279. assert_equal(I.dtype, np.dtype('int8'))
  280. assert_equal(I.format, 'dia')
  281. for fmt in sparse_formats:
  282. I = eye(3, format=fmt)
  283. assert_equal(I.format, fmt)
  284. assert_equal(I.toarray(), [[1,0,0],[0,1,0],[0,0,1]])
  285. def test_eye_array_vs_matrix(self):
  286. assert isinstance(construct.eye_array(3), sparray)
  287. assert not isinstance(construct.eye(3), sparray)
  288. @pytest.mark.parametrize("arr,kw_format,out_format", [
  289. ([[0, 0], [0, 1]], None, 'coo'), # 2D sparse
  290. ([[1, 0], [1, 1]], None, 'bsr'), # 2D dense
  291. ([[[1, 0], [1, 1]]], None, 'coo'), # 3D dense
  292. ])
  293. def test_kron_output_format(self, arr, kw_format, out_format):
  294. sparr = coo_array(arr)
  295. assert construct.kron(sparr, sparr, format=kw_format).format == out_format
  296. assert construct.kron(sparr, arr, format=kw_format).format == out_format
  297. assert construct.kron(arr, sparr, format=kw_format).format == out_format
  298. def test_kron(self):
  299. cases = []
  300. cases.append(array([[0]]))
  301. cases.append(array([[-1]]))
  302. cases.append(array([[4]]))
  303. cases.append(array([[10]]))
  304. cases.append(array([[0], [0]]))
  305. cases.append(array([[0, 0]]))
  306. cases.append(array([[1, 2], [3, 4]]))
  307. cases.append(array([[0, 2], [5, 0]]))
  308. cases.append(array([[0, 2, -6], [8, 0, 14]]))
  309. cases.append(array([[5, 4], [0, 0], [6, 0]]))
  310. cases.append(array([[5, 4, 4], [1, 0, 0], [6, 0, 8]]))
  311. cases.append(array([[0, 1, 0, 2, 0, 5, 8]]))
  312. cases.append(array([[0.5, 0.125, 0, 3.25], [0, 2.5, 0, 0]]))
  313. # test all cases with some formats
  314. for a in cases:
  315. ca = csr_array(a)
  316. for b in cases:
  317. cb = csr_array(b)
  318. expected = np.kron(a, b)
  319. for fmt in sparse_formats[1:4]:
  320. result = construct.kron(ca, cb, format=fmt)
  321. assert_equal(result.format, fmt)
  322. assert_array_equal(result.toarray(), expected)
  323. assert isinstance(result, sparray)
  324. # nD cases
  325. cases.append(array([0, 1, 2]))
  326. cases.append(array([[[0, 1, 2], [0, 1, 0]]]))
  327. cases.append(array([[[0, 1]], [[2, 2]], [[1, 0]], [[2, 0]]]))
  328. for a in cases:
  329. ca = coo_array(a)
  330. for b in cases:
  331. cb = coo_array(b)
  332. expected = np.kron(a, b)
  333. result = construct.kron(ca, cb, format="coo")
  334. assert_array_equal(result.toarray(), expected)
  335. # test one case with all formats
  336. a = array([[0.5, 0.125, 0, 3.25], [0, 2.5, 0, 0]])
  337. b = array([[5, 4, 4], [1, 0, 0], [6, 0, 8]])
  338. ca = csr_array(a)
  339. cb = csr_array(b)
  340. expected = np.kron(a, b)
  341. for fmt in sparse_formats:
  342. result = construct.kron(ca, cb, format=fmt)
  343. assert_equal(result.format, fmt)
  344. assert_array_equal(result.toarray(), expected)
  345. assert isinstance(result, sparray)
  346. # check that spmatrix returned when both inputs are spmatrix
  347. result = construct.kron(csr_matrix(a), csr_matrix(b), format=fmt)
  348. assert_equal(result.format, fmt)
  349. assert_array_equal(result.toarray(), expected)
  350. assert isinstance(result, spmatrix)
  351. def test_kron_ndim_exceptions(self):
  352. # spmatrix is default, so exceptions with 3D unless sparse arrays are input
  353. with pytest.raises(TypeError, match='expected 2D array or matrix'):
  354. construct.kron([[0], [1]], [[[0, 1]]])
  355. with pytest.raises(TypeError, match="expected 2D array or matrix"):
  356. construct.kron([[[1, 1]]], [[1], [1]])
  357. # no exception for 3D if any sparse arrays input
  358. construct.kron(coo_array([[[0, 1]]]), [[[0], [1]]])
  359. construct.kron([[[0, 1]]], coo_array([[[0], [1]]]))
  360. # no exception for 1D if either sparray or spmatrix
  361. construct.kron([[0], [1]], [0, 1, 0]) # spmatrix b/c lists; 1d-list -> 2d
  362. construct.kron([1, 1], [[1], [1]])
  363. construct.kron([[0], [1]], coo_array([0, 1, 0])) # sparray 1d-list -> 1d
  364. construct.kron(coo_array([1, 1]), [[1], [1]])
  365. def test_kron_large(self):
  366. n = 2**16
  367. a = construct.diags_array([1], shape=(1, n), offsets=n-1, dtype=None)
  368. b = construct.diags_array([1], shape=(n, 1), offsets=1-n, dtype=None)
  369. construct.kron(a, a)
  370. construct.kron(b, b)
  371. def test_kronsum(self):
  372. cases = []
  373. cases.append(array([[0]]))
  374. cases.append(array([[-1]]))
  375. cases.append(array([[4]]))
  376. cases.append(array([[10]]))
  377. cases.append(array([[1,2],[3,4]]))
  378. cases.append(array([[0,2],[5,0]]))
  379. cases.append(array([[0,2,-6],[8,0,14],[0,3,0]]))
  380. cases.append(array([[1,0,0],[0,5,-1],[4,-2,8]]))
  381. # test all cases with default format
  382. for a in cases:
  383. for b in cases:
  384. result = construct.kronsum(csr_array(a), csr_array(b)).toarray()
  385. expected = (np.kron(np.eye(b.shape[0]), a)
  386. + np.kron(b, np.eye(a.shape[0])))
  387. assert_array_equal(result, expected)
  388. # check that spmatrix returned when both inputs are spmatrix
  389. result = construct.kronsum(csr_matrix(a), csr_matrix(b)).toarray()
  390. assert_array_equal(result, expected)
  391. def test_kronsum_ndim_exceptions(self):
  392. with pytest.raises(ValueError, match='requires 2D input'):
  393. construct.kronsum([[0], [1]], csr_array([0, 1]))
  394. with pytest.raises(ValueError, match='requires 2D input'):
  395. construct.kronsum(csr_array([0, 1]), [[0], [1]])
  396. # no exception if sparse arrays are not input (spmatrix inferred)
  397. construct.kronsum([[0, 1], [1, 0]], [2])
  398. @pytest.mark.parametrize("coo_cls", [coo_matrix, coo_array])
  399. def test_vstack(self, coo_cls):
  400. A = coo_cls([[1,2],[3,4]])
  401. B = coo_cls([[5,6]])
  402. expected = array([[1, 2],
  403. [3, 4],
  404. [5, 6]])
  405. assert_equal(construct.vstack([A, B]).toarray(), expected)
  406. assert_equal(construct.vstack([A, B], dtype=np.float32).dtype,
  407. np.float32)
  408. assert_equal(construct.vstack([A.todok(), B.todok()]).toarray(), expected)
  409. assert_equal(construct.vstack([A.tocsr(), B.tocsr()]).toarray(),
  410. expected)
  411. result = construct.vstack([A.tocsr(), B.tocsr()],
  412. format="csr", dtype=np.float32)
  413. assert_equal(result.dtype, np.float32)
  414. assert_equal(result.indices.dtype, np.int32)
  415. assert_equal(result.indptr.dtype, np.int32)
  416. assert_equal(construct.vstack([A.tocsc(), B.tocsc()]).toarray(),
  417. expected)
  418. result = construct.vstack([A.tocsc(), B.tocsc()],
  419. format="csc", dtype=np.float32)
  420. assert_equal(result.dtype, np.float32)
  421. assert_equal(result.indices.dtype, np.int32)
  422. assert_equal(result.indptr.dtype, np.int32)
  423. def test_vstack_maintain64bit_idx_dtype(self):
  424. # see gh-20389 v/hstack returns int32 idx_dtype with input int64 idx_dtype
  425. X = csr_array([[1, 0, 0], [0, 1, 0], [0, 1, 0]])
  426. X.indptr = X.indptr.astype(np.int64)
  427. X.indices = X.indices.astype(np.int64)
  428. assert construct.vstack([X, X]).indptr.dtype == np.int64
  429. assert construct.hstack([X, X]).indptr.dtype == np.int64
  430. X = csc_array([[1, 0, 0], [0, 1, 0], [0, 1, 0]])
  431. X.indptr = X.indptr.astype(np.int64)
  432. X.indices = X.indices.astype(np.int64)
  433. assert construct.vstack([X, X]).indptr.dtype == np.int64
  434. assert construct.hstack([X, X]).indptr.dtype == np.int64
  435. X = coo_array([[1, 0, 0], [0, 1, 0], [0, 1, 0]])
  436. X.coords = tuple(co.astype(np.int64) for co in X.coords)
  437. assert construct.vstack([X, X]).coords[0].dtype == np.int64
  438. assert construct.hstack([X, X]).coords[0].dtype == np.int64
  439. def test_vstack_matrix_or_array(self):
  440. A = [[1,2],[3,4]]
  441. B = [[5,6]]
  442. assert isinstance(construct.vstack([coo_array(A), coo_array(B)]), sparray)
  443. assert isinstance(construct.vstack([coo_array(A), coo_matrix(B)]), sparray)
  444. assert isinstance(construct.vstack([coo_matrix(A), coo_array(B)]), sparray)
  445. assert isinstance(construct.vstack([coo_matrix(A), coo_matrix(B)]), spmatrix)
  446. def test_vstack_1d_with_2d(self):
  447. # fixes gh-21064
  448. arr = csr_array([[1, 0, 0], [0, 1, 0]])
  449. arr1d = csr_array([1, 0, 0])
  450. arr1dcoo = coo_array([1, 0, 0])
  451. assert construct.vstack([arr, np.array([0, 0, 0])]).shape == (3, 3)
  452. assert construct.hstack([arr1d, np.array([[0]])]).shape == (1, 4)
  453. assert construct.hstack([arr1d, arr1d]).shape == (1, 6)
  454. assert construct.vstack([arr1d, arr1d]).shape == (2, 3)
  455. # check csr specialty stacking code like _stack_along_minor_axis
  456. assert construct.hstack([arr, arr]).shape == (2, 6)
  457. assert construct.hstack([arr1d, arr1d]).shape == (1, 6)
  458. assert construct.hstack([arr1d, arr1dcoo]).shape == (1, 6)
  459. assert construct.vstack([arr, arr1dcoo]).shape == (3, 3)
  460. assert construct.vstack([arr1d, arr1dcoo]).shape == (2, 3)
  461. with pytest.raises(ValueError, match="incompatible row dimensions"):
  462. construct.hstack([arr, np.array([0, 0])])
  463. with pytest.raises(ValueError, match="incompatible column dimensions"):
  464. construct.vstack([arr, np.array([0, 0])])
  465. @pytest.mark.parametrize("coo_cls", [coo_matrix, coo_array])
  466. def test_hstack(self, coo_cls):
  467. A = coo_cls([[1,2],[3,4]])
  468. B = coo_cls([[5],[6]])
  469. expected = array([[1, 2, 5],
  470. [3, 4, 6]])
  471. assert_equal(construct.hstack([A, B]).toarray(), expected)
  472. assert_equal(construct.hstack([A, B], dtype=np.float32).dtype,
  473. np.float32)
  474. assert_equal(construct.hstack([A.todok(), B.todok()]).toarray(), expected)
  475. assert_equal(construct.hstack([A.tocsc(), B.tocsc()]).toarray(),
  476. expected)
  477. assert_equal(construct.hstack([A.tocsc(), B.tocsc()],
  478. dtype=np.float32).dtype,
  479. np.float32)
  480. assert_equal(construct.hstack([A.tocsr(), B.tocsr()]).toarray(),
  481. expected)
  482. assert_equal(construct.hstack([A.tocsr(), B.tocsr()],
  483. dtype=np.float32).dtype,
  484. np.float32)
  485. def test_hstack_matrix_or_array(self):
  486. A = [[1,2],[3,4]]
  487. B = [[5],[6]]
  488. assert isinstance(construct.hstack([coo_array(A), coo_array(B)]), sparray)
  489. assert isinstance(construct.hstack([coo_array(A), coo_matrix(B)]), sparray)
  490. assert isinstance(construct.hstack([coo_matrix(A), coo_array(B)]), sparray)
  491. assert isinstance(construct.hstack([coo_matrix(A), coo_matrix(B)]), spmatrix)
  492. @pytest.mark.parametrize("block_array", (construct.bmat, construct.block_array))
  493. def test_block_creation(self, block_array):
  494. A = coo_array([[1, 2], [3, 4]])
  495. B = coo_array([[5],[6]])
  496. C = coo_array([[7]])
  497. D = coo_array((0, 0))
  498. expected = array([[1, 2, 5],
  499. [3, 4, 6],
  500. [0, 0, 7]])
  501. assert_equal(block_array([[A, B], [None, C]]).toarray(), expected)
  502. E = csr_array((1, 2), dtype=np.int32)
  503. assert_equal(block_array([[A.tocsr(), B.tocsr()],
  504. [E, C.tocsr()]]).toarray(),
  505. expected)
  506. assert_equal(block_array([[A.tocsc(), B.tocsc()],
  507. [E.tocsc(), C.tocsc()]]).toarray(),
  508. expected)
  509. expected = array([[1, 2, 0],
  510. [3, 4, 0],
  511. [0, 0, 7]])
  512. assert_equal(block_array([[A, None], [None, C]]).toarray(), expected)
  513. assert_equal(block_array([[A.tocsr(), E.T.tocsr()],
  514. [E, C.tocsr()]]).toarray(),
  515. expected)
  516. assert_equal(block_array([[A.tocsc(), E.T.tocsc()],
  517. [E.tocsc(), C.tocsc()]]).toarray(),
  518. expected)
  519. Z = csr_array((1, 1), dtype=np.int32)
  520. expected = array([[0, 5],
  521. [0, 6],
  522. [7, 0]])
  523. assert_equal(block_array([[None, B], [C, None]]).toarray(), expected)
  524. assert_equal(block_array([[E.T.tocsr(), B.tocsr()],
  525. [C.tocsr(), Z]]).toarray(),
  526. expected)
  527. assert_equal(block_array([[E.T.tocsc(), B.tocsc()],
  528. [C.tocsc(), Z.tocsc()]]).toarray(),
  529. expected)
  530. expected = np.empty((0, 0))
  531. assert_equal(block_array([[None, None]]).toarray(), expected)
  532. assert_equal(block_array([[None, D], [D, None]]).toarray(),
  533. expected)
  534. # test bug reported in gh-5976
  535. expected = array([[7]])
  536. assert_equal(block_array([[None, D], [C, None]]).toarray(),
  537. expected)
  538. # test failure cases
  539. with assert_raises(ValueError) as excinfo:
  540. block_array([[A], [B]])
  541. excinfo.match(r'Got blocks\[1,0\]\.shape\[1\] == 1, expected 2')
  542. with assert_raises(ValueError) as excinfo:
  543. block_array([[A.tocsr()], [B.tocsr()]])
  544. excinfo.match(r'incompatible dimensions for axis 1')
  545. with assert_raises(ValueError) as excinfo:
  546. block_array([[A.tocsc()], [B.tocsc()]])
  547. excinfo.match(r'Mismatching dimensions along axis 1: ({1, 2}|{2, 1})')
  548. with assert_raises(ValueError) as excinfo:
  549. block_array([[A, C]])
  550. excinfo.match(r'Got blocks\[0,1\]\.shape\[0\] == 1, expected 2')
  551. with assert_raises(ValueError) as excinfo:
  552. block_array([[A.tocsr(), C.tocsr()]])
  553. excinfo.match(r'Mismatching dimensions along axis 0: ({1, 2}|{2, 1})')
  554. with assert_raises(ValueError) as excinfo:
  555. block_array([[A.tocsc(), C.tocsc()]])
  556. excinfo.match(r'incompatible dimensions for axis 0')
  557. def test_block_return_type(self):
  558. block = construct.block_array
  559. # csr format ensures we hit _compressed_sparse_stack
  560. # shape of F,G ensure we hit _stack_along_minor_axis
  561. # list version ensure we hit the path with neither helper function
  562. Fl, Gl = [[1, 2],[3, 4]], [[7], [5]]
  563. Fm, Gm = csr_matrix(Fl), csr_matrix(Gl)
  564. assert isinstance(block([[None, Fl], [Gl, None]], format="csr"), sparray)
  565. assert isinstance(block([[None, Fm], [Gm, None]], format="csr"), sparray)
  566. assert isinstance(block([[Fm, Gm]], format="csr"), sparray)
  567. def test_bmat_return_type(self):
  568. """This can be removed after sparse matrix is removed"""
  569. bmat = construct.bmat
  570. # check return type. if any input _is_array output array, else matrix
  571. Fl, Gl = [[1, 2],[3, 4]], [[7], [5]]
  572. Fm, Gm = csr_matrix(Fl), csr_matrix(Gl)
  573. Fa, Ga = csr_array(Fl), csr_array(Gl)
  574. assert isinstance(bmat([[Fa, Ga]], format="csr"), sparray)
  575. assert isinstance(bmat([[Fm, Gm]], format="csr"), spmatrix)
  576. assert isinstance(bmat([[None, Fa], [Ga, None]], format="csr"), sparray)
  577. assert isinstance(bmat([[None, Fm], [Ga, None]], format="csr"), sparray)
  578. assert isinstance(bmat([[None, Fm], [Gm, None]], format="csr"), spmatrix)
  579. assert isinstance(bmat([[None, Fl], [Gl, None]], format="csr"), spmatrix)
  580. # type returned by _compressed_sparse_stack (all csr)
  581. assert isinstance(bmat([[Ga, Ga]], format="csr"), sparray)
  582. assert isinstance(bmat([[Gm, Ga]], format="csr"), sparray)
  583. assert isinstance(bmat([[Ga, Gm]], format="csr"), sparray)
  584. assert isinstance(bmat([[Gm, Gm]], format="csr"), spmatrix)
  585. # shape is 2x2 so no _stack_along_minor_axis
  586. assert isinstance(bmat([[Fa, Fm]], format="csr"), sparray)
  587. assert isinstance(bmat([[Fm, Fm]], format="csr"), spmatrix)
  588. # type returned by _compressed_sparse_stack (all csc)
  589. assert isinstance(bmat([[Gm.tocsc(), Ga.tocsc()]], format="csc"), sparray)
  590. assert isinstance(bmat([[Gm.tocsc(), Gm.tocsc()]], format="csc"), spmatrix)
  591. # shape is 2x2 so no _stack_along_minor_axis
  592. assert isinstance(bmat([[Fa.tocsc(), Fm.tocsc()]], format="csr"), sparray)
  593. assert isinstance(bmat([[Fm.tocsc(), Fm.tocsc()]], format="csr"), spmatrix)
  594. # type returned when mixed input
  595. assert isinstance(bmat([[Gl, Ga]], format="csr"), sparray)
  596. assert isinstance(bmat([[Gm.tocsc(), Ga]], format="csr"), sparray)
  597. assert isinstance(bmat([[Gm.tocsc(), Gm]], format="csr"), spmatrix)
  598. assert isinstance(bmat([[Gm, Gm]], format="csc"), spmatrix)
  599. @pytest.mark.xslow
  600. @pytest.mark.xfail_on_32bit("Can't create large array for test")
  601. def test_concatenate_int32_overflow(self):
  602. """ test for indptr overflow when concatenating matrices """
  603. check_free_memory(30000)
  604. n = 33000
  605. A = csr_array(np.ones((n, n), dtype=bool))
  606. B = A.copy()
  607. C = construct._compressed_sparse_stack((A, B), axis=0,
  608. return_spmatrix=False)
  609. assert_(np.all(np.equal(np.diff(C.indptr), n)))
  610. assert_equal(C.indices.dtype, np.int64)
  611. assert_equal(C.indptr.dtype, np.int64)
  612. def test_block_diag_basic(self):
  613. """ basic test for block_diag """
  614. A = coo_array([[1,2],[3,4]])
  615. B = coo_array([[5],[6]])
  616. C = coo_array([[7]])
  617. expected = array([[1, 2, 0, 0],
  618. [3, 4, 0, 0],
  619. [0, 0, 5, 0],
  620. [0, 0, 6, 0],
  621. [0, 0, 0, 7]])
  622. ABC = construct.block_diag((A, B, C))
  623. assert_equal(ABC.toarray(), expected)
  624. assert ABC.coords[0].dtype == np.int32
  625. def test_block_diag_idx_dtype(self):
  626. X = coo_array([[1, 0, 0], [0, 1, 0], [0, 1, 0]])
  627. X.coords = tuple(co.astype(np.int64) for co in X.coords)
  628. assert construct.block_diag([X, X]).coords[0].dtype == np.int64
  629. def test_block_diag_scalar_1d_args(self):
  630. """ block_diag with scalar and 1d arguments """
  631. # one 1d matrix and a scalar
  632. assert_array_equal(construct.block_diag([[2,3], 4]).toarray(),
  633. [[2, 3, 0], [0, 0, 4]])
  634. # 1d sparse arrays
  635. A = coo_array([1,0,3])
  636. B = coo_array([0,4])
  637. assert_array_equal(construct.block_diag([A, B]).toarray(),
  638. [[1, 0, 3, 0, 0], [0, 0, 0, 0, 4]])
  639. def test_block_diag_1(self):
  640. """ block_diag with one matrix """
  641. assert_equal(construct.block_diag([[1, 0]]).toarray(),
  642. array([[1, 0]]))
  643. assert_equal(construct.block_diag([[[1, 0]]]).toarray(),
  644. array([[1, 0]]))
  645. assert_equal(construct.block_diag([[[1], [0]]]).toarray(),
  646. array([[1], [0]]))
  647. # just on scalar
  648. assert_equal(construct.block_diag([1]).toarray(),
  649. array([[1]]))
  650. def test_block_diag_sparse_arrays(self):
  651. """ block_diag with sparse arrays """
  652. A = coo_array([[1, 2, 3]], shape=(1, 3))
  653. B = coo_array([[4, 5]], shape=(1, 2))
  654. assert_equal(construct.block_diag([A, B]).toarray(),
  655. array([[1, 2, 3, 0, 0], [0, 0, 0, 4, 5]]))
  656. A = coo_array([[1], [2], [3]], shape=(3, 1))
  657. B = coo_array([[4], [5]], shape=(2, 1))
  658. assert_equal(construct.block_diag([A, B]).toarray(),
  659. array([[1, 0], [2, 0], [3, 0], [0, 4], [0, 5]]))
  660. def test_block_diag_return_type(self):
  661. A, B = coo_array([[1, 2, 3]]), coo_matrix([[2, 3, 4]])
  662. assert isinstance(construct.block_diag([A, A]), sparray)
  663. assert isinstance(construct.block_diag([A, B]), sparray)
  664. assert isinstance(construct.block_diag([B, A]), sparray)
  665. assert isinstance(construct.block_diag([B, B]), spmatrix)
  666. def test_random_sampling(self):
  667. # Simple sanity checks for sparse random sampling.
  668. for f in sprand, _sprandn:
  669. for t in [np.float32, np.float64, np.longdouble,
  670. np.int32, np.int64, np.complex64, np.complex128]:
  671. x = f(5, 10, density=0.1, dtype=t)
  672. assert_equal(x.dtype, t)
  673. assert_equal(x.shape, (5, 10))
  674. assert_equal(x.nnz, 5)
  675. x1 = f(5, 10, density=0.1, rng=4321)
  676. assert_equal(x1.dtype, np.float64)
  677. x2 = f(5, 10, density=0.1, rng=np.random.default_rng(4321))
  678. assert_array_equal(x1.data, x2.data)
  679. assert_array_equal(x1.row, x2.row)
  680. assert_array_equal(x1.col, x2.col)
  681. for density in [0.0, 0.1, 0.5, 1.0]:
  682. x = f(5, 10, density=density)
  683. assert_equal(x.nnz, int(density * np.prod(x.shape)))
  684. for fmt in ['coo', 'csc', 'csr', 'lil']:
  685. x = f(5, 10, format=fmt)
  686. assert_equal(x.format, fmt)
  687. assert_raises(ValueError, lambda: f(5, 10, 1.1))
  688. assert_raises(ValueError, lambda: f(5, 10, -0.1))
  689. @pytest.mark.parametrize("rng", [None, 4321, np.random.default_rng(4321)])
  690. def test_rand(self, rng):
  691. # Simple distributional checks for sparse.rand.
  692. x = sprand(10, 20, density=0.5, dtype=np.float64, rng=rng)
  693. assert_(np.all(np.less_equal(0, x.data)))
  694. assert_(np.all(np.less_equal(x.data, 1)))
  695. @pytest.mark.parametrize("rng", [None, 4321, np.random.default_rng(4321)])
  696. def test_randn(self, rng):
  697. # Simple distributional checks for sparse.randn.
  698. # Statistically, some of these should be negative
  699. # and some should be greater than 1.
  700. x = _sprandn(10, 20, density=0.5, dtype=np.float64, rng=rng)
  701. assert_(np.any(np.less(x.data, 0)))
  702. assert_(np.any(np.less(1, x.data)))
  703. x = _sprandn_array(10, 20, density=0.5, dtype=np.float64, rng=rng)
  704. assert_(np.any(np.less(x.data, 0)))
  705. assert_(np.any(np.less(1, x.data)))
  706. def test_random_accept_str_dtype(self):
  707. # anything that np.dtype can convert to a dtype should be accepted
  708. # for the dtype
  709. construct.random(10, 10, dtype='d')
  710. construct.random_array((10, 10), dtype='d')
  711. construct.random_array((10, 10, 10), dtype='d')
  712. construct.random_array((10, 10, 10, 10, 10), dtype='d')
  713. def test_random_array_maintains_array_shape(self):
  714. # preserve use of old random_state during SPEC 7 transition
  715. arr = construct.random_array((0, 4), density=0.3, dtype=int, random_state=0)
  716. assert arr.shape == (0, 4)
  717. arr = construct.random_array((10, 10, 10), density=0.3, dtype=int, rng=0)
  718. assert arr.shape == (10, 10, 10)
  719. arr = construct.random_array((10, 10, 10, 10, 10), density=0.3, dtype=int,
  720. rng=0)
  721. assert arr.shape == (10, 10, 10, 10, 10)
  722. def test_random_array_idx_dtype(self):
  723. A = construct.random_array((10, 10))
  724. assert A.coords[0].dtype == np.int32
  725. def test_random_sparse_matrix_returns_correct_number_of_non_zero_elements(self):
  726. # A 10 x 10 matrix, with density of 12.65%, should have 13 nonzero elements.
  727. # 10 x 10 x 0.1265 = 12.65, which should be rounded up to 13, not 12.
  728. sparse_matrix = construct.random(10, 10, density=0.1265)
  729. assert_equal(sparse_matrix.count_nonzero(),13)
  730. # check random_array
  731. sparse_array = construct.random_array((10, 10), density=0.1265)
  732. assert_equal(sparse_array.count_nonzero(),13)
  733. assert isinstance(sparse_array, sparray)
  734. # check big size
  735. shape = (2**33, 2**33)
  736. sparse_array = construct.random_array(shape, density=2.7105e-17)
  737. assert_equal(sparse_array.count_nonzero(),2000)
  738. # for n-D
  739. # check random_array
  740. sparse_array = construct.random_array((10, 10, 10, 10), density=0.12658)
  741. assert_equal(sparse_array.count_nonzero(),1266)
  742. assert isinstance(sparse_array, sparray)
  743. # check big size
  744. shape = (2**33, 2**33, 2**33)
  745. sparse_array = construct.random_array(shape, density=2.7105e-28)
  746. assert_equal(sparse_array.count_nonzero(),172)
  747. def test_diags_array():
  748. """Tests of diags_array that do not rely on diags wrapper."""
  749. diag = np.arange(1.0, 5.0)
  750. assert_array_equal(construct.diags_array(diag, dtype=None).toarray(), np.diag(diag))
  751. assert_array_equal(
  752. construct.diags_array(diag, offsets=2, dtype=None).toarray(), np.diag(diag, k=2)
  753. )
  754. assert_array_equal(
  755. construct.diags_array(diag, offsets=2, shape=(4, 4), dtype=None).toarray(),
  756. np.diag(diag, k=2)[:4, :4]
  757. )
  758. # Offset outside bounds when shape specified
  759. with pytest.raises(ValueError, match=".*out of bounds"):
  760. construct.diags(np.arange(1.0, 5.0), 5, shape=(4, 4))
  761. @pytest.mark.parametrize('func', [construct.diags_array, construct.diags])
  762. def test_diags_int(func):
  763. d = [[3], [1, 2], [4]]
  764. offsets = [-1, 0, 1]
  765. # Until the deprecation period is over, `dtype=None` must be given
  766. # explicitly to avoid the warning and the cast to an inexact type
  767. # in diags_array() (gh-23102).
  768. arr = func(d, offsets=offsets, dtype=None)
  769. expected = np.array([[1, 4], [3, 2]])
  770. assert_array_equal(arr.toarray(), expected, strict=True)
  771. @pytest.mark.parametrize('func', [construct.diags_array, construct.diags])
  772. def test_diags_int_to_float64(func):
  773. d = [[3], [1, 2], [4]]
  774. offsets = [-1, 0, 1]
  775. # Until the deprecation period is over, diags and diag_array will cast
  776. # integer inputs to float64 by default. A warning will be generated
  777. # that indicates this behavior is deprecated.
  778. # See gh-23102.
  779. with pytest.warns(FutureWarning, match="output has been cast to"):
  780. arr = func(d, offsets=offsets)
  781. expected = np.array([[1.0, 4.0], [3.0, 2.0]])
  782. assert_array_equal(arr.toarray(), expected, strict=True)
  783. def test_swapaxes():
  784. # Borrowed from Numpy swapaxes tests
  785. x = np.array([8.375, 7.545, 8.828, 8.5, 1.757, 5.928,
  786. 8.43, 7.78, 9.865, 5.878, 8.979, 4.732,
  787. 3.012, 6.022, 5.095, 3.116, 5.238, 3.957,
  788. 6.04, 9.63, 7.712, 3.382, 4.489, 6.479,
  789. 7.189, 9.645, 5.395, 4.961, 9.894, 2.893,
  790. 7.357, 9.828, 6.272, 3.758, 6.693, 0.993])
  791. sX = coo_array(x).reshape(6, 6)
  792. sXswapped = construct.swapaxes(sX, 0, 1)
  793. assert_equal(sXswapped[-1].toarray(), sX[:, -1].toarray())
  794. sXX = sX.reshape(3, 2, 2, 3)
  795. sXXswapped = construct.swapaxes(sXX, 0, 2)
  796. assert_equal(sXXswapped.shape, (2, 2, 3, 3))
  797. def test_3d_swapaxes():
  798. tgt = [[[0, 0], [2, 6]], [[1, 5], [0, 7]]]
  799. x = np.array([[[0, 1], [2, 0]], [[0, 5], [6, 7]]])
  800. A = coo_array(x) #[[[0, 1], [2, 0]], [[0, 5], [6, 7]]])
  801. out = construct.swapaxes(A, 0, 2)
  802. assert_equal(out.toarray(), tgt)
  803. assert_equal(out.toarray(), np.swapaxes(x, 0, 2))
  804. @pytest.mark.parametrize("format", sparse_formats)
  805. def test_sparse_format_swapaxes(format):
  806. A = np.array([[2, 0, 1], [3, 5, 0]])
  807. SA = coo_array(A).asformat(format)
  808. out = construct.swapaxes(SA, 1, 0)
  809. assert out.format == "coo"
  810. assert out.shape == (3, 2)
  811. assert_equal(out.toarray(), np.swapaxes(A, 1, 0))
  812. assert not out.has_canonical_format
  813. def test_axis_swapaxes():
  814. A = coo_array([[2, 0], [3, 5]])
  815. with assert_raises(ValueError, match="Invalid axis"):
  816. construct.swapaxes(A, -4, 0)
  817. with assert_raises(ValueError, match="Invalid axis"):
  818. construct.swapaxes(A, 0, -4)
  819. with assert_raises(ValueError, match="Invalid axis"):
  820. construct.swapaxes(A, 3, 0)
  821. with assert_raises(ValueError, match="Invalid axis"):
  822. construct.swapaxes(A, 0, 3)
  823. with assert_raises(ValueError, match="Invalid axis"):
  824. construct.swapaxes(A, 1.2, 1)
  825. with assert_raises(ValueError, match="Invalid axis"):
  826. construct.swapaxes(A, 1, 1.2)
  827. assert_equal(construct.swapaxes(A, 0, 0).toarray(), A.toarray())
  828. for i in range(2):
  829. assert_equal(
  830. construct.swapaxes(A, i, 1 - i).toarray(),
  831. construct.swapaxes(A, i - 2, -1 - i).toarray()
  832. )
  833. def test_permute_dims():
  834. # Borrowed from Numpy tests.
  835. x = np.array([8.375, 7.545, 8.828, 8.5, 1.757, 5.928,
  836. 8.43, 7.78, 9.865, 5.878, 8.979, 4.732,
  837. 3.012, 6.022, 5.095, 3.116, 5.238, 3.957,
  838. 6.04, 9.63, 7.712, 3.382, 4.489, 6.479,
  839. 7.189, 9.645, 5.395, 4.961, 9.894, 2.893,
  840. 7.357, 9.828, 6.272, 3.758, 6.693, 0.993])
  841. npx = x.reshape(6, 6)
  842. sX = coo_array(x).reshape(6, 6)
  843. sXpermuted = construct.permute_dims(sX, axes=(1, 0), copy=True)
  844. sXtransposed = sX.transpose(axes=(1, 0))
  845. assert_equal(sXpermuted.toarray(), sXtransposed.toarray())
  846. assert_equal(sXpermuted[-1].toarray(), sX[:, -1].toarray())
  847. npxx = npx.reshape(3, 2, 2, 3)
  848. sXX = sX.reshape(3, 2, 2, 3)
  849. sXXpermuted = construct.permute_dims(sXX, axes=(0, 2, 1, 3), copy=True)
  850. assert_equal(sXXpermuted.shape, (3, 2, 2, 3))
  851. sXXtransposed = sXX.transpose(axes=(0, 2, 1, 3))
  852. assert_equal(sXXtransposed.shape, (3, 2, 2, 3))
  853. assert_equal(sXXpermuted.toarray(), sXXtransposed.toarray())
  854. # TODO change np.transpose to np.permute_dims when numpy 2 is min supported version
  855. assert_equal(sXXpermuted.toarray(), np.transpose(npxx, axes=(0, 2, 1, 3)))
  856. def test_3d_permute_dims():
  857. tgt = [[[0], [2], [0], [6]], [[1], [0], [5], [7]]]
  858. x = np.array([[[0, 1], [2, 0], [0, 5], [6, 7]]])
  859. A = coo_array(x)
  860. out = construct.permute_dims(A, axes=(2, 1, 0))
  861. assert_equal(out.shape, (2, 4, 1))
  862. assert_equal(out.toarray(), tgt)
  863. # TODO change np.transpose to np.permute_dims when numpy 2 is min supported version
  864. assert_equal(out.toarray(), np.transpose(x, axes=(2, 1, 0)))
  865. def test_canonical_format_permute_dims():
  866. A = coo_array([[2, 0, 1], [3, 5, 0]])
  867. # identity axes keep has_canoncial_format True after permute_dims.
  868. assert construct.permute_dims(A, axes=(0, 1)).has_canonical_format is True
  869. assert construct.permute_dims(A, axes=[0, 1]).has_canonical_format is True
  870. # order changes set has_canonical_format to False
  871. assert construct.permute_dims(A, axes=[1, 0]).has_canonical_format is False
  872. def test_axis_permute_dims():
  873. A = coo_array([[2, 0, 1], [3, 5, 0]])
  874. with assert_raises(ValueError, match="Incorrect number of axes"):
  875. construct.permute_dims(A, axes=(2, 0, 1))
  876. with assert_raises(ValueError, match="duplicate value in axis"):
  877. construct.permute_dims(A, axes=(0, 0))
  878. with assert_raises(TypeError, match="axis must be an integer/tuple"):
  879. construct.permute_dims(A, axes={1, 0})
  880. with assert_raises(ValueError, match="axis out of range"):
  881. construct.permute_dims(A, axes=(-3, 0))
  882. with assert_raises(ValueError, match="axis out of range"):
  883. construct.permute_dims(A, axes=(0, -3))
  884. with assert_raises(ValueError, match="axis out of range"):
  885. construct.permute_dims(A, axes=(2, 0))
  886. with assert_raises(ValueError, match="axis out of range"):
  887. construct.permute_dims(A, axes=(0, 2))
  888. with assert_raises(TypeError, match="axis must be an integer"):
  889. construct.permute_dims(A, axes=(1.2, 0))
  890. assert_equal(
  891. construct.permute_dims(A, axes=(1, 0), copy=True).toarray(),
  892. A.transpose(axes=(1, 0), copy=True).toarray()
  893. )
  894. # use lists for axes
  895. assert_equal(
  896. construct.permute_dims(A, axes=[1, 0], copy=True).toarray(),
  897. A.transpose(axes=[1, 0], copy=True).toarray()
  898. )
  899. assert_equal(
  900. construct.permute_dims(A, axes=None, copy=True).toarray(),
  901. A.transpose(axes=(1, 0), copy=True).toarray()
  902. )
  903. assert_equal(
  904. construct.permute_dims(A, axes=(0, 1), copy=True).toarray(), A.toarray()
  905. )
  906. @pytest.mark.parametrize("format", sparse_formats)
  907. def test_sparse_format_permute_dims(format):
  908. A = np.array([[2, 0, 1], [3, 5, 0]])
  909. SA = coo_array(A).asformat(format)
  910. out = construct.permute_dims(SA, axes=(1, 0))
  911. assert out.format == "coo"
  912. assert out.shape == (3, 2)
  913. # TODO change np.transpose to np.permute_dims when numpy 2 is min supported version
  914. assert_equal(out.toarray(), np.transpose(A, axes=(1, 0)))
  915. assert not out.has_canonical_format
  916. def test_expand_dims():
  917. # Borrowed from Numpy tests.
  918. x = np.array([8.375, 7.545, 8.828, 8.5, 1.757, 5.928,
  919. 8.43, 7.78, 9.865, 5.878, 8.979, 4.732,
  920. 3.012, 6.022, 5.095, 3.116, 5.238, 3.957,
  921. 6.04, 9.63, 7.712, 3.382, 4.489, 6.479,
  922. 7.189, 9.645, 5.395, 4.961, 9.894, 2.893,
  923. 7.357, 9.828, 6.272, 3.758, 6.693, 0.993])
  924. npx = x.reshape(6, 6)
  925. sX = coo_array(npx)
  926. npx_expanded = np.expand_dims(npx, axis=1)
  927. sXexpanded = construct.expand_dims(sX, axis=1)
  928. assert_equal(sXexpanded[-1].toarray(), sX[-1, np.newaxis, :].toarray())
  929. assert_equal(sXexpanded.toarray(), npx_expanded)
  930. npxx = npx.reshape(3, 2, 2, 3)
  931. sXX = sX.reshape(3, 2, 2, 3)
  932. npxx_expanded = np.expand_dims(npxx, axis=2)
  933. sXXexpanded = construct.expand_dims(sXX, axis=2)
  934. assert_equal(sXXexpanded.shape, (3, 2, 1, 2, 3))
  935. assert_equal(sXXexpanded.toarray(), npxx_expanded)
  936. npxx_expanded = np.expand_dims(npxx, axis=-2)
  937. sXXexpanded = construct.expand_dims(sXX, axis=-2)
  938. assert_equal(sXXexpanded.shape, (3, 2, 2, 1, 3))
  939. assert_equal(sXXexpanded.toarray(), npxx_expanded)
  940. def test_3d_expand_dims():
  941. tgt = [[[[0, 0], [2, 6]]], [[[1, 5], [0, 7]]]]
  942. A = coo_array([[[0, 0], [2, 6]], [[1, 5], [0, 7]]])
  943. out = construct.expand_dims(A, axis=1)
  944. assert_equal(out.toarray(), tgt)
  945. @pytest.mark.parametrize("format", sparse_formats)
  946. def test_sparse_format_expand_dims(format):
  947. A = np.array([[2, 0], [3, 5]])
  948. SA = coo_array(A).asformat(format)
  949. out = construct.expand_dims(SA, axis=1)
  950. assert out.format == "coo"
  951. assert out.shape == (2, 1, 2)
  952. assert_equal(out.toarray(), np.expand_dims(A, axis=1))
  953. assert SA.tocoo().has_canonical_format == out.has_canonical_format
  954. def test_axis_expand_dims():
  955. A = coo_array([[2, 0], [3, 5]])
  956. with assert_raises(ValueError, match="Invalid axis"):
  957. construct.expand_dims(A, axis=-4)
  958. with assert_raises(ValueError, match="Invalid axis"):
  959. construct.expand_dims(A, axis=3)
  960. with assert_raises(ValueError, match="Invalid axis"):
  961. construct.expand_dims(A, axis=1.2)
  962. for i in range(3):
  963. assert_equal(
  964. construct.expand_dims(A, axis=i).toarray(),
  965. construct.expand_dims(A, axis=i - 3).toarray()
  966. )