|
| 1 | +""" |
| 2 | +Test the numpy_conversions module |
| 3 | +""" |
| 4 | +import pytest |
| 5 | + |
| 6 | +import numpy as np |
| 7 | +import os |
| 8 | + |
| 9 | +from boyle.utils.numpy_conversions import as_ndarray |
| 10 | + |
| 11 | + |
| 12 | +def are_arrays_identical(arr1, arr2): |
| 13 | + """Check if two 1-dimensional array point to the same buffer. |
| 14 | +
|
| 15 | + The check is performed only on the first value of the arrays. For |
| 16 | + this test to be reliable, arr2 must not point to a subset of arr1. |
| 17 | + For example, if arr2 = arr1[1:] has been executed just before calling |
| 18 | + this function, the test will FAIL, even if the same buffer is used by |
| 19 | + both arrays. arr2 = arr1[:1] will succeed though. |
| 20 | +
|
| 21 | + dtypes are not supposed to be identical. |
| 22 | + """ |
| 23 | + # Modify the first value in arr1 twice, and see if corresponding |
| 24 | + # value in arr2 has changed. Changing the value twice is required, since |
| 25 | + # the original value could be the first value that we use. |
| 26 | + |
| 27 | + orig1 = arr1[0] |
| 28 | + orig2 = arr2[0] |
| 29 | + |
| 30 | + arr1[0] = 0 |
| 31 | + if arr2[0] != orig2: |
| 32 | + arr1[0] = orig1 |
| 33 | + return True |
| 34 | + |
| 35 | + arr1[0] = 1 |
| 36 | + if arr2[0] != orig2: |
| 37 | + arr1[0] = orig1 |
| 38 | + return True |
| 39 | + |
| 40 | + arr1[0] = orig1 |
| 41 | + return False |
| 42 | + |
| 43 | + |
| 44 | +def test_are_array_identical(): |
| 45 | + arr1 = np.ones(4) |
| 46 | + orig1 = arr1.copy() |
| 47 | + |
| 48 | + arr2 = arr1 |
| 49 | + orig2 = arr2.copy() |
| 50 | + |
| 51 | + assert(are_arrays_identical(arr1, arr2)) |
| 52 | + np.testing.assert_array_almost_equal(orig1, arr1, decimal=10) |
| 53 | + np.testing.assert_array_almost_equal(orig2, arr2, decimal=10) |
| 54 | + |
| 55 | + arr2 = arr1[:1] |
| 56 | + orig2 = arr2.copy() |
| 57 | + assert(are_arrays_identical(arr1, arr2)) |
| 58 | + np.testing.assert_array_almost_equal(orig1, arr1, decimal=10) |
| 59 | + np.testing.assert_array_almost_equal(orig2, arr2, decimal=10) |
| 60 | + |
| 61 | + arr2 = arr1[1:] |
| 62 | + orig2 = arr2.copy() |
| 63 | + assert(not are_arrays_identical(arr1, arr2)) |
| 64 | + np.testing.assert_array_almost_equal(orig1, arr1, decimal=10) |
| 65 | + np.testing.assert_array_almost_equal(orig2, arr2, decimal=10) |
| 66 | + |
| 67 | + arr2 = arr1.copy() |
| 68 | + orig2 = arr2.copy() |
| 69 | + assert(not are_arrays_identical(arr1, arr2)) |
| 70 | + np.testing.assert_array_almost_equal(orig1, arr1, decimal=10) |
| 71 | + np.testing.assert_array_almost_equal(orig2, arr2, decimal=10) |
| 72 | + |
| 73 | + |
| 74 | +def test_as_ndarray(): |
| 75 | + # All test cases |
| 76 | + # input dtype, input order, should copy, output dtype, output order, copied |
| 77 | + test_cases = [ |
| 78 | + # no-op |
| 79 | + (np.float, "C", False, None, None, False), |
| 80 | + (np.float, "F", False, None, None, False), |
| 81 | + |
| 82 | + # simple copy |
| 83 | + (np.float, "C", True, None, None, True), |
| 84 | + (np.float, "F", True, None, None, True), |
| 85 | + |
| 86 | + # dtype provided, identical |
| 87 | + (np.float, "C", False, np.float, None, False), |
| 88 | + (np.float, "F", False, np.float, None, False), |
| 89 | + |
| 90 | + # dtype changed |
| 91 | + (np.float, "C", False, np.float32, None, True), |
| 92 | + (np.float, "F", False, np.float32, None, True), |
| 93 | + |
| 94 | + # dtype and order provided, but identical |
| 95 | + (np.float, "C", False, np.float, "C", False), |
| 96 | + (np.float, "F", False, np.float, "F", False), |
| 97 | + |
| 98 | + # order provided, unchanged |
| 99 | + (np.float, "C", False, None, "C", False), |
| 100 | + (np.float, "F", False, None, "F", False), |
| 101 | + (np.float, "C", True, None, "C", True), |
| 102 | + (np.float, "F", True, None, "F", True), |
| 103 | + |
| 104 | + # order provided, changed |
| 105 | + (np.float, "C", False, None, "F", True), |
| 106 | + (np.float, "F", False, None, "C", True), |
| 107 | + (np.float, "C", True, None, "F", True), |
| 108 | + (np.float, "F", True, None, "C", True), |
| 109 | + |
| 110 | + # Special case for int8 <-> bool conversion. |
| 111 | + (np.int8, "C", False, np.bool, None, False), |
| 112 | + (np.int8, "F", False, np.bool, None, False), |
| 113 | + (np.int8, "C", False, np.bool, "C", False), |
| 114 | + (np.int8, "F", False, np.bool, "F", False), |
| 115 | + (np.int8, "C", False, np.bool, "F", True), |
| 116 | + (np.int8, "F", False, np.bool, "C", True), |
| 117 | + |
| 118 | + (np.int8, "C", True, np.bool, None, True), |
| 119 | + (np.int8, "F", True, np.bool, None, True), |
| 120 | + (np.int8, "C", True, np.bool, "C", True), |
| 121 | + (np.int8, "F", True, np.bool, "F", True), |
| 122 | + |
| 123 | + (np.bool, "C", False, np.int8, None, False), |
| 124 | + (np.bool, "F", False, np.int8, None, False), |
| 125 | + (np.bool, "C", False, np.int8, "C", False), |
| 126 | + (np.bool, "F", False, np.int8, "F", False), |
| 127 | + (np.bool, "C", False, np.int8, "F", True), |
| 128 | + (np.bool, "F", False, np.int8, "C", True), |
| 129 | + |
| 130 | + (np.bool, "C", True, np.int8, None, True), |
| 131 | + (np.bool, "F", True, np.int8, None, True), |
| 132 | + (np.bool, "C", True, np.int8, "C", True), |
| 133 | + (np.bool, "F", True, np.int8, "F", True), |
| 134 | + ] |
| 135 | + |
| 136 | + shape = (10, 11) |
| 137 | + for case in test_cases: |
| 138 | + in_dtype, in_order, copy, out_dtype, out_order, copied = case |
| 139 | + arr1 = np.ones(shape, dtype=in_dtype, order=in_order) |
| 140 | + arr2 = as_ndarray(arr1, |
| 141 | + copy=copy, dtype=out_dtype, order=out_order) |
| 142 | + assert (not are_arrays_identical(arr1[0], arr2[0]) == copied), str(case) |
| 143 | + if out_dtype is None: |
| 144 | + assert (arr2.dtype == in_dtype), str(case) |
| 145 | + else: |
| 146 | + assert (arr2.dtype == out_dtype), str(case) |
| 147 | + |
| 148 | + result_order = out_order if out_order is not None else in_order |
| 149 | + if result_order == "F": |
| 150 | + assert (arr2.flags["F_CONTIGUOUS"]), str(case) |
| 151 | + else: |
| 152 | + assert (arr2.flags["C_CONTIGUOUS"]), str(case) |
| 153 | + |
| 154 | + # memmap |
| 155 | + filename = os.path.join(os.path.dirname(__file__), "data", "mmap.dat") |
| 156 | + |
| 157 | + # same dtype, no copy requested |
| 158 | + arr1 = np.memmap(filename, dtype='float32', mode='w+', shape=(5,)) |
| 159 | + arr2 = as_ndarray(arr1) |
| 160 | + assert(not are_arrays_identical(arr1, arr2)) |
| 161 | + |
| 162 | + # same dtype, copy requested |
| 163 | + arr1 = np.memmap(filename, dtype='float32', mode='readwrite', shape=(5,)) |
| 164 | + arr2 = as_ndarray(arr1, copy=True) |
| 165 | + assert(not are_arrays_identical(arr1, arr2)) |
| 166 | + |
| 167 | + # different dtype |
| 168 | + arr1 = np.memmap(filename, dtype='float32', mode='readwrite', shape=(5,)) |
| 169 | + arr2 = as_ndarray(arr1, dtype=np.int) |
| 170 | + assert(arr2.dtype == np.int) |
| 171 | + assert(not are_arrays_identical(arr1, arr2)) |
| 172 | + |
| 173 | + # same dtype, explicitly provided: must copy |
| 174 | + arr1 = np.memmap(filename, dtype='float32', mode='readwrite', shape=(5,)) |
| 175 | + arr2 = as_ndarray(arr1, dtype=np.float32) |
| 176 | + assert(arr2.dtype == np.float32) |
| 177 | + assert(not are_arrays_identical(arr1, arr2)) |
| 178 | + |
| 179 | + # same dtype, order provided |
| 180 | + arr1 = np.memmap(filename, dtype='float32', mode='readwrite', shape=(10, 10)) |
| 181 | + arr2 = as_ndarray(arr1, order="F") |
| 182 | + assert(arr2.flags["F_CONTIGUOUS"] and not arr2.flags["C_CONTIGUOUS"]) |
| 183 | + assert(arr2.dtype == arr1.dtype) |
| 184 | + assert(not are_arrays_identical(arr1[0], arr2[0])) |
| 185 | + |
| 186 | + # same dtype, order unchanged but provided |
| 187 | + arr1 = np.memmap(filename, dtype='float32', mode='readwrite', |
| 188 | + shape=(10, 10), order="F") |
| 189 | + arr2 = as_ndarray(arr1, order="F") |
| 190 | + assert(arr2.flags["F_CONTIGUOUS"] and not arr2.flags["C_CONTIGUOUS"]) |
| 191 | + assert(arr2.dtype == arr1.dtype) |
| 192 | + assert(not are_arrays_identical(arr1[0], arr2[0])) |
| 193 | + |
| 194 | + # dtype and order specified |
| 195 | + arr1 = np.memmap(filename, dtype='float32', mode='readwrite', |
| 196 | + shape=(10, 10), order="F") |
| 197 | + arr2 = as_ndarray(arr1, order="F", dtype=np.int32) |
| 198 | + assert(arr2.flags["F_CONTIGUOUS"] and not arr2.flags["C_CONTIGUOUS"]) |
| 199 | + assert(arr2.dtype == np.int32) |
| 200 | + assert(not are_arrays_identical(arr1[0], arr2[0])) |
| 201 | + |
| 202 | + # list |
| 203 | + # same dtype, no copy requested |
| 204 | + arr1 = [0, 1, 2, 3] |
| 205 | + arr2 = as_ndarray(arr1) |
| 206 | + assert(not are_arrays_identical(arr1, arr2)) |
| 207 | + |
| 208 | + # same dtype, copy requested |
| 209 | + arr1 = [0, 1, 2, 3] |
| 210 | + arr2 = as_ndarray(arr1, copy=True) |
| 211 | + assert(not are_arrays_identical(arr1, arr2)) |
| 212 | + |
| 213 | + # different dtype |
| 214 | + arr1 = [0, 1, 2, 3] |
| 215 | + arr2 = as_ndarray(arr1, dtype=np.float) |
| 216 | + assert(arr2.dtype == np.float) |
| 217 | + assert(not are_arrays_identical(arr1, arr2)) |
| 218 | + |
| 219 | + # order specified |
| 220 | + arr1 = [[0, 1, 2, 3], [0, 1, 2, 3]] |
| 221 | + arr2 = as_ndarray(arr1, dtype=np.float, order="F") |
| 222 | + assert(arr2.dtype == np.float) |
| 223 | + assert(arr2.flags["F_CONTIGUOUS"] and not arr2.flags["C_CONTIGUOUS"]) |
| 224 | + assert(not are_arrays_identical(arr1[0], arr2[0])) |
| 225 | + |
| 226 | + # Unhandled cases |
| 227 | + pytest.raises(ValueError, as_ndarray, "test string") |
| 228 | + pytest.raises(ValueError, as_ndarray, [], order="invalid") |
| 229 | + |
0 commit comments