test_mio.py 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238
  1. # -*- coding: latin-1 -*-
  2. ''' Nose test generators
  3. Need function load / save / roundtrip tests
  4. '''
  5. from __future__ import division, print_function, absolute_import
  6. import os
  7. from collections import OrderedDict
  8. from os.path import join as pjoin, dirname
  9. from glob import glob
  10. from io import BytesIO
  11. from tempfile import mkdtemp
  12. from scipy._lib.six import u, text_type, string_types
  13. import warnings
  14. import shutil
  15. import gzip
  16. from numpy.testing import (assert_array_equal, assert_array_almost_equal,
  17. assert_equal, assert_)
  18. from pytest import raises as assert_raises
  19. from scipy._lib._numpy_compat import suppress_warnings
  20. import numpy as np
  21. from numpy import array
  22. import scipy.sparse as SP
  23. import scipy.io.matlab.byteordercodes as boc
  24. from scipy.io.matlab.miobase import matdims, MatWriteError, MatReadError
  25. from scipy.io.matlab.mio import (mat_reader_factory, loadmat, savemat, whosmat)
  26. from scipy.io.matlab.mio5 import (MatlabObject, MatFile5Writer, MatFile5Reader,
  27. MatlabFunction, varmats_from_mat,
  28. to_writeable, EmptyStructMarker)
  29. from scipy.io.matlab import mio5_params as mio5p
  30. test_data_path = pjoin(dirname(__file__), 'data')
  31. def mlarr(*args, **kwargs):
  32. """Convenience function to return matlab-compatible 2D array."""
  33. arr = np.array(*args, **kwargs)
  34. arr.shape = matdims(arr)
  35. return arr
  36. # Define cases to test
  37. theta = np.pi/4*np.arange(9,dtype=float).reshape(1,9)
  38. case_table4 = [
  39. {'name': 'double',
  40. 'classes': {'testdouble': 'double'},
  41. 'expected': {'testdouble': theta}
  42. }]
  43. case_table4.append(
  44. {'name': 'string',
  45. 'classes': {'teststring': 'char'},
  46. 'expected': {'teststring':
  47. array([u('"Do nine men interpret?" "Nine men," I nod.')])}
  48. })
  49. case_table4.append(
  50. {'name': 'complex',
  51. 'classes': {'testcomplex': 'double'},
  52. 'expected': {'testcomplex': np.cos(theta) + 1j*np.sin(theta)}
  53. })
  54. A = np.zeros((3,5))
  55. A[0] = list(range(1,6))
  56. A[:,0] = list(range(1,4))
  57. case_table4.append(
  58. {'name': 'matrix',
  59. 'classes': {'testmatrix': 'double'},
  60. 'expected': {'testmatrix': A},
  61. })
  62. case_table4.append(
  63. {'name': 'sparse',
  64. 'classes': {'testsparse': 'sparse'},
  65. 'expected': {'testsparse': SP.coo_matrix(A)},
  66. })
  67. B = A.astype(complex)
  68. B[0,0] += 1j
  69. case_table4.append(
  70. {'name': 'sparsecomplex',
  71. 'classes': {'testsparsecomplex': 'sparse'},
  72. 'expected': {'testsparsecomplex': SP.coo_matrix(B)},
  73. })
  74. case_table4.append(
  75. {'name': 'multi',
  76. 'classes': {'theta': 'double', 'a': 'double'},
  77. 'expected': {'theta': theta, 'a': A},
  78. })
  79. case_table4.append(
  80. {'name': 'minus',
  81. 'classes': {'testminus': 'double'},
  82. 'expected': {'testminus': mlarr(-1)},
  83. })
  84. case_table4.append(
  85. {'name': 'onechar',
  86. 'classes': {'testonechar': 'char'},
  87. 'expected': {'testonechar': array([u('r')])},
  88. })
  89. # Cell arrays stored as object arrays
  90. CA = mlarr(( # tuple for object array creation
  91. [],
  92. mlarr([1]),
  93. mlarr([[1,2]]),
  94. mlarr([[1,2,3]])), dtype=object).reshape(1,-1)
  95. CA[0,0] = array(
  96. [u('This cell contains this string and 3 arrays of increasing length')])
  97. case_table5 = [
  98. {'name': 'cell',
  99. 'classes': {'testcell': 'cell'},
  100. 'expected': {'testcell': CA}}]
  101. CAE = mlarr(( # tuple for object array creation
  102. mlarr(1),
  103. mlarr(2),
  104. mlarr([]),
  105. mlarr([]),
  106. mlarr(3)), dtype=object).reshape(1,-1)
  107. objarr = np.empty((1,1),dtype=object)
  108. objarr[0,0] = mlarr(1)
  109. case_table5.append(
  110. {'name': 'scalarcell',
  111. 'classes': {'testscalarcell': 'cell'},
  112. 'expected': {'testscalarcell': objarr}
  113. })
  114. case_table5.append(
  115. {'name': 'emptycell',
  116. 'classes': {'testemptycell': 'cell'},
  117. 'expected': {'testemptycell': CAE}})
  118. case_table5.append(
  119. {'name': 'stringarray',
  120. 'classes': {'teststringarray': 'char'},
  121. 'expected': {'teststringarray': array(
  122. [u('one '), u('two '), u('three')])},
  123. })
  124. case_table5.append(
  125. {'name': '3dmatrix',
  126. 'classes': {'test3dmatrix': 'double'},
  127. 'expected': {
  128. 'test3dmatrix': np.transpose(np.reshape(list(range(1,25)), (4,3,2)))}
  129. })
  130. st_sub_arr = array([np.sqrt(2),np.exp(1),np.pi]).reshape(1,3)
  131. dtype = [(n, object) for n in ['stringfield', 'doublefield', 'complexfield']]
  132. st1 = np.zeros((1,1), dtype)
  133. st1['stringfield'][0,0] = array([u('Rats live on no evil star.')])
  134. st1['doublefield'][0,0] = st_sub_arr
  135. st1['complexfield'][0,0] = st_sub_arr * (1 + 1j)
  136. case_table5.append(
  137. {'name': 'struct',
  138. 'classes': {'teststruct': 'struct'},
  139. 'expected': {'teststruct': st1}
  140. })
  141. CN = np.zeros((1,2), dtype=object)
  142. CN[0,0] = mlarr(1)
  143. CN[0,1] = np.zeros((1,3), dtype=object)
  144. CN[0,1][0,0] = mlarr(2, dtype=np.uint8)
  145. CN[0,1][0,1] = mlarr([[3]], dtype=np.uint8)
  146. CN[0,1][0,2] = np.zeros((1,2), dtype=object)
  147. CN[0,1][0,2][0,0] = mlarr(4, dtype=np.uint8)
  148. CN[0,1][0,2][0,1] = mlarr(5, dtype=np.uint8)
  149. case_table5.append(
  150. {'name': 'cellnest',
  151. 'classes': {'testcellnest': 'cell'},
  152. 'expected': {'testcellnest': CN},
  153. })
  154. st2 = np.empty((1,1), dtype=[(n, object) for n in ['one', 'two']])
  155. st2[0,0]['one'] = mlarr(1)
  156. st2[0,0]['two'] = np.empty((1,1), dtype=[('three', object)])
  157. st2[0,0]['two'][0,0]['three'] = array([u('number 3')])
  158. case_table5.append(
  159. {'name': 'structnest',
  160. 'classes': {'teststructnest': 'struct'},
  161. 'expected': {'teststructnest': st2}
  162. })
  163. a = np.empty((1,2), dtype=[(n, object) for n in ['one', 'two']])
  164. a[0,0]['one'] = mlarr(1)
  165. a[0,0]['two'] = mlarr(2)
  166. a[0,1]['one'] = array([u('number 1')])
  167. a[0,1]['two'] = array([u('number 2')])
  168. case_table5.append(
  169. {'name': 'structarr',
  170. 'classes': {'teststructarr': 'struct'},
  171. 'expected': {'teststructarr': a}
  172. })
  173. ODT = np.dtype([(n, object) for n in
  174. ['expr', 'inputExpr', 'args',
  175. 'isEmpty', 'numArgs', 'version']])
  176. MO = MatlabObject(np.zeros((1,1), dtype=ODT), 'inline')
  177. m0 = MO[0,0]
  178. m0['expr'] = array([u('x')])
  179. m0['inputExpr'] = array([u(' x = INLINE_INPUTS_{1};')])
  180. m0['args'] = array([u('x')])
  181. m0['isEmpty'] = mlarr(0)
  182. m0['numArgs'] = mlarr(1)
  183. m0['version'] = mlarr(1)
  184. case_table5.append(
  185. {'name': 'object',
  186. 'classes': {'testobject': 'object'},
  187. 'expected': {'testobject': MO}
  188. })
  189. fp_u_str = open(pjoin(test_data_path, 'japanese_utf8.txt'), 'rb')
  190. u_str = fp_u_str.read().decode('utf-8')
  191. fp_u_str.close()
  192. case_table5.append(
  193. {'name': 'unicode',
  194. 'classes': {'testunicode': 'char'},
  195. 'expected': {'testunicode': array([u_str])}
  196. })
  197. case_table5.append(
  198. {'name': 'sparse',
  199. 'classes': {'testsparse': 'sparse'},
  200. 'expected': {'testsparse': SP.coo_matrix(A)},
  201. })
  202. case_table5.append(
  203. {'name': 'sparsecomplex',
  204. 'classes': {'testsparsecomplex': 'sparse'},
  205. 'expected': {'testsparsecomplex': SP.coo_matrix(B)},
  206. })
  207. case_table5.append(
  208. {'name': 'bool',
  209. 'classes': {'testbools': 'logical'},
  210. 'expected': {'testbools':
  211. array([[True], [False]])},
  212. })
  213. case_table5_rt = case_table5[:]
  214. # Inline functions can't be concatenated in matlab, so RT only
  215. case_table5_rt.append(
  216. {'name': 'objectarray',
  217. 'classes': {'testobjectarray': 'object'},
  218. 'expected': {'testobjectarray': np.repeat(MO, 2).reshape(1,2)}})
  219. def types_compatible(var1, var2):
  220. """Check if types are same or compatible.
  221. 0-D numpy scalars are compatible with bare python scalars.
  222. """
  223. type1 = type(var1)
  224. type2 = type(var2)
  225. if type1 is type2:
  226. return True
  227. if type1 is np.ndarray and var1.shape == ():
  228. return type(var1.item()) is type2
  229. if type2 is np.ndarray and var2.shape == ():
  230. return type(var2.item()) is type1
  231. return False
  232. def _check_level(label, expected, actual):
  233. """ Check one level of a potentially nested array """
  234. if SP.issparse(expected): # allow different types of sparse matrices
  235. assert_(SP.issparse(actual))
  236. assert_array_almost_equal(actual.todense(),
  237. expected.todense(),
  238. err_msg=label,
  239. decimal=5)
  240. return
  241. # Check types are as expected
  242. assert_(types_compatible(expected, actual),
  243. "Expected type %s, got %s at %s" %
  244. (type(expected), type(actual), label))
  245. # A field in a record array may not be an ndarray
  246. # A scalar from a record array will be type np.void
  247. if not isinstance(expected,
  248. (np.void, np.ndarray, MatlabObject)):
  249. assert_equal(expected, actual)
  250. return
  251. # This is an ndarray-like thing
  252. assert_(expected.shape == actual.shape,
  253. msg='Expected shape %s, got %s at %s' % (expected.shape,
  254. actual.shape,
  255. label))
  256. ex_dtype = expected.dtype
  257. if ex_dtype.hasobject: # array of objects
  258. if isinstance(expected, MatlabObject):
  259. assert_equal(expected.classname, actual.classname)
  260. for i, ev in enumerate(expected):
  261. level_label = "%s, [%d], " % (label, i)
  262. _check_level(level_label, ev, actual[i])
  263. return
  264. if ex_dtype.fields: # probably recarray
  265. for fn in ex_dtype.fields:
  266. level_label = "%s, field %s, " % (label, fn)
  267. _check_level(level_label,
  268. expected[fn], actual[fn])
  269. return
  270. if ex_dtype.type in (text_type, # string or bool
  271. np.unicode_,
  272. np.bool_):
  273. assert_equal(actual, expected, err_msg=label)
  274. return
  275. # Something numeric
  276. assert_array_almost_equal(actual, expected, err_msg=label, decimal=5)
  277. def _load_check_case(name, files, case):
  278. for file_name in files:
  279. matdict = loadmat(file_name, struct_as_record=True)
  280. label = "test %s; file %s" % (name, file_name)
  281. for k, expected in case.items():
  282. k_label = "%s, variable %s" % (label, k)
  283. assert_(k in matdict, "Missing key at %s" % k_label)
  284. _check_level(k_label, expected, matdict[k])
  285. def _whos_check_case(name, files, case, classes):
  286. for file_name in files:
  287. label = "test %s; file %s" % (name, file_name)
  288. whos = whosmat(file_name)
  289. expected_whos = []
  290. for k, expected in case.items():
  291. expected_whos.append((k, expected.shape, classes[k]))
  292. whos.sort()
  293. expected_whos.sort()
  294. assert_equal(whos, expected_whos,
  295. "%s: %r != %r" % (label, whos, expected_whos)
  296. )
  297. # Round trip tests
  298. def _rt_check_case(name, expected, format):
  299. mat_stream = BytesIO()
  300. savemat(mat_stream, expected, format=format)
  301. mat_stream.seek(0)
  302. _load_check_case(name, [mat_stream], expected)
  303. # generator for load tests
  304. def test_load():
  305. for case in case_table4 + case_table5:
  306. name = case['name']
  307. expected = case['expected']
  308. filt = pjoin(test_data_path, 'test%s_*.mat' % name)
  309. files = glob(filt)
  310. assert_(len(files) > 0,
  311. "No files for test %s using filter %s" % (name, filt))
  312. _load_check_case(name, files, expected)
  313. # generator for whos tests
  314. def test_whos():
  315. for case in case_table4 + case_table5:
  316. name = case['name']
  317. expected = case['expected']
  318. classes = case['classes']
  319. filt = pjoin(test_data_path, 'test%s_*.mat' % name)
  320. files = glob(filt)
  321. assert_(len(files) > 0,
  322. "No files for test %s using filter %s" % (name, filt))
  323. _whos_check_case(name, files, expected, classes)
  324. # generator for round trip tests
  325. def test_round_trip():
  326. for case in case_table4 + case_table5_rt:
  327. case_table4_names = [case['name'] for case in case_table4]
  328. name = case['name'] + '_round_trip'
  329. expected = case['expected']
  330. for format in (['4', '5'] if case['name'] in case_table4_names else ['5']):
  331. _rt_check_case(name, expected, format)
  332. def test_gzip_simple():
  333. xdense = np.zeros((20,20))
  334. xdense[2,3] = 2.3
  335. xdense[4,5] = 4.5
  336. x = SP.csc_matrix(xdense)
  337. name = 'gzip_test'
  338. expected = {'x':x}
  339. format = '4'
  340. tmpdir = mkdtemp()
  341. try:
  342. fname = pjoin(tmpdir,name)
  343. mat_stream = gzip.open(fname,mode='wb')
  344. savemat(mat_stream, expected, format=format)
  345. mat_stream.close()
  346. mat_stream = gzip.open(fname,mode='rb')
  347. actual = loadmat(mat_stream, struct_as_record=True)
  348. mat_stream.close()
  349. finally:
  350. shutil.rmtree(tmpdir)
  351. assert_array_almost_equal(actual['x'].todense(),
  352. expected['x'].todense(),
  353. err_msg=repr(actual))
  354. def test_multiple_open():
  355. # Ticket #1039, on Windows: check that files are not left open
  356. tmpdir = mkdtemp()
  357. try:
  358. x = dict(x=np.zeros((2, 2)))
  359. fname = pjoin(tmpdir, "a.mat")
  360. # Check that file is not left open
  361. savemat(fname, x)
  362. os.unlink(fname)
  363. savemat(fname, x)
  364. loadmat(fname)
  365. os.unlink(fname)
  366. # Check that stream is left open
  367. f = open(fname, 'wb')
  368. savemat(f, x)
  369. f.seek(0)
  370. f.close()
  371. f = open(fname, 'rb')
  372. loadmat(f)
  373. f.seek(0)
  374. f.close()
  375. finally:
  376. shutil.rmtree(tmpdir)
  377. def test_mat73():
  378. # Check any hdf5 files raise an error
  379. filenames = glob(
  380. pjoin(test_data_path, 'testhdf5*.mat'))
  381. assert_(len(filenames) > 0)
  382. for filename in filenames:
  383. fp = open(filename, 'rb')
  384. assert_raises(NotImplementedError,
  385. loadmat,
  386. fp,
  387. struct_as_record=True)
  388. fp.close()
  389. def test_warnings():
  390. # This test is an echo of the previous behavior, which was to raise a
  391. # warning if the user triggered a search for mat files on the Python system
  392. # path. We can remove the test in the next version after upcoming (0.13)
  393. fname = pjoin(test_data_path, 'testdouble_7.1_GLNX86.mat')
  394. with warnings.catch_warnings():
  395. warnings.simplefilter('error')
  396. # This should not generate a warning
  397. mres = loadmat(fname, struct_as_record=True)
  398. # This neither
  399. mres = loadmat(fname, struct_as_record=False)
  400. def test_regression_653():
  401. # Saving a dictionary with only invalid keys used to raise an error. Now we
  402. # save this as an empty struct in matlab space.
  403. sio = BytesIO()
  404. savemat(sio, {'d':{1:2}}, format='5')
  405. back = loadmat(sio)['d']
  406. # Check we got an empty struct equivalent
  407. assert_equal(back.shape, (1,1))
  408. assert_equal(back.dtype, np.dtype(object))
  409. assert_(back[0,0] is None)
  410. def test_structname_len():
  411. # Test limit for length of field names in structs
  412. lim = 31
  413. fldname = 'a' * lim
  414. st1 = np.zeros((1,1), dtype=[(fldname, object)])
  415. savemat(BytesIO(), {'longstruct': st1}, format='5')
  416. fldname = 'a' * (lim+1)
  417. st1 = np.zeros((1,1), dtype=[(fldname, object)])
  418. assert_raises(ValueError, savemat, BytesIO(),
  419. {'longstruct': st1}, format='5')
  420. def test_4_and_long_field_names_incompatible():
  421. # Long field names option not supported in 4
  422. my_struct = np.zeros((1,1),dtype=[('my_fieldname',object)])
  423. assert_raises(ValueError, savemat, BytesIO(),
  424. {'my_struct':my_struct}, format='4', long_field_names=True)
  425. def test_long_field_names():
  426. # Test limit for length of field names in structs
  427. lim = 63
  428. fldname = 'a' * lim
  429. st1 = np.zeros((1,1), dtype=[(fldname, object)])
  430. savemat(BytesIO(), {'longstruct': st1}, format='5',long_field_names=True)
  431. fldname = 'a' * (lim+1)
  432. st1 = np.zeros((1,1), dtype=[(fldname, object)])
  433. assert_raises(ValueError, savemat, BytesIO(),
  434. {'longstruct': st1}, format='5',long_field_names=True)
  435. def test_long_field_names_in_struct():
  436. # Regression test - long_field_names was erased if you passed a struct
  437. # within a struct
  438. lim = 63
  439. fldname = 'a' * lim
  440. cell = np.ndarray((1,2),dtype=object)
  441. st1 = np.zeros((1,1), dtype=[(fldname, object)])
  442. cell[0,0] = st1
  443. cell[0,1] = st1
  444. savemat(BytesIO(), {'longstruct': cell}, format='5',long_field_names=True)
  445. #
  446. # Check to make sure it fails with long field names off
  447. #
  448. assert_raises(ValueError, savemat, BytesIO(),
  449. {'longstruct': cell}, format='5', long_field_names=False)
  450. def test_cell_with_one_thing_in_it():
  451. # Regression test - make a cell array that's 1 x 2 and put two
  452. # strings in it. It works. Make a cell array that's 1 x 1 and put
  453. # a string in it. It should work but, in the old days, it didn't.
  454. cells = np.ndarray((1,2),dtype=object)
  455. cells[0,0] = 'Hello'
  456. cells[0,1] = 'World'
  457. savemat(BytesIO(), {'x': cells}, format='5')
  458. cells = np.ndarray((1,1),dtype=object)
  459. cells[0,0] = 'Hello, world'
  460. savemat(BytesIO(), {'x': cells}, format='5')
  461. def test_writer_properties():
  462. # Tests getting, setting of properties of matrix writer
  463. mfw = MatFile5Writer(BytesIO())
  464. assert_equal(mfw.global_vars, [])
  465. mfw.global_vars = ['avar']
  466. assert_equal(mfw.global_vars, ['avar'])
  467. assert_equal(mfw.unicode_strings, False)
  468. mfw.unicode_strings = True
  469. assert_equal(mfw.unicode_strings, True)
  470. assert_equal(mfw.long_field_names, False)
  471. mfw.long_field_names = True
  472. assert_equal(mfw.long_field_names, True)
  473. def test_use_small_element():
  474. # Test whether we're using small data element or not
  475. sio = BytesIO()
  476. wtr = MatFile5Writer(sio)
  477. # First check size for no sde for name
  478. arr = np.zeros(10)
  479. wtr.put_variables({'aaaaa': arr})
  480. w_sz = len(sio.getvalue())
  481. # Check small name results in largish difference in size
  482. sio.truncate(0)
  483. sio.seek(0)
  484. wtr.put_variables({'aaaa': arr})
  485. assert_(w_sz - len(sio.getvalue()) > 4)
  486. # Whereas increasing name size makes less difference
  487. sio.truncate(0)
  488. sio.seek(0)
  489. wtr.put_variables({'aaaaaa': arr})
  490. assert_(len(sio.getvalue()) - w_sz < 4)
  491. def test_save_dict():
  492. # Test that dict can be saved (as recarray), loaded as matstruct
  493. dict_types = ((dict, False), (OrderedDict, True),)
  494. ab_exp = np.array([[(1, 2)]], dtype=[('a', object), ('b', object)])
  495. ba_exp = np.array([[(2, 1)]], dtype=[('b', object), ('a', object)])
  496. for dict_type, is_ordered in dict_types:
  497. # Initialize with tuples to keep order for OrderedDict
  498. d = dict_type([('a', 1), ('b', 2)])
  499. stream = BytesIO()
  500. savemat(stream, {'dict': d})
  501. stream.seek(0)
  502. vals = loadmat(stream)['dict']
  503. assert_equal(set(vals.dtype.names), set(['a', 'b']))
  504. if is_ordered: # Input was ordered, output in ab order
  505. assert_array_equal(vals, ab_exp)
  506. else: # Not ordered input, either order output
  507. if vals.dtype.names[0] == 'a':
  508. assert_array_equal(vals, ab_exp)
  509. else:
  510. assert_array_equal(vals, ba_exp)
  511. def test_1d_shape():
  512. # New 5 behavior is 1D -> row vector
  513. arr = np.arange(5)
  514. for format in ('4', '5'):
  515. # Column is the default
  516. stream = BytesIO()
  517. savemat(stream, {'oned': arr}, format=format)
  518. vals = loadmat(stream)
  519. assert_equal(vals['oned'].shape, (1, 5))
  520. # can be explicitly 'column' for oned_as
  521. stream = BytesIO()
  522. savemat(stream, {'oned':arr},
  523. format=format,
  524. oned_as='column')
  525. vals = loadmat(stream)
  526. assert_equal(vals['oned'].shape, (5,1))
  527. # but different from 'row'
  528. stream = BytesIO()
  529. savemat(stream, {'oned':arr},
  530. format=format,
  531. oned_as='row')
  532. vals = loadmat(stream)
  533. assert_equal(vals['oned'].shape, (1,5))
  534. def test_compression():
  535. arr = np.zeros(100).reshape((5,20))
  536. arr[2,10] = 1
  537. stream = BytesIO()
  538. savemat(stream, {'arr':arr})
  539. raw_len = len(stream.getvalue())
  540. vals = loadmat(stream)
  541. assert_array_equal(vals['arr'], arr)
  542. stream = BytesIO()
  543. savemat(stream, {'arr':arr}, do_compression=True)
  544. compressed_len = len(stream.getvalue())
  545. vals = loadmat(stream)
  546. assert_array_equal(vals['arr'], arr)
  547. assert_(raw_len > compressed_len)
  548. # Concatenate, test later
  549. arr2 = arr.copy()
  550. arr2[0,0] = 1
  551. stream = BytesIO()
  552. savemat(stream, {'arr':arr, 'arr2':arr2}, do_compression=False)
  553. vals = loadmat(stream)
  554. assert_array_equal(vals['arr2'], arr2)
  555. stream = BytesIO()
  556. savemat(stream, {'arr':arr, 'arr2':arr2}, do_compression=True)
  557. vals = loadmat(stream)
  558. assert_array_equal(vals['arr2'], arr2)
  559. def test_single_object():
  560. stream = BytesIO()
  561. savemat(stream, {'A':np.array(1, dtype=object)})
  562. def test_skip_variable():
  563. # Test skipping over the first of two variables in a MAT file
  564. # using mat_reader_factory and put_variables to read them in.
  565. #
  566. # This is a regression test of a problem that's caused by
  567. # using the compressed file reader seek instead of the raw file
  568. # I/O seek when skipping over a compressed chunk.
  569. #
  570. # The problem arises when the chunk is large: this file has
  571. # a 256x256 array of random (uncompressible) doubles.
  572. #
  573. filename = pjoin(test_data_path,'test_skip_variable.mat')
  574. #
  575. # Prove that it loads with loadmat
  576. #
  577. d = loadmat(filename, struct_as_record=True)
  578. assert_('first' in d)
  579. assert_('second' in d)
  580. #
  581. # Make the factory
  582. #
  583. factory, file_opened = mat_reader_factory(filename, struct_as_record=True)
  584. #
  585. # This is where the factory breaks with an error in MatMatrixGetter.to_next
  586. #
  587. d = factory.get_variables('second')
  588. assert_('second' in d)
  589. factory.mat_stream.close()
  590. def test_empty_struct():
  591. # ticket 885
  592. filename = pjoin(test_data_path,'test_empty_struct.mat')
  593. # before ticket fix, this would crash with ValueError, empty data
  594. # type
  595. d = loadmat(filename, struct_as_record=True)
  596. a = d['a']
  597. assert_equal(a.shape, (1,1))
  598. assert_equal(a.dtype, np.dtype(object))
  599. assert_(a[0,0] is None)
  600. stream = BytesIO()
  601. arr = np.array((), dtype='U')
  602. # before ticket fix, this used to give data type not understood
  603. savemat(stream, {'arr':arr})
  604. d = loadmat(stream)
  605. a2 = d['arr']
  606. assert_array_equal(a2, arr)
  607. def test_save_empty_dict():
  608. # saving empty dict also gives empty struct
  609. stream = BytesIO()
  610. savemat(stream, {'arr': {}})
  611. d = loadmat(stream)
  612. a = d['arr']
  613. assert_equal(a.shape, (1,1))
  614. assert_equal(a.dtype, np.dtype(object))
  615. assert_(a[0,0] is None)
  616. def assert_any_equal(output, alternatives):
  617. """ Assert `output` is equal to at least one element in `alternatives`
  618. """
  619. one_equal = False
  620. for expected in alternatives:
  621. if np.all(output == expected):
  622. one_equal = True
  623. break
  624. assert_(one_equal)
  625. def test_to_writeable():
  626. # Test to_writeable function
  627. res = to_writeable(np.array([1])) # pass through ndarrays
  628. assert_equal(res.shape, (1,))
  629. assert_array_equal(res, 1)
  630. # Dict fields can be written in any order
  631. expected1 = np.array([(1, 2)], dtype=[('a', '|O8'), ('b', '|O8')])
  632. expected2 = np.array([(2, 1)], dtype=[('b', '|O8'), ('a', '|O8')])
  633. alternatives = (expected1, expected2)
  634. assert_any_equal(to_writeable({'a':1,'b':2}), alternatives)
  635. # Fields with underscores discarded
  636. assert_any_equal(to_writeable({'a':1,'b':2, '_c':3}), alternatives)
  637. # Not-string fields discarded
  638. assert_any_equal(to_writeable({'a':1,'b':2, 100:3}), alternatives)
  639. # String fields that are valid Python identifiers discarded
  640. assert_any_equal(to_writeable({'a':1,'b':2, '99':3}), alternatives)
  641. # Object with field names is equivalent
  642. class klass(object):
  643. pass
  644. c = klass
  645. c.a = 1
  646. c.b = 2
  647. assert_any_equal(to_writeable(c), alternatives)
  648. # empty list and tuple go to empty array
  649. res = to_writeable([])
  650. assert_equal(res.shape, (0,))
  651. assert_equal(res.dtype.type, np.float64)
  652. res = to_writeable(())
  653. assert_equal(res.shape, (0,))
  654. assert_equal(res.dtype.type, np.float64)
  655. # None -> None
  656. assert_(to_writeable(None) is None)
  657. # String to strings
  658. assert_equal(to_writeable('a string').dtype.type, np.str_)
  659. # Scalars to numpy to numpy scalars
  660. res = to_writeable(1)
  661. assert_equal(res.shape, ())
  662. assert_equal(res.dtype.type, np.array(1).dtype.type)
  663. assert_array_equal(res, 1)
  664. # Empty dict returns EmptyStructMarker
  665. assert_(to_writeable({}) is EmptyStructMarker)
  666. # Object does not have (even empty) __dict__
  667. assert_(to_writeable(object()) is None)
  668. # Custom object does have empty __dict__, returns EmptyStructMarker
  669. class C(object):
  670. pass
  671. assert_(to_writeable(c()) is EmptyStructMarker)
  672. # dict keys with legal characters are convertible
  673. res = to_writeable({'a': 1})['a']
  674. assert_equal(res.shape, (1,))
  675. assert_equal(res.dtype.type, np.object_)
  676. # Only fields with illegal characters, falls back to EmptyStruct
  677. assert_(to_writeable({'1':1}) is EmptyStructMarker)
  678. assert_(to_writeable({'_a':1}) is EmptyStructMarker)
  679. # Unless there are valid fields, in which case structured array
  680. assert_equal(to_writeable({'1':1, 'f': 2}),
  681. np.array([(2,)], dtype=[('f', '|O8')]))
  682. def test_recarray():
  683. # check roundtrip of structured array
  684. dt = [('f1', 'f8'),
  685. ('f2', 'S10')]
  686. arr = np.zeros((2,), dtype=dt)
  687. arr[0]['f1'] = 0.5
  688. arr[0]['f2'] = 'python'
  689. arr[1]['f1'] = 99
  690. arr[1]['f2'] = 'not perl'
  691. stream = BytesIO()
  692. savemat(stream, {'arr': arr})
  693. d = loadmat(stream, struct_as_record=False)
  694. a20 = d['arr'][0,0]
  695. assert_equal(a20.f1, 0.5)
  696. assert_equal(a20.f2, 'python')
  697. d = loadmat(stream, struct_as_record=True)
  698. a20 = d['arr'][0,0]
  699. assert_equal(a20['f1'], 0.5)
  700. assert_equal(a20['f2'], 'python')
  701. # structs always come back as object types
  702. assert_equal(a20.dtype, np.dtype([('f1', 'O'),
  703. ('f2', 'O')]))
  704. a21 = d['arr'].flat[1]
  705. assert_equal(a21['f1'], 99)
  706. assert_equal(a21['f2'], 'not perl')
  707. def test_save_object():
  708. class C(object):
  709. pass
  710. c = C()
  711. c.field1 = 1
  712. c.field2 = 'a string'
  713. stream = BytesIO()
  714. savemat(stream, {'c': c})
  715. d = loadmat(stream, struct_as_record=False)
  716. c2 = d['c'][0,0]
  717. assert_equal(c2.field1, 1)
  718. assert_equal(c2.field2, 'a string')
  719. d = loadmat(stream, struct_as_record=True)
  720. c2 = d['c'][0,0]
  721. assert_equal(c2['field1'], 1)
  722. assert_equal(c2['field2'], 'a string')
  723. def test_read_opts():
  724. # tests if read is seeing option sets, at initialization and after
  725. # initialization
  726. arr = np.arange(6).reshape(1,6)
  727. stream = BytesIO()
  728. savemat(stream, {'a': arr})
  729. rdr = MatFile5Reader(stream)
  730. back_dict = rdr.get_variables()
  731. rarr = back_dict['a']
  732. assert_array_equal(rarr, arr)
  733. rdr = MatFile5Reader(stream, squeeze_me=True)
  734. assert_array_equal(rdr.get_variables()['a'], arr.reshape((6,)))
  735. rdr.squeeze_me = False
  736. assert_array_equal(rarr, arr)
  737. rdr = MatFile5Reader(stream, byte_order=boc.native_code)
  738. assert_array_equal(rdr.get_variables()['a'], arr)
  739. # inverted byte code leads to error on read because of swapped
  740. # header etc
  741. rdr = MatFile5Reader(stream, byte_order=boc.swapped_code)
  742. assert_raises(Exception, rdr.get_variables)
  743. rdr.byte_order = boc.native_code
  744. assert_array_equal(rdr.get_variables()['a'], arr)
  745. arr = np.array(['a string'])
  746. stream.truncate(0)
  747. stream.seek(0)
  748. savemat(stream, {'a': arr})
  749. rdr = MatFile5Reader(stream)
  750. assert_array_equal(rdr.get_variables()['a'], arr)
  751. rdr = MatFile5Reader(stream, chars_as_strings=False)
  752. carr = np.atleast_2d(np.array(list(arr.item()), dtype='U1'))
  753. assert_array_equal(rdr.get_variables()['a'], carr)
  754. rdr.chars_as_strings = True
  755. assert_array_equal(rdr.get_variables()['a'], arr)
  756. def test_empty_string():
  757. # make sure reading empty string does not raise error
  758. estring_fname = pjoin(test_data_path, 'single_empty_string.mat')
  759. fp = open(estring_fname, 'rb')
  760. rdr = MatFile5Reader(fp)
  761. d = rdr.get_variables()
  762. fp.close()
  763. assert_array_equal(d['a'], np.array([], dtype='U1'))
  764. # empty string round trip. Matlab cannot distinguish
  765. # between a string array that is empty, and a string array
  766. # containing a single empty string, because it stores strings as
  767. # arrays of char. There is no way of having an array of char that
  768. # is not empty, but contains an empty string.
  769. stream = BytesIO()
  770. savemat(stream, {'a': np.array([''])})
  771. rdr = MatFile5Reader(stream)
  772. d = rdr.get_variables()
  773. assert_array_equal(d['a'], np.array([], dtype='U1'))
  774. stream.truncate(0)
  775. stream.seek(0)
  776. savemat(stream, {'a': np.array([], dtype='U1')})
  777. rdr = MatFile5Reader(stream)
  778. d = rdr.get_variables()
  779. assert_array_equal(d['a'], np.array([], dtype='U1'))
  780. stream.close()
  781. def test_corrupted_data():
  782. import zlib
  783. for exc, fname in [(ValueError, 'corrupted_zlib_data.mat'),
  784. (zlib.error, 'corrupted_zlib_checksum.mat')]:
  785. with open(pjoin(test_data_path, fname), 'rb') as fp:
  786. rdr = MatFile5Reader(fp)
  787. assert_raises(exc, rdr.get_variables)
  788. def test_corrupted_data_check_can_be_disabled():
  789. with open(pjoin(test_data_path, 'corrupted_zlib_data.mat'), 'rb') as fp:
  790. rdr = MatFile5Reader(fp, verify_compressed_data_integrity=False)
  791. rdr.get_variables()
  792. def test_read_both_endian():
  793. # make sure big- and little- endian data is read correctly
  794. for fname in ('big_endian.mat', 'little_endian.mat'):
  795. fp = open(pjoin(test_data_path, fname), 'rb')
  796. rdr = MatFile5Reader(fp)
  797. d = rdr.get_variables()
  798. fp.close()
  799. assert_array_equal(d['strings'],
  800. np.array([['hello'],
  801. ['world']], dtype=object))
  802. assert_array_equal(d['floats'],
  803. np.array([[2., 3.],
  804. [3., 4.]], dtype=np.float32))
  805. def test_write_opposite_endian():
  806. # We don't support writing opposite endian .mat files, but we need to behave
  807. # correctly if the user supplies an other-endian numpy array to write out
  808. float_arr = np.array([[2., 3.],
  809. [3., 4.]])
  810. int_arr = np.arange(6).reshape((2, 3))
  811. uni_arr = np.array(['hello', 'world'], dtype='U')
  812. stream = BytesIO()
  813. savemat(stream, {'floats': float_arr.byteswap().newbyteorder(),
  814. 'ints': int_arr.byteswap().newbyteorder(),
  815. 'uni_arr': uni_arr.byteswap().newbyteorder()})
  816. rdr = MatFile5Reader(stream)
  817. d = rdr.get_variables()
  818. assert_array_equal(d['floats'], float_arr)
  819. assert_array_equal(d['ints'], int_arr)
  820. assert_array_equal(d['uni_arr'], uni_arr)
  821. stream.close()
  822. def test_logical_array():
  823. # The roundtrip test doesn't verify that we load the data up with the
  824. # correct (bool) dtype
  825. with open(pjoin(test_data_path, 'testbool_8_WIN64.mat'), 'rb') as fobj:
  826. rdr = MatFile5Reader(fobj, mat_dtype=True)
  827. d = rdr.get_variables()
  828. x = np.array([[True], [False]], dtype=np.bool_)
  829. assert_array_equal(d['testbools'], x)
  830. assert_equal(d['testbools'].dtype, x.dtype)
  831. def test_logical_out_type():
  832. # Confirm that bool type written as uint8, uint8 class
  833. # See gh-4022
  834. stream = BytesIO()
  835. barr = np.array([False, True, False])
  836. savemat(stream, {'barray': barr})
  837. stream.seek(0)
  838. reader = MatFile5Reader(stream)
  839. reader.initialize_read()
  840. reader.read_file_header()
  841. hdr, _ = reader.read_var_header()
  842. assert_equal(hdr.mclass, mio5p.mxUINT8_CLASS)
  843. assert_equal(hdr.is_logical, True)
  844. var = reader.read_var_array(hdr, False)
  845. assert_equal(var.dtype.type, np.uint8)
  846. def test_mat4_3d():
  847. # test behavior when writing 3D arrays to matlab 4 files
  848. stream = BytesIO()
  849. arr = np.arange(24).reshape((2,3,4))
  850. assert_raises(ValueError, savemat, stream, {'a': arr}, True, '4')
  851. def test_func_read():
  852. func_eg = pjoin(test_data_path, 'testfunc_7.4_GLNX86.mat')
  853. fp = open(func_eg, 'rb')
  854. rdr = MatFile5Reader(fp)
  855. d = rdr.get_variables()
  856. fp.close()
  857. assert_(isinstance(d['testfunc'], MatlabFunction))
  858. stream = BytesIO()
  859. wtr = MatFile5Writer(stream)
  860. assert_raises(MatWriteError, wtr.put_variables, d)
  861. def test_mat_dtype():
  862. double_eg = pjoin(test_data_path, 'testmatrix_6.1_SOL2.mat')
  863. fp = open(double_eg, 'rb')
  864. rdr = MatFile5Reader(fp, mat_dtype=False)
  865. d = rdr.get_variables()
  866. fp.close()
  867. assert_equal(d['testmatrix'].dtype.kind, 'u')
  868. fp = open(double_eg, 'rb')
  869. rdr = MatFile5Reader(fp, mat_dtype=True)
  870. d = rdr.get_variables()
  871. fp.close()
  872. assert_equal(d['testmatrix'].dtype.kind, 'f')
  873. def test_sparse_in_struct():
  874. # reproduces bug found by DC where Cython code was insisting on
  875. # ndarray return type, but getting sparse matrix
  876. st = {'sparsefield': SP.coo_matrix(np.eye(4))}
  877. stream = BytesIO()
  878. savemat(stream, {'a':st})
  879. d = loadmat(stream, struct_as_record=True)
  880. assert_array_equal(d['a'][0,0]['sparsefield'].todense(), np.eye(4))
  881. def test_mat_struct_squeeze():
  882. stream = BytesIO()
  883. in_d = {'st':{'one':1, 'two':2}}
  884. savemat(stream, in_d)
  885. # no error without squeeze
  886. out_d = loadmat(stream, struct_as_record=False)
  887. # previous error was with squeeze, with mat_struct
  888. out_d = loadmat(stream,
  889. struct_as_record=False,
  890. squeeze_me=True,
  891. )
  892. def test_scalar_squeeze():
  893. stream = BytesIO()
  894. in_d = {'scalar': [[0.1]], 'string': 'my name', 'st':{'one':1, 'two':2}}
  895. savemat(stream, in_d)
  896. out_d = loadmat(stream, squeeze_me=True)
  897. assert_(isinstance(out_d['scalar'], float))
  898. assert_(isinstance(out_d['string'], string_types))
  899. assert_(isinstance(out_d['st'], np.ndarray))
  900. def test_str_round():
  901. # from report by Angus McMorland on mailing list 3 May 2010
  902. stream = BytesIO()
  903. in_arr = np.array(['Hello', 'Foob'])
  904. out_arr = np.array(['Hello', 'Foob '])
  905. savemat(stream, dict(a=in_arr))
  906. res = loadmat(stream)
  907. # resulted in ['HloolFoa', 'elWrdobr']
  908. assert_array_equal(res['a'], out_arr)
  909. stream.truncate(0)
  910. stream.seek(0)
  911. # Make Fortran ordered version of string
  912. in_str = in_arr.tostring(order='F')
  913. in_from_str = np.ndarray(shape=a.shape,
  914. dtype=in_arr.dtype,
  915. order='F',
  916. buffer=in_str)
  917. savemat(stream, dict(a=in_from_str))
  918. assert_array_equal(res['a'], out_arr)
  919. # unicode save did lead to buffer too small error
  920. stream.truncate(0)
  921. stream.seek(0)
  922. in_arr_u = in_arr.astype('U')
  923. out_arr_u = out_arr.astype('U')
  924. savemat(stream, {'a': in_arr_u})
  925. res = loadmat(stream)
  926. assert_array_equal(res['a'], out_arr_u)
  927. def test_fieldnames():
  928. # Check that field names are as expected
  929. stream = BytesIO()
  930. savemat(stream, {'a': {'a':1, 'b':2}})
  931. res = loadmat(stream)
  932. field_names = res['a'].dtype.names
  933. assert_equal(set(field_names), set(('a', 'b')))
  934. def test_loadmat_varnames():
  935. # Test that we can get just one variable from a mat file using loadmat
  936. mat5_sys_names = ['__globals__',
  937. '__header__',
  938. '__version__']
  939. for eg_file, sys_v_names in (
  940. (pjoin(test_data_path, 'testmulti_4.2c_SOL2.mat'), []), (pjoin(
  941. test_data_path, 'testmulti_7.4_GLNX86.mat'), mat5_sys_names)):
  942. vars = loadmat(eg_file)
  943. assert_equal(set(vars.keys()), set(['a', 'theta'] + sys_v_names))
  944. vars = loadmat(eg_file, variable_names='a')
  945. assert_equal(set(vars.keys()), set(['a'] + sys_v_names))
  946. vars = loadmat(eg_file, variable_names=['a'])
  947. assert_equal(set(vars.keys()), set(['a'] + sys_v_names))
  948. vars = loadmat(eg_file, variable_names=['theta'])
  949. assert_equal(set(vars.keys()), set(['theta'] + sys_v_names))
  950. vars = loadmat(eg_file, variable_names=('theta',))
  951. assert_equal(set(vars.keys()), set(['theta'] + sys_v_names))
  952. vars = loadmat(eg_file, variable_names=[])
  953. assert_equal(set(vars.keys()), set(sys_v_names))
  954. vnames = ['theta']
  955. vars = loadmat(eg_file, variable_names=vnames)
  956. assert_equal(vnames, ['theta'])
  957. def test_round_types():
  958. # Check that saving, loading preserves dtype in most cases
  959. arr = np.arange(10)
  960. stream = BytesIO()
  961. for dts in ('f8','f4','i8','i4','i2','i1',
  962. 'u8','u4','u2','u1','c16','c8'):
  963. stream.truncate(0)
  964. stream.seek(0) # needed for BytesIO in python 3
  965. savemat(stream, {'arr': arr.astype(dts)})
  966. vars = loadmat(stream)
  967. assert_equal(np.dtype(dts), vars['arr'].dtype)
  968. def test_varmats_from_mat():
  969. # Make a mat file with several variables, write it, read it back
  970. names_vars = (('arr', mlarr(np.arange(10))),
  971. ('mystr', mlarr('a string')),
  972. ('mynum', mlarr(10)))
  973. # Dict like thing to give variables in defined order
  974. class C(object):
  975. def items(self):
  976. return names_vars
  977. stream = BytesIO()
  978. savemat(stream, C())
  979. varmats = varmats_from_mat(stream)
  980. assert_equal(len(varmats), 3)
  981. for i in range(3):
  982. name, var_stream = varmats[i]
  983. exp_name, exp_res = names_vars[i]
  984. assert_equal(name, exp_name)
  985. res = loadmat(var_stream)
  986. assert_array_equal(res[name], exp_res)
  987. def test_one_by_zero():
  988. # Test 1x0 chars get read correctly
  989. func_eg = pjoin(test_data_path, 'one_by_zero_char.mat')
  990. fp = open(func_eg, 'rb')
  991. rdr = MatFile5Reader(fp)
  992. d = rdr.get_variables()
  993. fp.close()
  994. assert_equal(d['var'].shape, (0,))
  995. def test_load_mat4_le():
  996. # We were getting byte order wrong when reading little-endian floa64 dense
  997. # matrices on big-endian platforms
  998. mat4_fname = pjoin(test_data_path, 'test_mat4_le_floats.mat')
  999. vars = loadmat(mat4_fname)
  1000. assert_array_equal(vars['a'], [[0.1, 1.2]])
  1001. def test_unicode_mat4():
  1002. # Mat4 should save unicode as latin1
  1003. bio = BytesIO()
  1004. var = {'second_cat': u('Schrödinger')}
  1005. savemat(bio, var, format='4')
  1006. var_back = loadmat(bio)
  1007. assert_equal(var_back['second_cat'], var['second_cat'])
  1008. def test_logical_sparse():
  1009. # Test we can read logical sparse stored in mat file as bytes.
  1010. # See https://github.com/scipy/scipy/issues/3539.
  1011. # In some files saved by MATLAB, the sparse data elements (Real Part
  1012. # Subelement in MATLAB speak) are stored with apparent type double
  1013. # (miDOUBLE) but are in fact single bytes.
  1014. filename = pjoin(test_data_path,'logical_sparse.mat')
  1015. # Before fix, this would crash with:
  1016. # ValueError: indices and data should have the same size
  1017. d = loadmat(filename, struct_as_record=True)
  1018. log_sp = d['sp_log_5_4']
  1019. assert_(isinstance(log_sp, SP.csc_matrix))
  1020. assert_equal(log_sp.dtype.type, np.bool_)
  1021. assert_array_equal(log_sp.toarray(),
  1022. [[True, True, True, False],
  1023. [False, False, True, False],
  1024. [False, False, True, False],
  1025. [False, False, False, False],
  1026. [False, False, False, False]])
  1027. def test_empty_sparse():
  1028. # Can we read empty sparse matrices?
  1029. sio = BytesIO()
  1030. import scipy.sparse
  1031. empty_sparse = scipy.sparse.csr_matrix([[0,0],[0,0]])
  1032. savemat(sio, dict(x=empty_sparse))
  1033. sio.seek(0)
  1034. res = loadmat(sio)
  1035. assert_array_equal(res['x'].shape, empty_sparse.shape)
  1036. assert_array_equal(res['x'].todense(), 0)
  1037. # Do empty sparse matrices get written with max nnz 1?
  1038. # See https://github.com/scipy/scipy/issues/4208
  1039. sio.seek(0)
  1040. reader = MatFile5Reader(sio)
  1041. reader.initialize_read()
  1042. reader.read_file_header()
  1043. hdr, _ = reader.read_var_header()
  1044. assert_equal(hdr.nzmax, 1)
  1045. def test_empty_mat_error():
  1046. # Test we get a specific warning for an empty mat file
  1047. sio = BytesIO()
  1048. assert_raises(MatReadError, loadmat, sio)
  1049. def test_miuint32_compromise():
  1050. # Reader should accept miUINT32 for miINT32, but check signs
  1051. # mat file with miUINT32 for miINT32, but OK values
  1052. filename = pjoin(test_data_path, 'miuint32_for_miint32.mat')
  1053. res = loadmat(filename)
  1054. assert_equal(res['an_array'], np.arange(10)[None, :])
  1055. # mat file with miUINT32 for miINT32, with negative value
  1056. filename = pjoin(test_data_path, 'bad_miuint32.mat')
  1057. with suppress_warnings() as sup:
  1058. sup.filter(message="unclosed file") # Py3k ResourceWarning
  1059. assert_raises(ValueError, loadmat, filename)
  1060. def test_miutf8_for_miint8_compromise():
  1061. # Check reader accepts ascii as miUTF8 for array names
  1062. filename = pjoin(test_data_path, 'miutf8_array_name.mat')
  1063. res = loadmat(filename)
  1064. assert_equal(res['array_name'], [[1]])
  1065. # mat file with non-ascii utf8 name raises error
  1066. filename = pjoin(test_data_path, 'bad_miutf8_array_name.mat')
  1067. with suppress_warnings() as sup:
  1068. sup.filter(message="unclosed file") # Py3k ResourceWarning
  1069. assert_raises(ValueError, loadmat, filename)
  1070. def test_bad_utf8():
  1071. # Check that reader reads bad UTF with 'replace' option
  1072. filename = pjoin(test_data_path,'broken_utf8.mat')
  1073. res = loadmat(filename)
  1074. assert_equal(res['bad_string'],
  1075. b'\x80 am broken'.decode('utf8', 'replace'))
  1076. def test_save_unicode_field(tmpdir):
  1077. filename = os.path.join(str(tmpdir), 'test.mat')
  1078. test_dict = {u'a':{u'b':1,u'c':'test_str'}}
  1079. savemat(filename, test_dict)
  1080. def test_filenotfound():
  1081. # Check the correct error is thrown
  1082. assert_raises(IOError, loadmat, "NotExistentFile00.mat")
  1083. assert_raises(IOError, loadmat, "NotExistentFile00")