Richard Burton | dc0c6ed | 2020-04-08 16:39:05 +0100 | [diff] [blame] | 1 | # Copyright © 2020 Arm Ltd. All rights reserved. |
| 2 | # SPDX-License-Identifier: MIT |
| 3 | import pytest |
| 4 | import numpy as np |
| 5 | |
| 6 | import pyarmnn as ann |
| 7 | |
| 8 | |
| 9 | def _get_tensor_info(dt): |
| 10 | tensor_info = ann.TensorInfo(ann.TensorShape((2, 3)), dt) |
| 11 | |
| 12 | return tensor_info |
| 13 | |
| 14 | |
| 15 | @pytest.mark.parametrize("dt, data", |
| 16 | [ |
| 17 | (ann.DataType_Float32, np.random.randint(1, size=(2, 4)).astype(np.float32)), |
| 18 | (ann.DataType_Float16, np.random.randint(1, size=(2, 4)).astype(np.float16)), |
| 19 | (ann.DataType_QAsymmU8, np.random.randint(1, size=(2, 4)).astype(np.uint8)), |
| 20 | (ann.DataType_QAsymmS8, np.random.randint(1, size=(2, 4)).astype(np.int8)), |
| 21 | (ann.DataType_QSymmS8, np.random.randint(1, size=(2, 4)).astype(np.int8)), |
| 22 | (ann.DataType_Signed32, np.random.randint(1, size=(2, 4)).astype(np.int32)), |
| 23 | (ann.DataType_QSymmS16, np.random.randint(1, size=(2, 4)).astype(np.int16)) |
| 24 | ], ids=['float32', 'float16', 'unsigned int8', 'signed int8', 'signed int8', 'int32', 'int16']) |
| 25 | def test_const_tensor_too_many_elements(dt, data): |
| 26 | tensor_info = _get_tensor_info(dt) |
| 27 | num_bytes = tensor_info.GetNumBytes() |
| 28 | |
| 29 | with pytest.raises(ValueError) as err: |
| 30 | ann.ConstTensor(tensor_info, data) |
| 31 | |
| 32 | assert 'ConstTensor requires {} bytes, {} provided.'.format(num_bytes, data.nbytes) in str(err.value) |
| 33 | |
| 34 | |
| 35 | @pytest.mark.parametrize("dt, data", |
| 36 | [ |
| 37 | (ann.DataType_Float32, np.random.randint(1, size=(2, 2)).astype(np.float32)), |
| 38 | (ann.DataType_Float16, np.random.randint(1, size=(2, 2)).astype(np.float16)), |
| 39 | (ann.DataType_QAsymmU8, np.random.randint(1, size=(2, 2)).astype(np.uint8)), |
| 40 | (ann.DataType_QAsymmS8, np.random.randint(1, size=(2, 2)).astype(np.int8)), |
| 41 | (ann.DataType_QSymmS8, np.random.randint(1, size=(2, 2)).astype(np.int8)), |
| 42 | (ann.DataType_Signed32, np.random.randint(1, size=(2, 2)).astype(np.int32)), |
| 43 | (ann.DataType_QSymmS16, np.random.randint(1, size=(2, 2)).astype(np.int16)) |
| 44 | ], ids=['float32', 'float16', 'unsigned int8', 'signed int8', 'signed int8', 'int32', 'int16']) |
| 45 | def test_const_tensor_too_little_elements(dt, data): |
| 46 | tensor_info = _get_tensor_info(dt) |
| 47 | num_bytes = tensor_info.GetNumBytes() |
| 48 | |
| 49 | with pytest.raises(ValueError) as err: |
| 50 | ann.ConstTensor(tensor_info, data) |
| 51 | |
| 52 | assert 'ConstTensor requires {} bytes, {} provided.'.format(num_bytes, data.nbytes) in str(err.value) |
| 53 | |
| 54 | |
| 55 | @pytest.mark.parametrize("dt, data", |
| 56 | [ |
| 57 | (ann.DataType_Float32, np.random.randint(1, size=(2, 2, 3, 3)).astype(np.float32)), |
| 58 | (ann.DataType_Float16, np.random.randint(1, size=(2, 2, 3, 3)).astype(np.float16)), |
| 59 | (ann.DataType_QAsymmU8, np.random.randint(1, size=(2, 2, 3, 3)).astype(np.uint8)), |
| 60 | (ann.DataType_QAsymmS8, np.random.randint(1, size=(2, 2, 3, 3)).astype(np.int8)), |
| 61 | (ann.DataType_QSymmS8, np.random.randint(1, size=(2, 2, 3, 3)).astype(np.int8)), |
| 62 | (ann.DataType_Signed32, np.random.randint(1, size=(2, 2, 3, 3)).astype(np.int32)), |
| 63 | (ann.DataType_QSymmS16, np.random.randint(1, size=(2, 2, 3, 3)).astype(np.int16)) |
| 64 | ], ids=['float32', 'float16', 'unsigned int8', 'signed int8', 'signed int8', 'int32', 'int16']) |
| 65 | def test_const_tensor_multi_dimensional_input(dt, data): |
| 66 | tensor = ann.ConstTensor(ann.TensorInfo(ann.TensorShape((2, 2, 3, 3)), dt), data) |
| 67 | |
| 68 | assert data.size == tensor.GetNumElements() |
| 69 | assert data.nbytes == tensor.GetNumBytes() |
| 70 | assert dt == tensor.GetDataType() |
| 71 | assert tensor.get_memory_area().data |
| 72 | |
| 73 | |
| 74 | def test_create_const_tensor_from_tensor(): |
| 75 | tensor_info = ann.TensorInfo(ann.TensorShape((2, 3)), ann.DataType_Float32) |
| 76 | tensor = ann.Tensor(tensor_info) |
| 77 | copied_tensor = ann.ConstTensor(tensor) |
| 78 | |
| 79 | assert copied_tensor != tensor, "Different objects" |
| 80 | assert copied_tensor.GetInfo() != tensor.GetInfo(), "Different objects" |
| 81 | assert copied_tensor.get_memory_area().ctypes.data == tensor.get_memory_area().ctypes.data, "Same memory area" |
| 82 | assert copied_tensor.GetNumElements() == tensor.GetNumElements() |
| 83 | assert copied_tensor.GetNumBytes() == tensor.GetNumBytes() |
| 84 | assert copied_tensor.GetDataType() == tensor.GetDataType() |
| 85 | |
| 86 | |
| 87 | def test_const_tensor_from_tensor_has_memory_area_access_after_deletion_of_original_tensor(): |
| 88 | tensor_info = ann.TensorInfo(ann.TensorShape((2, 3)), ann.DataType_Float32) |
| 89 | tensor = ann.Tensor(tensor_info) |
| 90 | |
| 91 | tensor.get_memory_area()[0] = 100 |
| 92 | |
| 93 | copied_mem = tensor.get_memory_area().copy() |
| 94 | |
| 95 | assert 100 == copied_mem[0], "Memory was copied correctly" |
| 96 | |
| 97 | copied_tensor = ann.ConstTensor(tensor) |
| 98 | |
| 99 | tensor.get_memory_area()[0] = 200 |
| 100 | |
| 101 | assert 200 == tensor.get_memory_area()[0], "Tensor and copied Tensor point to the same memory" |
| 102 | assert 200 == copied_tensor.get_memory_area()[0], "Tensor and copied Tensor point to the same memory" |
| 103 | |
| 104 | assert 100 == copied_mem[0], "Copied test memory not affected" |
| 105 | |
| 106 | copied_mem[0] = 200 # modify test memory to equal copied Tensor |
| 107 | |
| 108 | del tensor |
| 109 | np.testing.assert_array_equal(copied_tensor.get_memory_area(), copied_mem), "After initial tensor was deleted, " \ |
| 110 | "copied Tensor still has " \ |
| 111 | "its memory as expected" |
| 112 | |
| 113 | |
| 114 | def test_create_const_tensor_incorrect_args(): |
| 115 | with pytest.raises(ValueError) as err: |
| 116 | ann.ConstTensor('something', 'something') |
| 117 | |
| 118 | expected_error_message = "Incorrect number of arguments or type of arguments provided to create Const Tensor." |
| 119 | assert expected_error_message in str(err.value) |
| 120 | |
| 121 | |
| 122 | @pytest.mark.parametrize("dt, data", |
| 123 | [ |
| 124 | # -1 not in data type enum |
| 125 | (-1, np.random.randint(1, size=(2, 3)).astype(np.float32)), |
| 126 | ], ids=['unknown']) |
| 127 | def test_const_tensor_unsupported_datatype(dt, data): |
| 128 | tensor_info = _get_tensor_info(dt) |
| 129 | |
| 130 | with pytest.raises(ValueError) as err: |
| 131 | ann.ConstTensor(tensor_info, data) |
| 132 | |
| 133 | assert 'The data type provided for this Tensor is not supported: -1' in str(err.value) |
| 134 | |
| 135 | |
| 136 | @pytest.mark.parametrize("dt, data", |
| 137 | [ |
| 138 | (ann.DataType_Float32, [[1, 1, 1], [1, 1, 1]]), |
| 139 | (ann.DataType_Float16, [[1, 1, 1], [1, 1, 1]]), |
| 140 | (ann.DataType_QAsymmU8, [[1, 1, 1], [1, 1, 1]]), |
| 141 | (ann.DataType_QAsymmS8, [[1, 1, 1], [1, 1, 1]]), |
| 142 | (ann.DataType_QSymmS8, [[1, 1, 1], [1, 1, 1]]) |
| 143 | ], ids=['float32', 'float16', 'unsigned int8', 'signed int8', 'signed int8']) |
| 144 | def test_const_tensor_incorrect_input_datatype(dt, data): |
| 145 | tensor_info = _get_tensor_info(dt) |
| 146 | |
| 147 | with pytest.raises(TypeError) as err: |
| 148 | ann.ConstTensor(tensor_info, data) |
| 149 | |
| 150 | assert 'Data must be provided as a numpy array.' in str(err.value) |
| 151 | |
| 152 | |
| 153 | @pytest.mark.parametrize("dt, data", |
| 154 | [ |
| 155 | (ann.DataType_Float32, np.random.randint(1, size=(2, 3)).astype(np.float32)), |
| 156 | (ann.DataType_Float16, np.random.randint(1, size=(2, 3)).astype(np.float16)), |
| 157 | (ann.DataType_QAsymmU8, np.random.randint(1, size=(2, 3)).astype(np.uint8)), |
| 158 | (ann.DataType_QAsymmS8, np.random.randint(1, size=(2, 3)).astype(np.int8)), |
| 159 | (ann.DataType_QSymmS8, np.random.randint(1, size=(2, 3)).astype(np.int8)), |
| 160 | (ann.DataType_Signed32, np.random.randint(1, size=(2, 3)).astype(np.int32)), |
| 161 | (ann.DataType_QSymmS16, np.random.randint(1, size=(2, 3)).astype(np.int16)) |
| 162 | ], ids=['float32', 'float16', 'unsigned int8', 'signed int8', 'signed int8', 'int32', 'int16']) |
| 163 | class TestNumpyDataTypes: |
| 164 | |
| 165 | def test_copy_const_tensor(self, dt, data): |
| 166 | tensor_info = _get_tensor_info(dt) |
| 167 | tensor = ann.ConstTensor(tensor_info, data) |
| 168 | copied_tensor = ann.ConstTensor(tensor) |
| 169 | |
| 170 | assert copied_tensor != tensor, "Different objects" |
| 171 | assert copied_tensor.GetInfo() != tensor.GetInfo(), "Different objects" |
| 172 | assert copied_tensor.get_memory_area().ctypes.data == tensor.get_memory_area().ctypes.data, "Same memory area" |
| 173 | assert copied_tensor.GetNumElements() == tensor.GetNumElements() |
| 174 | assert copied_tensor.GetNumBytes() == tensor.GetNumBytes() |
| 175 | assert copied_tensor.GetDataType() == tensor.GetDataType() |
| 176 | |
| 177 | def test_const_tensor__str__(self, dt, data): |
| 178 | tensor_info = _get_tensor_info(dt) |
| 179 | d_type = tensor_info.GetDataType() |
| 180 | num_dimensions = tensor_info.GetNumDimensions() |
| 181 | num_bytes = tensor_info.GetNumBytes() |
| 182 | num_elements = tensor_info.GetNumElements() |
| 183 | tensor = ann.ConstTensor(tensor_info, data) |
| 184 | |
| 185 | assert str(tensor) == "ConstTensor{{DataType: {}, NumBytes: {}, NumDimensions: " \ |
| 186 | "{}, NumElements: {}}}".format(d_type, num_bytes, num_dimensions, num_elements) |
| 187 | |
| 188 | def test_const_tensor_with_info(self, dt, data): |
| 189 | tensor_info = _get_tensor_info(dt) |
| 190 | elements = tensor_info.GetNumElements() |
| 191 | num_bytes = tensor_info.GetNumBytes() |
| 192 | d_type = dt |
| 193 | |
| 194 | tensor = ann.ConstTensor(tensor_info, data) |
| 195 | |
| 196 | assert tensor_info != tensor.GetInfo(), "Different objects" |
| 197 | assert elements == tensor.GetNumElements() |
| 198 | assert num_bytes == tensor.GetNumBytes() |
| 199 | assert d_type == tensor.GetDataType() |
| 200 | |
| 201 | def test_immutable_memory(self, dt, data): |
| 202 | tensor_info = _get_tensor_info(dt) |
| 203 | |
| 204 | tensor = ann.ConstTensor(tensor_info, data) |
| 205 | |
| 206 | with pytest.raises(ValueError) as err: |
| 207 | tensor.get_memory_area()[0] = 0 |
| 208 | |
| 209 | assert 'is read-only' in str(err.value) |
| 210 | |
| 211 | def test_numpy_dtype_matches_ann_dtype(self, dt, data): |
| 212 | np_data_type_mapping = {ann.DataType_QAsymmU8: np.uint8, |
| 213 | ann.DataType_QAsymmS8: np.int8, |
| 214 | ann.DataType_QSymmS8: np.int8, |
| 215 | ann.DataType_Float32: np.float32, |
| 216 | ann.DataType_QSymmS16: np.int16, |
| 217 | ann.DataType_Signed32: np.int32, |
| 218 | ann.DataType_Float16: np.float16} |
| 219 | |
| 220 | tensor_info = _get_tensor_info(dt) |
| 221 | tensor = ann.ConstTensor(tensor_info, data) |
| 222 | assert np_data_type_mapping[tensor.GetDataType()] == data.dtype |
| 223 | |
| 224 | |
| 225 | # This test checks that mismatched numpy and PyArmNN datatypes with same number of bits raises correct error. |
| 226 | @pytest.mark.parametrize("dt, data", |
| 227 | [ |
| 228 | (ann.DataType_Float32, np.random.randint(1, size=(2, 3)).astype(np.int32)), |
| 229 | (ann.DataType_Float16, np.random.randint(1, size=(2, 3)).astype(np.int16)), |
| 230 | (ann.DataType_QAsymmU8, np.random.randint(1, size=(2, 3)).astype(np.int8)), |
| 231 | (ann.DataType_QAsymmS8, np.random.randint(1, size=(2, 3)).astype(np.uint8)), |
| 232 | (ann.DataType_QSymmS8, np.random.randint(1, size=(2, 3)).astype(np.uint8)), |
| 233 | (ann.DataType_Signed32, np.random.randint(1, size=(2, 3)).astype(np.float32)), |
| 234 | (ann.DataType_QSymmS16, np.random.randint(1, size=(2, 3)).astype(np.float16)) |
| 235 | ], ids=['float32', 'float16', 'unsigned int8', 'signed int8', 'signed int8', 'int32', 'int16']) |
| 236 | def test_numpy_dtype_mismatch_ann_dtype(dt, data): |
| 237 | np_data_type_mapping = {ann.DataType_QAsymmU8: np.uint8, |
| 238 | ann.DataType_QAsymmS8: np.int8, |
| 239 | ann.DataType_QSymmS8: np.int8, |
| 240 | ann.DataType_Float32: np.float32, |
| 241 | ann.DataType_QSymmS16: np.int16, |
| 242 | ann.DataType_Signed32: np.int32, |
| 243 | ann.DataType_Float16: np.float16} |
| 244 | |
| 245 | tensor_info = _get_tensor_info(dt) |
| 246 | with pytest.raises(TypeError) as err: |
| 247 | ann.ConstTensor(tensor_info, data) |
| 248 | |
| 249 | assert str(err.value) == "Expected data to have type {} for type {} but instead got numpy.{}".format( |
| 250 | np_data_type_mapping[dt], dt, data.dtype) |
| 251 | |