test_mmio.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. from tempfile import mkdtemp
  2. import os
  3. import io
  4. import shutil
  5. import threading
  6. import textwrap
  7. import numpy as np
  8. from numpy import array, transpose, pi
  9. from numpy.testing import (assert_equal, assert_allclose,
  10. assert_array_equal, assert_array_almost_equal)
  11. import pytest
  12. from pytest import raises as assert_raises
  13. import scipy.sparse
  14. import scipy.io._mmio
  15. import scipy.io._fast_matrix_market as fmm
  16. parametrize_args = [('integer', 'int'),
  17. ('unsigned-integer', 'uint')]
  18. pytestmark = pytest.mark.thread_unsafe
  19. # Run the entire test suite on both _mmio and _fast_matrix_market implementations
  20. @pytest.fixture(scope='module', params=(scipy.io._mmio, fmm), autouse=True)
  21. def implementations(request):
  22. global mminfo
  23. global mmread
  24. global mmwrite
  25. mminfo = request.param.mminfo
  26. mmread = request.param.mmread
  27. mmwrite = request.param.mmwrite
  28. class TestMMIOArray:
  29. def setup_method(self):
  30. self.tmpdir = mkdtemp(suffix=str(threading.get_native_id()))
  31. self.fn = os.path.join(self.tmpdir, 'testfile.mtx')
  32. def teardown_method(self):
  33. shutil.rmtree(self.tmpdir)
  34. def check(self, a, info):
  35. mmwrite(self.fn, a)
  36. assert_equal(mminfo(self.fn), info)
  37. b = mmread(self.fn, spmatrix=False)
  38. assert_array_almost_equal(a, b)
  39. def check_exact(self, a, info):
  40. mmwrite(self.fn, a)
  41. assert_equal(mminfo(self.fn), info)
  42. b = mmread(self.fn, spmatrix=False)
  43. assert_equal(a, b)
  44. @pytest.mark.parametrize('typeval, dtype', parametrize_args)
  45. def test_simple_integer(self, typeval, dtype):
  46. self.check_exact(array([[1, 2], [3, 4]], dtype=dtype),
  47. (2, 2, 4, 'array', typeval, 'general'))
  48. @pytest.mark.parametrize('typeval, dtype', parametrize_args)
  49. def test_32bit_integer(self, typeval, dtype):
  50. a = array([[2**31-1, 2**31-2], [2**31-3, 2**31-4]], dtype=dtype)
  51. self.check_exact(a, (2, 2, 4, 'array', typeval, 'general'))
  52. def test_64bit_integer(self):
  53. a = array([[2**31, 2**32], [2**63-2, 2**63-1]], dtype=np.int64)
  54. if (np.intp(0).itemsize < 8) and mmwrite == scipy.io._mmio.mmwrite:
  55. assert_raises(OverflowError, mmwrite, self.fn, a)
  56. else:
  57. self.check_exact(a, (2, 2, 4, 'array', 'integer', 'general'))
  58. def test_64bit_unsigned_integer(self):
  59. a = array([[2**31, 2**32], [2**64-2, 2**64-1]], dtype=np.uint64)
  60. self.check_exact(a, (2, 2, 4, 'array', 'unsigned-integer', 'general'))
  61. @pytest.mark.parametrize('typeval, dtype', parametrize_args)
  62. def test_simple_upper_triangle_integer(self, typeval, dtype):
  63. self.check_exact(array([[0, 1], [0, 0]], dtype=dtype),
  64. (2, 2, 4, 'array', typeval, 'general'))
  65. @pytest.mark.parametrize('typeval, dtype', parametrize_args)
  66. def test_simple_lower_triangle_integer(self, typeval, dtype):
  67. self.check_exact(array([[0, 0], [1, 0]], dtype=dtype),
  68. (2, 2, 4, 'array', typeval, 'general'))
  69. @pytest.mark.parametrize('typeval, dtype', parametrize_args)
  70. def test_simple_rectangular_integer(self, typeval, dtype):
  71. self.check_exact(array([[1, 2, 3], [4, 5, 6]], dtype=dtype),
  72. (2, 3, 6, 'array', typeval, 'general'))
  73. def test_simple_rectangular_float(self):
  74. self.check([[1, 2], [3.5, 4], [5, 6]],
  75. (3, 2, 6, 'array', 'real', 'general'))
  76. def test_simple_float(self):
  77. self.check([[1, 2], [3, 4.0]],
  78. (2, 2, 4, 'array', 'real', 'general'))
  79. def test_simple_complex(self):
  80. self.check([[1, 2], [3, 4j]],
  81. (2, 2, 4, 'array', 'complex', 'general'))
  82. @pytest.mark.parametrize('typeval, dtype', parametrize_args)
  83. def test_simple_symmetric_integer(self, typeval, dtype):
  84. self.check_exact(array([[1, 2], [2, 4]], dtype=dtype),
  85. (2, 2, 4, 'array', typeval, 'symmetric'))
  86. def test_simple_skew_symmetric_integer(self):
  87. self.check_exact([[0, 2], [-2, 0]],
  88. (2, 2, 4, 'array', 'integer', 'skew-symmetric'))
  89. def test_simple_skew_symmetric_float(self):
  90. self.check(array([[0, 2], [-2.0, 0.0]], 'f'),
  91. (2, 2, 4, 'array', 'real', 'skew-symmetric'))
  92. def test_simple_hermitian_complex(self):
  93. self.check([[1, 2+3j], [2-3j, 4]],
  94. (2, 2, 4, 'array', 'complex', 'hermitian'))
  95. def test_random_symmetric_float(self):
  96. sz = (20, 20)
  97. a = np.random.random(sz)
  98. a = a + transpose(a)
  99. self.check(a, (20, 20, 400, 'array', 'real', 'symmetric'))
  100. def test_random_rectangular_float(self):
  101. sz = (20, 15)
  102. a = np.random.random(sz)
  103. self.check(a, (20, 15, 300, 'array', 'real', 'general'))
  104. @pytest.mark.fail_slow(10)
  105. def test_bad_number_of_array_header_fields(self):
  106. s = """\
  107. %%MatrixMarket matrix array real general
  108. 3 3 999
  109. 1.0
  110. 2.0
  111. 3.0
  112. 4.0
  113. 5.0
  114. 6.0
  115. 7.0
  116. 8.0
  117. 9.0
  118. """
  119. text = textwrap.dedent(s).encode('ascii')
  120. with pytest.raises(ValueError, match='not of length 2'):
  121. scipy.io.mmread(io.BytesIO(text))
  122. def test_gh13634_non_skew_symmetric_int(self):
  123. self.check_exact(array([[1, 2], [-2, 99]], dtype=np.int32),
  124. (2, 2, 4, 'array', 'integer', 'general'))
  125. def test_gh13634_non_skew_symmetric_float(self):
  126. self.check(array([[1, 2], [-2, 99.]], dtype=np.float32),
  127. (2, 2, 4, 'array', 'real', 'general'))
  128. class TestMMIOSparseCSR(TestMMIOArray):
  129. def setup_method(self):
  130. self.tmpdir = mkdtemp(suffix=str(threading.get_native_id()))
  131. self.fn = os.path.join(self.tmpdir, 'testfile.mtx')
  132. def teardown_method(self):
  133. shutil.rmtree(self.tmpdir)
  134. def check(self, a, info):
  135. mmwrite(self.fn, a)
  136. assert_equal(mminfo(self.fn), info)
  137. b = mmread(self.fn, spmatrix=False)
  138. assert_array_almost_equal(a.toarray(), b.toarray())
  139. def check_exact(self, a, info):
  140. mmwrite(self.fn, a)
  141. assert_equal(mminfo(self.fn), info)
  142. b = mmread(self.fn, spmatrix=False)
  143. assert_equal(a.toarray(), b.toarray())
  144. @pytest.mark.parametrize('typeval, dtype', parametrize_args)
  145. def test_simple_integer(self, typeval, dtype):
  146. self.check_exact(scipy.sparse.csr_array([[1, 2], [3, 4]], dtype=dtype),
  147. (2, 2, 4, 'coordinate', typeval, 'general'))
  148. def test_32bit_integer(self):
  149. a = scipy.sparse.csr_array(array([[2**31-1, -2**31+2],
  150. [2**31-3, 2**31-4]],
  151. dtype=np.int32))
  152. self.check_exact(a, (2, 2, 4, 'coordinate', 'integer', 'general'))
  153. def test_64bit_integer(self):
  154. a = scipy.sparse.csr_array(array([[2**32+1, 2**32+1],
  155. [-2**63+2, 2**63-2]],
  156. dtype=np.int64))
  157. if (np.intp(0).itemsize < 8) and mmwrite == scipy.io._mmio.mmwrite:
  158. assert_raises(OverflowError, mmwrite, self.fn, a)
  159. else:
  160. self.check_exact(a, (2, 2, 4, 'coordinate', 'integer', 'general'))
  161. def test_32bit_unsigned_integer(self):
  162. a = scipy.sparse.csr_array(array([[2**31-1, 2**31-2],
  163. [2**31-3, 2**31-4]],
  164. dtype=np.uint32))
  165. self.check_exact(a, (2, 2, 4, 'coordinate', 'unsigned-integer', 'general'))
  166. def test_64bit_unsigned_integer(self):
  167. a = scipy.sparse.csr_array(array([[2**32+1, 2**32+1],
  168. [2**64-2, 2**64-1]],
  169. dtype=np.uint64))
  170. self.check_exact(a, (2, 2, 4, 'coordinate', 'unsigned-integer', 'general'))
  171. @pytest.mark.parametrize('typeval, dtype', parametrize_args)
  172. def test_simple_upper_triangle_integer(self, typeval, dtype):
  173. self.check_exact(scipy.sparse.csr_array([[0, 1], [0, 0]], dtype=dtype),
  174. (2, 2, 1, 'coordinate', typeval, 'general'))
  175. @pytest.mark.parametrize('typeval, dtype', parametrize_args)
  176. def test_simple_lower_triangle_integer(self, typeval, dtype):
  177. self.check_exact(scipy.sparse.csr_array([[0, 0], [1, 0]], dtype=dtype),
  178. (2, 2, 1, 'coordinate', typeval, 'general'))
  179. @pytest.mark.parametrize('typeval, dtype', parametrize_args)
  180. def test_simple_rectangular_integer(self, typeval, dtype):
  181. self.check_exact(scipy.sparse.csr_array([[1, 2, 3], [4, 5, 6]], dtype=dtype),
  182. (2, 3, 6, 'coordinate', typeval, 'general'))
  183. def test_simple_rectangular_float(self):
  184. self.check(scipy.sparse.csr_array([[1, 2], [3.5, 4], [5, 6]]),
  185. (3, 2, 6, 'coordinate', 'real', 'general'))
  186. def test_simple_float(self):
  187. self.check(scipy.sparse.csr_array([[1, 2], [3, 4.0]]),
  188. (2, 2, 4, 'coordinate', 'real', 'general'))
  189. def test_simple_complex(self):
  190. self.check(scipy.sparse.csr_array([[1, 2], [3, 4j]]),
  191. (2, 2, 4, 'coordinate', 'complex', 'general'))
  192. @pytest.mark.parametrize('typeval, dtype', parametrize_args)
  193. def test_simple_symmetric_integer(self, typeval, dtype):
  194. self.check_exact(scipy.sparse.csr_array([[1, 2], [2, 4]], dtype=dtype),
  195. (2, 2, 3, 'coordinate', typeval, 'symmetric'))
  196. def test_simple_skew_symmetric_integer(self):
  197. self.check_exact(scipy.sparse.csr_array([[0, 2], [-2, 0]]),
  198. (2, 2, 1, 'coordinate', 'integer', 'skew-symmetric'))
  199. def test_simple_skew_symmetric_float(self):
  200. self.check(scipy.sparse.csr_array(array([[0, 2], [-2.0, 0]], 'f')),
  201. (2, 2, 1, 'coordinate', 'real', 'skew-symmetric'))
  202. def test_simple_hermitian_complex(self):
  203. self.check(scipy.sparse.csr_array([[1, 2+3j], [2-3j, 4]]),
  204. (2, 2, 3, 'coordinate', 'complex', 'hermitian'))
  205. def test_random_symmetric_float(self):
  206. sz = (20, 20)
  207. a = np.random.random(sz)
  208. a = a + transpose(a)
  209. a = scipy.sparse.csr_array(a)
  210. self.check(a, (20, 20, 210, 'coordinate', 'real', 'symmetric'))
  211. def test_random_rectangular_float(self):
  212. sz = (20, 15)
  213. a = np.random.random(sz)
  214. a = scipy.sparse.csr_array(a)
  215. self.check(a, (20, 15, 300, 'coordinate', 'real', 'general'))
  216. def test_simple_pattern(self):
  217. a = scipy.sparse.csr_array([[0, 1.5], [3.0, 2.5]])
  218. p = np.zeros_like(a.toarray())
  219. p[a.toarray() > 0] = 1
  220. info = (2, 2, 3, 'coordinate', 'pattern', 'general')
  221. mmwrite(self.fn, a, field='pattern')
  222. assert_equal(mminfo(self.fn), info)
  223. b = mmread(self.fn, spmatrix=False)
  224. assert_array_almost_equal(p, b.toarray())
  225. assert not scipy.sparse.isspmatrix(b)
  226. b = mmread(self.fn, spmatrix=True)
  227. assert scipy.sparse.isspmatrix(b)
  228. b = mmread(self.fn) # chk default
  229. assert scipy.sparse.isspmatrix(b)
  230. def test_gh13634_non_skew_symmetric_int(self):
  231. a = scipy.sparse.csr_array([[1, 2], [-2, 99]], dtype=np.int32)
  232. self.check_exact(a, (2, 2, 4, 'coordinate', 'integer', 'general'))
  233. def test_gh13634_non_skew_symmetric_float(self):
  234. a = scipy.sparse.csr_array([[1, 2], [-2, 99.]], dtype=np.float32)
  235. self.check(a, (2, 2, 4, 'coordinate', 'real', 'general'))
  236. _32bit_integer_dense_example = '''\
  237. %%MatrixMarket matrix array integer general
  238. 2 2
  239. 2147483647
  240. 2147483646
  241. 2147483647
  242. 2147483646
  243. '''
  244. _32bit_integer_sparse_example = '''\
  245. %%MatrixMarket matrix coordinate integer symmetric
  246. 2 2 2
  247. 1 1 2147483647
  248. 2 2 2147483646
  249. '''
  250. _64bit_integer_dense_example = '''\
  251. %%MatrixMarket matrix array integer general
  252. 2 2
  253. 2147483648
  254. -9223372036854775806
  255. -2147483648
  256. 9223372036854775807
  257. '''
  258. _64bit_integer_sparse_general_example = '''\
  259. %%MatrixMarket matrix coordinate integer general
  260. 2 2 3
  261. 1 1 2147483648
  262. 1 2 9223372036854775807
  263. 2 2 9223372036854775807
  264. '''
  265. _64bit_integer_sparse_symmetric_example = '''\
  266. %%MatrixMarket matrix coordinate integer symmetric
  267. 2 2 3
  268. 1 1 2147483648
  269. 1 2 -9223372036854775807
  270. 2 2 9223372036854775807
  271. '''
  272. _64bit_integer_sparse_skew_example = '''\
  273. %%MatrixMarket matrix coordinate integer skew-symmetric
  274. 2 2 3
  275. 1 1 2147483648
  276. 1 2 -9223372036854775807
  277. 2 2 9223372036854775807
  278. '''
  279. _over64bit_integer_dense_example = '''\
  280. %%MatrixMarket matrix array integer general
  281. 2 2
  282. 2147483648
  283. 9223372036854775807
  284. 2147483648
  285. 9223372036854775808
  286. '''
  287. _over64bit_integer_sparse_example = '''\
  288. %%MatrixMarket matrix coordinate integer symmetric
  289. 2 2 2
  290. 1 1 2147483648
  291. 2 2 19223372036854775808
  292. '''
  293. class TestMMIOReadLargeIntegers:
  294. def setup_method(self):
  295. self.tmpdir = mkdtemp(suffix=str(threading.get_native_id()))
  296. self.fn = os.path.join(self.tmpdir, 'testfile.mtx')
  297. def teardown_method(self):
  298. shutil.rmtree(self.tmpdir)
  299. def check_read(self, example, a, info, dense, over32, over64):
  300. with open(self.fn, 'w') as f:
  301. f.write(example)
  302. assert_equal(mminfo(self.fn), info)
  303. if ((over32 and (np.intp(0).itemsize < 8) and mmwrite == scipy.io._mmio.mmwrite)
  304. or over64):
  305. assert_raises(OverflowError, mmread, self.fn)
  306. else:
  307. b = mmread(self.fn, spmatrix=False)
  308. if not dense:
  309. b = b.toarray()
  310. assert_equal(a, b)
  311. def test_read_32bit_integer_dense(self):
  312. a = array([[2**31-1, 2**31-1],
  313. [2**31-2, 2**31-2]], dtype=np.int64)
  314. self.check_read(_32bit_integer_dense_example,
  315. a,
  316. (2, 2, 4, 'array', 'integer', 'general'),
  317. dense=True,
  318. over32=False,
  319. over64=False)
  320. def test_read_32bit_integer_sparse(self):
  321. a = array([[2**31-1, 0],
  322. [0, 2**31-2]], dtype=np.int64)
  323. self.check_read(_32bit_integer_sparse_example,
  324. a,
  325. (2, 2, 2, 'coordinate', 'integer', 'symmetric'),
  326. dense=False,
  327. over32=False,
  328. over64=False)
  329. def test_read_64bit_integer_dense(self):
  330. a = array([[2**31, -2**31],
  331. [-2**63+2, 2**63-1]], dtype=np.int64)
  332. self.check_read(_64bit_integer_dense_example,
  333. a,
  334. (2, 2, 4, 'array', 'integer', 'general'),
  335. dense=True,
  336. over32=True,
  337. over64=False)
  338. def test_read_64bit_integer_sparse_general(self):
  339. a = array([[2**31, 2**63-1],
  340. [0, 2**63-1]], dtype=np.int64)
  341. self.check_read(_64bit_integer_sparse_general_example,
  342. a,
  343. (2, 2, 3, 'coordinate', 'integer', 'general'),
  344. dense=False,
  345. over32=True,
  346. over64=False)
  347. def test_read_64bit_integer_sparse_symmetric(self):
  348. a = array([[2**31, -2**63+1],
  349. [-2**63+1, 2**63-1]], dtype=np.int64)
  350. self.check_read(_64bit_integer_sparse_symmetric_example,
  351. a,
  352. (2, 2, 3, 'coordinate', 'integer', 'symmetric'),
  353. dense=False,
  354. over32=True,
  355. over64=False)
  356. def test_read_64bit_integer_sparse_skew(self):
  357. a = array([[2**31, -2**63+1],
  358. [2**63-1, 2**63-1]], dtype=np.int64)
  359. self.check_read(_64bit_integer_sparse_skew_example,
  360. a,
  361. (2, 2, 3, 'coordinate', 'integer', 'skew-symmetric'),
  362. dense=False,
  363. over32=True,
  364. over64=False)
  365. def test_read_over64bit_integer_dense(self):
  366. self.check_read(_over64bit_integer_dense_example,
  367. None,
  368. (2, 2, 4, 'array', 'integer', 'general'),
  369. dense=True,
  370. over32=True,
  371. over64=True)
  372. def test_read_over64bit_integer_sparse(self):
  373. self.check_read(_over64bit_integer_sparse_example,
  374. None,
  375. (2, 2, 2, 'coordinate', 'integer', 'symmetric'),
  376. dense=False,
  377. over32=True,
  378. over64=True)
  379. _general_example = '''\
  380. %%MatrixMarket matrix coordinate real general
  381. %=================================================================================
  382. %
  383. % This ASCII file represents a sparse MxN matrix with L
  384. % nonzeros in the following Matrix Market format:
  385. %
  386. % +----------------------------------------------+
  387. % |%%MatrixMarket matrix coordinate real general | <--- header line
  388. % |% | <--+
  389. % |% comments | |-- 0 or more comment lines
  390. % |% | <--+
  391. % | M N L | <--- rows, columns, entries
  392. % | I1 J1 A(I1, J1) | <--+
  393. % | I2 J2 A(I2, J2) | |
  394. % | I3 J3 A(I3, J3) | |-- L lines
  395. % | . . . | |
  396. % | IL JL A(IL, JL) | <--+
  397. % +----------------------------------------------+
  398. %
  399. % Indices are 1-based, i.e. A(1,1) is the first element.
  400. %
  401. %=================================================================================
  402. 5 5 8
  403. 1 1 1.000e+00
  404. 2 2 1.050e+01
  405. 3 3 1.500e-02
  406. 1 4 6.000e+00
  407. 4 2 2.505e+02
  408. 4 4 -2.800e+02
  409. 4 5 3.332e+01
  410. 5 5 1.200e+01
  411. '''
  412. _hermitian_example = '''\
  413. %%MatrixMarket matrix coordinate complex hermitian
  414. 5 5 7
  415. 1 1 1.0 0
  416. 2 2 10.5 0
  417. 4 2 250.5 22.22
  418. 3 3 1.5e-2 0
  419. 4 4 -2.8e2 0
  420. 5 5 12. 0
  421. 5 4 0 33.32
  422. '''
  423. _skew_example = '''\
  424. %%MatrixMarket matrix coordinate real skew-symmetric
  425. 5 5 7
  426. 1 1 1.0
  427. 2 2 10.5
  428. 4 2 250.5
  429. 3 3 1.5e-2
  430. 4 4 -2.8e2
  431. 5 5 12.
  432. 5 4 0
  433. '''
  434. _symmetric_example = '''\
  435. %%MatrixMarket matrix coordinate real symmetric
  436. 5 5 7
  437. 1 1 1.0
  438. 2 2 10.5
  439. 4 2 250.5
  440. 3 3 1.5e-2
  441. 4 4 -2.8e2
  442. 5 5 12.
  443. 5 4 8
  444. '''
  445. _symmetric_pattern_example = '''\
  446. %%MatrixMarket matrix coordinate pattern symmetric
  447. 5 5 7
  448. 1 1
  449. 2 2
  450. 4 2
  451. 3 3
  452. 4 4
  453. 5 5
  454. 5 4
  455. '''
  456. # example (without comment lines) from Figure 1 in
  457. # https://math.nist.gov/MatrixMarket/reports/MMformat.ps
  458. _empty_lines_example = '''\
  459. %%MatrixMarket MATRIX Coordinate Real General
  460. 5 5 8
  461. 1 1 1.0
  462. 2 2 10.5
  463. 3 3 1.5e-2
  464. 4 4 -2.8E2
  465. 5 5 12.
  466. 1 4 6
  467. 4 2 250.5
  468. 4 5 33.32
  469. '''
  470. class TestMMIOCoordinate:
  471. def setup_method(self):
  472. self.tmpdir = mkdtemp(suffix=str(threading.get_native_id()))
  473. self.fn = os.path.join(self.tmpdir, 'testfile.mtx')
  474. def teardown_method(self):
  475. shutil.rmtree(self.tmpdir)
  476. def check_read(self, example, a, info):
  477. f = open(self.fn, 'w')
  478. f.write(example)
  479. f.close()
  480. assert_equal(mminfo(self.fn), info)
  481. b = mmread(self.fn, spmatrix=False).toarray()
  482. assert_array_almost_equal(a, b)
  483. def test_read_general(self):
  484. a = [[1, 0, 0, 6, 0],
  485. [0, 10.5, 0, 0, 0],
  486. [0, 0, .015, 0, 0],
  487. [0, 250.5, 0, -280, 33.32],
  488. [0, 0, 0, 0, 12]]
  489. self.check_read(_general_example, a,
  490. (5, 5, 8, 'coordinate', 'real', 'general'))
  491. def test_read_hermitian(self):
  492. a = [[1, 0, 0, 0, 0],
  493. [0, 10.5, 0, 250.5 - 22.22j, 0],
  494. [0, 0, .015, 0, 0],
  495. [0, 250.5 + 22.22j, 0, -280, -33.32j],
  496. [0, 0, 0, 33.32j, 12]]
  497. self.check_read(_hermitian_example, a,
  498. (5, 5, 7, 'coordinate', 'complex', 'hermitian'))
  499. def test_read_skew(self):
  500. a = [[1, 0, 0, 0, 0],
  501. [0, 10.5, 0, -250.5, 0],
  502. [0, 0, .015, 0, 0],
  503. [0, 250.5, 0, -280, 0],
  504. [0, 0, 0, 0, 12]]
  505. self.check_read(_skew_example, a,
  506. (5, 5, 7, 'coordinate', 'real', 'skew-symmetric'))
  507. def test_read_symmetric(self):
  508. a = [[1, 0, 0, 0, 0],
  509. [0, 10.5, 0, 250.5, 0],
  510. [0, 0, .015, 0, 0],
  511. [0, 250.5, 0, -280, 8],
  512. [0, 0, 0, 8, 12]]
  513. self.check_read(_symmetric_example, a,
  514. (5, 5, 7, 'coordinate', 'real', 'symmetric'))
  515. def test_read_symmetric_pattern(self):
  516. a = [[1, 0, 0, 0, 0],
  517. [0, 1, 0, 1, 0],
  518. [0, 0, 1, 0, 0],
  519. [0, 1, 0, 1, 1],
  520. [0, 0, 0, 1, 1]]
  521. self.check_read(_symmetric_pattern_example, a,
  522. (5, 5, 7, 'coordinate', 'pattern', 'symmetric'))
  523. def test_read_empty_lines(self):
  524. a = [[1, 0, 0, 6, 0],
  525. [0, 10.5, 0, 0, 0],
  526. [0, 0, .015, 0, 0],
  527. [0, 250.5, 0, -280, 33.32],
  528. [0, 0, 0, 0, 12]]
  529. self.check_read(_empty_lines_example, a,
  530. (5, 5, 8, 'coordinate', 'real', 'general'))
  531. def test_empty_write_read(self):
  532. # https://github.com/scipy/scipy/issues/1410 (Trac #883)
  533. b = scipy.sparse.coo_array((10, 10))
  534. mmwrite(self.fn, b)
  535. assert_equal(mminfo(self.fn),
  536. (10, 10, 0, 'coordinate', 'real', 'symmetric'))
  537. a = b.toarray()
  538. b = mmread(self.fn, spmatrix=False).toarray()
  539. assert_array_almost_equal(a, b)
  540. def test_bzip2_py3(self):
  541. # test if fix for #2152 works
  542. try:
  543. # bz2 module isn't always built when building Python.
  544. import bz2
  545. except ImportError:
  546. return
  547. I = array([0, 0, 1, 2, 3, 3, 3, 4])
  548. J = array([0, 3, 1, 2, 1, 3, 4, 4])
  549. V = array([1.0, 6.0, 10.5, 0.015, 250.5, -280.0, 33.32, 12.0])
  550. b = scipy.sparse.coo_array((V, (I, J)), shape=(5, 5))
  551. mmwrite(self.fn, b)
  552. fn_bzip2 = f"{self.fn}.bz2"
  553. with open(self.fn, 'rb') as f_in:
  554. f_out = bz2.BZ2File(fn_bzip2, 'wb')
  555. f_out.write(f_in.read())
  556. f_out.close()
  557. a = mmread(fn_bzip2, spmatrix=False).toarray()
  558. assert_array_almost_equal(a, b.toarray())
  559. def test_gzip_py3(self):
  560. # test if fix for #2152 works
  561. try:
  562. # gzip module can be missing from Python installation
  563. import gzip
  564. except ImportError:
  565. return
  566. I = array([0, 0, 1, 2, 3, 3, 3, 4])
  567. J = array([0, 3, 1, 2, 1, 3, 4, 4])
  568. V = array([1.0, 6.0, 10.5, 0.015, 250.5, -280.0, 33.32, 12.0])
  569. b = scipy.sparse.coo_array((V, (I, J)), shape=(5, 5))
  570. mmwrite(self.fn, b)
  571. fn_gzip = f"{self.fn}.gz"
  572. with open(self.fn, 'rb') as f_in:
  573. f_out = gzip.open(fn_gzip, 'wb')
  574. f_out.write(f_in.read())
  575. f_out.close()
  576. a = mmread(fn_gzip, spmatrix=False).toarray()
  577. assert_array_almost_equal(a, b.toarray())
  578. def test_real_write_read(self):
  579. I = array([0, 0, 1, 2, 3, 3, 3, 4])
  580. J = array([0, 3, 1, 2, 1, 3, 4, 4])
  581. V = array([1.0, 6.0, 10.5, 0.015, 250.5, -280.0, 33.32, 12.0])
  582. b = scipy.sparse.coo_array((V, (I, J)), shape=(5, 5))
  583. mmwrite(self.fn, b)
  584. assert_equal(mminfo(self.fn),
  585. (5, 5, 8, 'coordinate', 'real', 'general'))
  586. a = b.toarray()
  587. b = mmread(self.fn, spmatrix=False).toarray()
  588. assert_array_almost_equal(a, b)
  589. def test_complex_write_read(self):
  590. I = array([0, 0, 1, 2, 3, 3, 3, 4])
  591. J = array([0, 3, 1, 2, 1, 3, 4, 4])
  592. V = array([1.0 + 3j, 6.0 + 2j, 10.50 + 0.9j, 0.015 + -4.4j,
  593. 250.5 + 0j, -280.0 + 5j, 33.32 + 6.4j, 12.00 + 0.8j])
  594. b = scipy.sparse.coo_array((V, (I, J)), shape=(5, 5))
  595. mmwrite(self.fn, b)
  596. assert_equal(mminfo(self.fn),
  597. (5, 5, 8, 'coordinate', 'complex', 'general'))
  598. a = b.toarray()
  599. b = mmread(self.fn, spmatrix=False).toarray()
  600. assert_array_almost_equal(a, b)
  601. def test_sparse_formats(self, tmp_path):
  602. # Note: `tmp_path` is a pytest fixture, it handles cleanup
  603. tmpdir = tmp_path / 'sparse_formats'
  604. tmpdir.mkdir()
  605. mats = []
  606. I = array([0, 0, 1, 2, 3, 3, 3, 4])
  607. J = array([0, 3, 1, 2, 1, 3, 4, 4])
  608. V = array([1.0, 6.0, 10.5, 0.015, 250.5, -280.0, 33.32, 12.0])
  609. mats.append(scipy.sparse.coo_array((V, (I, J)), shape=(5, 5)))
  610. V = array([1.0 + 3j, 6.0 + 2j, 10.50 + 0.9j, 0.015 + -4.4j,
  611. 250.5 + 0j, -280.0 + 5j, 33.32 + 6.4j, 12.00 + 0.8j])
  612. mats.append(scipy.sparse.coo_array((V, (I, J)), shape=(5, 5)))
  613. for mat in mats:
  614. expected = mat.toarray()
  615. for fmt in ['csr', 'csc', 'coo']:
  616. fname = tmpdir / (fmt + '.mtx')
  617. mmwrite(fname, mat.asformat(fmt))
  618. result = mmread(fname, spmatrix=False).toarray()
  619. assert_array_almost_equal(result, expected)
  620. def test_precision(self):
  621. test_values = [pi] + [10**(i) for i in range(0, -10, -1)]
  622. test_precisions = range(1, 10)
  623. for value in test_values:
  624. for precision in test_precisions:
  625. # construct sparse matrix with test value at last main diagonal
  626. n = 10**precision + 1
  627. A = scipy.sparse.dok_array((n, n))
  628. A[n-1, n-1] = value
  629. # write matrix with test precision and read again
  630. mmwrite(self.fn, A, precision=precision)
  631. A = scipy.io.mmread(self.fn, spmatrix=False)
  632. # check for right entries in matrix
  633. assert_array_equal(A.row, [n-1])
  634. assert_array_equal(A.col, [n-1])
  635. assert_allclose(A.data, [float(f'{value:.{precision}g}')])
  636. def test_bad_number_of_coordinate_header_fields(self):
  637. s = """\
  638. %%MatrixMarket matrix coordinate real general
  639. 5 5 8 999
  640. 1 1 1.000e+00
  641. 2 2 1.050e+01
  642. 3 3 1.500e-02
  643. 1 4 6.000e+00
  644. 4 2 2.505e+02
  645. 4 4 -2.800e+02
  646. 4 5 3.332e+01
  647. 5 5 1.200e+01
  648. """
  649. text = textwrap.dedent(s).encode('ascii')
  650. with pytest.raises(ValueError, match='not of length 3'):
  651. scipy.io.mmread(io.BytesIO(text))
  652. def test_gh11389():
  653. mmread(io.StringIO("%%MatrixMarket matrix coordinate complex symmetric\n"
  654. " 1 1 1\n"
  655. "1 1 -2.1846000000000e+02 0.0000000000000e+00"),
  656. spmatrix=False)
  657. def test_gh18123(tmp_path):
  658. lines = [" %%MatrixMarket matrix coordinate real general\n",
  659. "5 5 3\n",
  660. "2 3 1.0\n",
  661. "3 4 2.0\n",
  662. "3 5 3.0\n"]
  663. test_file = tmp_path / "test.mtx"
  664. with open(test_file, "w") as f:
  665. f.writelines(lines)
  666. mmread(test_file, spmatrix=False)
  667. def test_mtx_append(tmp_path):
  668. a = mmread(io.StringIO("%%MatrixMarket matrix coordinate complex symmetric\n"
  669. " 1 1 1\n"
  670. "1 1 -2.1846000000000e+02 0.0000000000000e+00"),
  671. spmatrix=False)
  672. test_writefile = tmp_path / "test_mtx"
  673. test_readfile = tmp_path / "test_mtx.mtx"
  674. mmwrite(test_writefile, a)
  675. mmread(test_readfile, spmatrix=False)
  676. def test_threadpoolctl():
  677. try:
  678. import threadpoolctl
  679. if not hasattr(threadpoolctl, "register"):
  680. pytest.skip("threadpoolctl too old")
  681. return
  682. except ImportError:
  683. pytest.skip("no threadpoolctl")
  684. return
  685. with threadpoolctl.threadpool_limits(limits=4):
  686. assert_equal(fmm.PARALLELISM, 4)
  687. with threadpoolctl.threadpool_limits(limits=2, user_api='scipy'):
  688. assert_equal(fmm.PARALLELISM, 2)
  689. def test_gh21999_file_not_exist():
  690. tmpdir = mkdtemp(suffix=str(threading.get_native_id()))
  691. wrong_fn = os.path.join(tmpdir, 'not_exist_test_file.mtx')
  692. assert_raises(FileNotFoundError, mmread, wrong_fn)