Coverage for nilearn/_utils/tests/test_numpy_conversions.py: 0%

111 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-20 10:58 +0200

1"""Test the numpy_conversions module. 

2 

3This test file is in nilearn/tests because Nosetest, 

4which we historically used, 

5ignores modules whose name starts with an underscore. 

6""" 

7 

8from pathlib import Path 

9 

10import numpy as np 

11import pytest 

12 

13from nilearn._utils.numpy_conversions import as_ndarray, csv_to_array 

14 

15 

16def are_arrays_identical(arr1, arr2): 

17 """Check if two 1-dimensional array point to the same buffer. 

18 

19 The check is performed only on the first value of the arrays. For 

20 this test to be reliable, arr2 must not point to a subset of arr1. 

21 For example, if arr2 = arr1[1:] has been executed just before calling 

22 this function, the test will FAIL, even if the same buffer is used by 

23 both arrays. arr2 = arr1[:1] will succeed though. 

24 

25 dtypes are not supposed to be identical. 

26 """ 

27 # Modify the first value in arr1 twice, and see if corresponding 

28 # value in arr2 has changed. Changing the value twice is required, since 

29 # the original value could be the first value that we use. 

30 

31 orig1 = arr1[0] 

32 orig2 = arr2[0] 

33 

34 arr1[0] = 0 

35 if arr2[0] != orig2: 

36 arr1[0] = orig1 

37 return True 

38 

39 arr1[0] = 1 

40 if arr2[0] != orig2: 

41 arr1[0] = orig1 

42 return True 

43 

44 arr1[0] = orig1 

45 return False 

46 

47 

48def test_are_array_identical(): 

49 arr1 = np.ones(4) 

50 orig1 = arr1.copy() 

51 

52 arr2 = arr1 

53 orig2 = arr2.copy() 

54 

55 assert are_arrays_identical(arr1, arr2) 

56 np.testing.assert_array_almost_equal(orig1, arr1, decimal=10) 

57 np.testing.assert_array_almost_equal(orig2, arr2, decimal=10) 

58 

59 arr2 = arr1[:1] 

60 orig2 = arr2.copy() 

61 assert are_arrays_identical(arr1, arr2) 

62 np.testing.assert_array_almost_equal(orig1, arr1, decimal=10) 

63 np.testing.assert_array_almost_equal(orig2, arr2, decimal=10) 

64 

65 arr2 = arr1[1:] 

66 orig2 = arr2.copy() 

67 assert not are_arrays_identical(arr1, arr2) 

68 np.testing.assert_array_almost_equal(orig1, arr1, decimal=10) 

69 np.testing.assert_array_almost_equal(orig2, arr2, decimal=10) 

70 

71 arr2 = arr1.copy() 

72 orig2 = arr2.copy() 

73 assert not are_arrays_identical(arr1, arr2) 

74 np.testing.assert_array_almost_equal(orig1, arr1, decimal=10) 

75 np.testing.assert_array_almost_equal(orig2, arr2, decimal=10) 

76 

77 

78@pytest.mark.parametrize( 

79 "input_dtype, input_order, copy, output_dtype, output_order, was_copied", 

80 [ 

81 # no-op 

82 (float, "C", False, None, None, False), 

83 (float, "F", False, None, None, False), 

84 # simple copy 

85 (float, "C", True, None, None, True), 

86 (float, "F", True, None, None, True), 

87 # dtype provided, identical 

88 (float, "C", False, float, None, False), 

89 (float, "F", False, float, None, False), 

90 # dtype changed 

91 (float, "C", False, np.float32, None, True), 

92 (float, "F", False, np.float32, None, True), 

93 # dtype and order provided, but identical 

94 (float, "C", False, float, "C", False), 

95 (float, "F", False, float, "F", False), 

96 # order provided, unchanged 

97 (float, "C", False, None, "C", False), 

98 (float, "F", False, None, "F", False), 

99 (float, "C", True, None, "C", True), 

100 (float, "F", True, None, "F", True), 

101 # order provided, changed 

102 (float, "C", False, None, "F", True), 

103 (float, "F", False, None, "C", True), 

104 (float, "C", True, None, "F", True), 

105 (float, "F", True, None, "C", True), 

106 # Special case for int8 <-> bool conversion. 

107 (np.int8, "C", False, bool, None, False), 

108 (np.int8, "F", False, bool, None, False), 

109 (np.int8, "C", False, bool, "C", False), 

110 (np.int8, "F", False, bool, "F", False), 

111 (np.int8, "C", False, bool, "F", True), 

112 (np.int8, "F", False, bool, "C", True), 

113 (np.int8, "C", True, bool, None, True), 

114 (np.int8, "F", True, bool, None, True), 

115 (np.int8, "C", True, bool, "C", True), 

116 (np.int8, "F", True, bool, "F", True), 

117 (bool, "C", False, np.int8, None, False), 

118 (bool, "F", False, np.int8, None, False), 

119 (bool, "C", False, np.int8, "C", False), 

120 (bool, "F", False, np.int8, "F", False), 

121 (bool, "C", False, np.int8, "F", True), 

122 (bool, "F", False, np.int8, "C", True), 

123 (bool, "C", True, np.int8, None, True), 

124 (bool, "F", True, np.int8, None, True), 

125 (bool, "C", True, np.int8, "C", True), 

126 (bool, "F", True, np.int8, "F", True), 

127 ], 

128) 

129def test_as_ndarray( 

130 input_dtype, input_order, copy, output_dtype, output_order, was_copied 

131): 

132 shape = (10, 11) 

133 arr1 = np.ones(shape, dtype=input_dtype, order=input_order) 

134 arr2 = as_ndarray(arr1, copy=copy, dtype=output_dtype, order=output_order) 

135 

136 # check if nd_array copied the original array or not 

137 assert are_arrays_identical(arr1[0], arr2[0]) != was_copied 

138 

139 if output_dtype is None: 

140 assert arr2.dtype == input_dtype 

141 else: 

142 assert arr2.dtype == output_dtype 

143 

144 result_order = output_order if output_order is not None else input_order 

145 

146 if result_order == "F": 

147 assert arr2.flags["F_CONTIGUOUS"] 

148 else: 

149 assert arr2.flags["C_CONTIGUOUS"] 

150 

151 

152def test_as_ndarray_memmap(): 

153 # memmap 

154 filename = Path(__file__).parent / "data" / "mmap.dat" 

155 

156 # same dtype, no copy requested 

157 arr1 = np.memmap(filename, dtype="float32", mode="w+", shape=(5,)) 

158 arr2 = as_ndarray(arr1) 

159 assert not are_arrays_identical(arr1, arr2) 

160 

161 # same dtype, copy requested 

162 arr1 = np.memmap(filename, dtype="float32", mode="readwrite", shape=(5,)) 

163 arr2 = as_ndarray(arr1, copy=True) 

164 assert not are_arrays_identical(arr1, arr2) 

165 

166 # different dtype 

167 arr1 = np.memmap(filename, dtype="float32", mode="readwrite", shape=(5,)) 

168 arr2 = as_ndarray(arr1, dtype=int) 

169 assert arr2.dtype == int 

170 assert not are_arrays_identical(arr1, arr2) 

171 

172 # same dtype, explicitly provided: must copy 

173 arr1 = np.memmap(filename, dtype="float32", mode="readwrite", shape=(5,)) 

174 arr2 = as_ndarray(arr1, dtype=np.float32) 

175 assert arr2.dtype == np.float32 

176 assert not are_arrays_identical(arr1, arr2) 

177 

178 # same dtype, order provided 

179 arr1 = np.memmap( 

180 filename, dtype="float32", mode="readwrite", shape=(10, 10) 

181 ) 

182 arr2 = as_ndarray(arr1, order="F") 

183 assert arr2.flags["F_CONTIGUOUS"] and not arr2.flags["C_CONTIGUOUS"] 

184 assert arr2.dtype == arr1.dtype 

185 assert not are_arrays_identical(arr1[0], arr2[0]) 

186 

187 # same dtype, order unchanged but provided 

188 arr1 = np.memmap( 

189 filename, dtype="float32", mode="readwrite", shape=(10, 10), order="F" 

190 ) 

191 arr2 = as_ndarray(arr1, order="F") 

192 assert arr2.flags["F_CONTIGUOUS"] and not arr2.flags["C_CONTIGUOUS"] 

193 assert arr2.dtype == arr1.dtype 

194 assert not are_arrays_identical(arr1[0], arr2[0]) 

195 

196 # dtype and order specified 

197 arr1 = np.memmap( 

198 filename, dtype="float32", mode="readwrite", shape=(10, 10), order="F" 

199 ) 

200 arr2 = as_ndarray(arr1, order="F", dtype=np.int32) 

201 assert arr2.flags["F_CONTIGUOUS"] and not arr2.flags["C_CONTIGUOUS"] 

202 assert arr2.dtype == np.int32 

203 assert not are_arrays_identical(arr1[0], arr2[0]) 

204 

205 

206def test_as_ndarray_more(): 

207 # list 

208 # same dtype, no copy requested 

209 arr1 = [0, 1, 2, 3] 

210 arr2 = as_ndarray(arr1) 

211 assert not are_arrays_identical(arr1, arr2) 

212 

213 # same dtype, copy requested 

214 arr1 = [0, 1, 2, 3] 

215 arr2 = as_ndarray(arr1, copy=True) 

216 assert not are_arrays_identical(arr1, arr2) 

217 

218 # different dtype 

219 arr1 = [0, 1, 2, 3] 

220 arr2 = as_ndarray(arr1, dtype=float) 

221 assert arr2.dtype == float 

222 assert not are_arrays_identical(arr1, arr2) 

223 

224 # order specified 

225 arr1 = [[0, 1, 2, 3], [0, 1, 2, 3]] 

226 arr2 = as_ndarray(arr1, dtype=float, order="F") 

227 assert arr2.dtype == float 

228 assert arr2.flags["F_CONTIGUOUS"] and not arr2.flags["C_CONTIGUOUS"] 

229 assert not are_arrays_identical(arr1[0], arr2[0]) 

230 

231 # Unhandled cases 

232 with pytest.raises(ValueError): 

233 as_ndarray("test_string") 

234 with pytest.raises(ValueError): 

235 as_ndarray([], order="invalid") 

236 

237 

238def test_csv_to_array(tmp_path): 

239 # Create a phony CSV file 

240 filename = tmp_path / "tmp.csv" 

241 with filename.open(mode="w") as fp: 

242 fp.write("1.,2.,3.,4.,5.\n") 

243 assert np.allclose( 

244 csv_to_array(filename), np.asarray([1.0, 2.0, 3.0, 4.0, 5.0]) 

245 ) 

246 with pytest.raises(TypeError): 

247 csv_to_array(filename, delimiters="?!")