blob: 665ebc2c316826e2e14d6b8f1edfa3c22eb147a5 [file] [log] [blame]
Louis Verhaardfa2f92a2020-09-21 11:56:18 +02001# Copyright (C) 2020 Arm Limited or its affiliates. All rights reserved.
2#
3# SPDX-License-Identifier: Apache-2.0
4#
5# Licensed under the Apache License, Version 2.0 (the License); you may
6# not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an AS IS BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17# Description:
18# Unit tests for support_operators
Michael McGeagh37ded342020-10-01 15:37:44 +010019import numpy as np
20
Louis Verhaardfa2f92a2020-09-21 11:56:18 +020021from ethosu.vela.data_type import DataType
Louis Verhaardaee5d752020-09-30 09:01:52 +020022from ethosu.vela.operation import Op
Louis Verhaardfa2f92a2020-09-21 11:56:18 +020023from ethosu.vela.supported_operators import SupportedOperators
24from ethosu.vela.tensor import create_const_tensor
Michael McGeagh37ded342020-10-01 15:37:44 +010025from ethosu.vela.tensor import QuantizationParameters
Louis Verhaardfa2f92a2020-09-21 11:56:18 +020026from ethosu.vela.tensor import Tensor
27from ethosu.vela.test import testutil
28
29support = SupportedOperators()
30
31
32def create_strided_slice_op(in_shape, out_shape, start_offsets, end_offsets):
Dwight Lidman8359a472020-09-28 15:53:40 +020033 qp = QuantizationParameters()
Louis Verhaardfa2f92a2020-09-21 11:56:18 +020034 in0 = Tensor(in_shape, DataType.uint8, "in")
Dwight Lidman8359a472020-09-28 15:53:40 +020035 in0.quantization = qp
36 in1 = create_const_tensor("begin", [len(start_offsets)], DataType.uint8, start_offsets, quantization=qp)
37 in2 = create_const_tensor("end", [len(end_offsets)], DataType.uint8, end_offsets, quantization=qp)
38 in3 = create_const_tensor("strides", [len(end_offsets)], DataType.uint8, len(end_offsets) * [1], quantization=qp)
Louis Verhaardfa2f92a2020-09-21 11:56:18 +020039 out = Tensor(out_shape, DataType.uint8, "out")
Dwight Lidman8359a472020-09-28 15:53:40 +020040 out.quantization = qp
Louis Verhaardfa2f92a2020-09-21 11:56:18 +020041 attrs = {"ellipsis_mask": 0, "new_axis_mask": 0, "shrink_axis_mask": 0, "begin_mask": 0, "end_mask": 0}
Louis Verhaardaee5d752020-09-30 09:01:52 +020042 return testutil.create_op(Op.StridedSlice, [in0, in1, in2, in3], out, attrs=attrs)
Louis Verhaardfa2f92a2020-09-21 11:56:18 +020043
44
45def create_strided_slice():
46 # Creates a valid strided slice operator with some valid inputs/outputs
47 op = create_strided_slice_op([1, 10, 10, 10], [1, 5, 5, 10], [127, 2, 2, 0], [0, 7, -3, 0])
48 op.attrs["begin_mask"] = 1
49 op.attrs["end_mask"] = 9
50 assert support.is_operator_supported(op)
51 return op
52
53
54def test_strided_slice():
55 # Tests support for StridedSlice operator
56 op = create_strided_slice()
57 # Setting one of new_axis_mask/shrink_axis_mask to non-zero is ok
58 op.attrs["new_axis_mask"] = 2
59 assert support.is_operator_supported(op)
60 op = create_strided_slice()
61 op.attrs["shrink_axis_mask"] = 3
62 assert support.is_operator_supported(op)
63 # But setting both to non-zero is not supported
64 op.attrs["new_axis_mask"] = 2
65 assert not support.is_operator_supported(op)
66 # begin values must not be None
67 op.inputs[1].values = None
68 assert not support.is_operator_supported(op)
69 # Unsupported strides
70 op = create_strided_slice()
71 op.inputs[3].values = [1, 1, 2, 1]
72 assert not support.is_operator_supported(op)
73 # Wrong number of input tensors
74 op = create_strided_slice()
75 op.add_input_tensor(op.inputs[0].clone())
76 assert not support.is_operator_supported(op)
77 # Unsupported ellipsis mask
78 op = create_strided_slice()
79 op.attrs["ellipsis_mask"] = 1
80 assert not support.is_operator_supported(op)
81 # Examples where end offset <= begin offset
82 op = create_strided_slice()
83 op.inputs[1].values = [0, 7, 2, 0]
84 assert not support.is_operator_supported(op)
85 op = create_strided_slice()
86 op.inputs[2].values = [0, 7, 2, 0]
87 assert not support.is_operator_supported(op)
88 op = create_strided_slice()
89 op.attrs["begin_mask"] = 0
90 assert not support.is_operator_supported(op)
91 op = create_strided_slice()
92 op.attrs["end_mask"] = 0
93 assert not support.is_operator_supported(op)
Michael McGeagh37ded342020-10-01 15:37:44 +010094
95
96def test_constraint_tens_defined_shape():
97 # Tensors cannot have None in them
Michael McGeagh1f951fc2020-10-14 09:30:02 +010098 op = testutil.create_op_with_quant_tensors(Op.Relu, [1, 8, None, 8], [1, 8, 8, 8])
Michael McGeagh37ded342020-10-01 15:37:44 +010099 assert not support.is_operator_supported(op)
100
101
Michael McGeagh184b2502020-10-09 17:19:52 +0100102def test_constraint_tens_output_shapeless():
Michael McGeagh37ded342020-10-01 15:37:44 +0100103 # Shapeless output is not allowed at all:
Michael McGeagh1f951fc2020-10-14 09:30:02 +0100104 op = testutil.create_elemwise_op(Op.Mul, "op", [1, 8, 8, 8], [1, 8, 8, 8], [])
Michael McGeagh37ded342020-10-01 15:37:44 +0100105 assert not support.is_operator_supported(op)
Michael McGeagh184b2502020-10-09 17:19:52 +0100106
107
108def test_constraint_tens_input_shapeless():
109 # Shapeless input is allowed if its of a certain type:
Michael McGeagh1f951fc2020-10-14 09:30:02 +0100110 op = testutil.create_elemwise_op(Op.Mul, "op", [1, 8, 8, 8], [], [1, 8, 8, 8])
Michael McGeagh184b2502020-10-09 17:19:52 +0100111 assert support.is_operator_supported(op)
Michael McGeagh37ded342020-10-01 15:37:44 +0100112 # Invalid shapeless input due to op type:
Michael McGeagh1f951fc2020-10-14 09:30:02 +0100113 op = testutil.create_op_with_quant_tensors(Op.Relu, [], [1, 8, 8, 8])
Michael McGeagh37ded342020-10-01 15:37:44 +0100114 assert not support.is_operator_supported(op)
115
116
117def test_constraint_tens_shape_size():
118 # Tensors cannot be > 4D
Michael McGeagh1f951fc2020-10-14 09:30:02 +0100119 op = testutil.create_op_with_quant_tensors(Op.Relu, [1, 1, 8, 8, 8], [1, 1, 8, 8, 8])
Michael McGeagh37ded342020-10-01 15:37:44 +0100120 assert not support.is_operator_supported(op)
121
122
123def test_constraint_tens_dtype():
Michael McGeagh184b2502020-10-09 17:19:52 +0100124 # Tensors can only be of type uint8, int8, int16 and int32
Michael McGeagh1f951fc2020-10-14 09:30:02 +0100125 op = testutil.create_op_with_quant_tensors(Op.Relu, [1, 8, 8, 8], [1, 8, 8, 8], datatype=DataType.float32)
Michael McGeagh37ded342020-10-01 15:37:44 +0100126 assert not support.is_operator_supported(op)
Michael McGeagh184b2502020-10-09 17:19:52 +0100127
128
129def test_constraint_tens_int32_ops():
Michael McGeagh37ded342020-10-01 15:37:44 +0100130 # For int32, only select op types are allowed:
Michael McGeagh1f951fc2020-10-14 09:30:02 +0100131 op = testutil.create_elemwise_op(Op.Mul, "op", [1, 8, 8, 8], [], [1, 8, 8, 8], datatype=DataType.int32)
Michael McGeagh37ded342020-10-01 15:37:44 +0100132 assert support.is_operator_supported(op)
Michael McGeagh1f951fc2020-10-14 09:30:02 +0100133 op = testutil.create_op_with_quant_tensors(Op.Relu, [1, 8, 8, 8], [1, 8, 8, 8], datatype=DataType.int32)
Michael McGeagh37ded342020-10-01 15:37:44 +0100134 assert not support.is_operator_supported(op)
135
136
137def test_constraint_tens_dimension():
138 # Tensors can only have values in the inclusive range of 1-65535
Michael McGeagh1f951fc2020-10-14 09:30:02 +0100139 op = testutil.create_op_with_quant_tensors(Op.Relu, [1, 8, 8, 0], [1, 8, 8, 65536])
Michael McGeagh37ded342020-10-01 15:37:44 +0100140 assert not support.is_operator_supported(op)
141
142
Michael McGeagh184b2502020-10-09 17:19:52 +0100143def test_constraint_tens_quant_none_check():
144 # Tensors must have quantization parameters
Michael McGeagh1f951fc2020-10-14 09:30:02 +0100145 op = testutil.create_elemwise_op(Op.Mul, "op", [1, 8, 8, 8], [], [1, 8, 8, 8], ifm2_quant=None)
Michael McGeagh184b2502020-10-09 17:19:52 +0100146 assert not support.is_operator_supported(op)
147
148
149def test_constraint_tens_quant_scale():
150 # Quantization scale cannot be infinit
151 qp = QuantizationParameters()
152 qp.scale_f32 = np.inf
Michael McGeagh1f951fc2020-10-14 09:30:02 +0100153 op = testutil.create_elemwise_op(Op.Mul, "op", [1, 8, 8, 8], [], [1, 8, 8, 8], ifm_quant=qp)
Michael McGeagh184b2502020-10-09 17:19:52 +0100154 assert not support.is_operator_supported(op)
155
156
Michael McGeagh37ded342020-10-01 15:37:44 +0100157def test_constraint_faf():
158 # Fused activation functions, if set, must be a valid op type
Michael McGeagh1f951fc2020-10-14 09:30:02 +0100159 op = testutil.create_op_with_quant_tensors(Op.Relu, [1, 8, 8, 8], [1, 8, 8, 8])
Louis Verhaardaee5d752020-09-30 09:01:52 +0200160 op.activation = Op.Conv2D
Michael McGeagh37ded342020-10-01 15:37:44 +0100161 assert not support.is_operator_supported(op)
Michael McGeagh1f951fc2020-10-14 09:30:02 +0100162
163
164def test_constraint_conv_pass():
165 # First test a simple conv passes
166 op = testutil.create_op_with_quant_tensors(Op.Conv2D, [1, 1, 1, 1], [1, 1, 1, 1], weights_shape=[1, 1, 1, 1])
167 op.attrs = {"stride_w": 1, "stride_h": 1}
168 assert support.is_operator_supported(op)
169
170
171def test_constraint_stride_type():
172 # Stride width and height must be integer types
173 op = testutil.create_op_with_quant_tensors(Op.Conv2D, [1, 8, 8, 8], [1, 8, 8, 8])
174 op.attrs = {"stride_w": 1.5, "stride_h": "1"}
175 assert not support.is_operator_supported(op)
176
177
178def test_constraint_stride_range():
179 # Stride width and height must lie within a certain range
180 op = testutil.create_op_with_quant_tensors(Op.Conv2D, [1, 8, 8, 8], [1, 8, 8, 8])
181 op.attrs = {"stride_w": 0, "stride_h": 20}
182 assert not support.is_operator_supported(op)
183
184
185def test_constraint_dilation_type():
186 # Dilation width and height must be integer types
187 op = testutil.create_op_with_quant_tensors(Op.Conv2D, [1, 8, 8, 8], [1, 8, 8, 8])
188 op.attrs = {"stride_w": 1, "stride_h": 1, "dilation_w_factor": 1.5, "dilation_h_factor": "1"}
189 assert not support.is_operator_supported(op)
190
191
192def test_constraint_dilation_range():
193 # Dilation width and height must lie within a certain range
194 op = testutil.create_op_with_quant_tensors(Op.Conv2D, [1, 8, 8, 8], [1, 8, 8, 8])
195 op.attrs = {"stride_w": 1, "stride_h": 1, "dilation_w_factor": 0, "dilation_h_factor": 20}
196 assert not support.is_operator_supported(op)
197
198
199def test_constraint_dilated_height_range():
200 # Dilated kernel height must lie within a certain range
201 op = testutil.create_op_with_quant_tensors(Op.Conv2D, [1, 8, 8, 8], [1, 8, 8, 8], weights_shape=[65, 64, 1, 1])
202 op.attrs = {"stride_w": 1, "stride_h": 1}
203 assert not support.is_operator_supported(op)
204
205
206def test_constraint_dilated_product_range():
207 # Dilated kernel width x height must lie within a certain range
208 op = testutil.create_op_with_quant_tensors(Op.Conv2D, [1, 8, 8, 8], [1, 8, 8, 8], weights_shape=[64, 65, 1, 1])
209 op.attrs = {"stride_w": 1, "stride_h": 1}
210 assert not support.is_operator_supported(op)
211
212
213def test_constraint_weights_type():
214 # Weight tensor must be 8-bit
215 op = testutil.create_op_with_quant_tensors(
216 Op.Conv2D, [1, 8, 8, 8], [1, 8, 8, 8], weights_shape=[1, 1, 1, 1], datatype=DataType.int16
217 )
218 op.attrs = {"stride_w": 1, "stride_h": 1}
219 assert not support.is_operator_supported(op)
220
221
222def test_constraint_weights_nonconst():
223 # Weight tensor cannot be non-const tensors
224 op = testutil.create_op_with_quant_tensors(Op.Conv2D, [1, 8, 8, 8], [1, 8, 8, 8])
225 op.attrs = {"stride_w": 1, "stride_h": 1}
226 weights = Tensor([64, 64, 1, 1], DataType.uint8, "weights")
227 weights.quantization = QuantizationParameters()
228 op.add_input_tensor(weights)
229 assert not support.is_operator_supported(op)
230
231
232def test_constraint_weights_limit():
233 # Sum of weights has a limit
234 op = testutil.create_op_with_quant_tensors(Op.Conv2D, [1, 8, 8, 8], [1, 8, 8, 8], weights_shape=[1, 1, 1, 1])
235 op.attrs = {"stride_w": 1, "stride_h": 1}
236 op.weights.quantization.zero_point = np.array([[[[(127 * 65536) + 1]]]])
237 assert not support.is_operator_supported(op)
238
239
240def test_constraint_bias_type():
241 # Bias must have a certain datatype
242 op = testutil.create_op_with_quant_tensors(Op.Conv2DBias, [1, 8, 8, 8], [1, 8, 8, 8], weights_shape=[1, 1, 1, 1])
243 op.attrs = {"stride_w": 1, "stride_h": 1}
244 bias = Tensor([1, 8, 8, 8], DataType.uint8, "bias")
245 op.add_input_tensor(bias)
246 assert not support.is_operator_supported(op)
247
248
249def test_constraint_bias_40bit():
250 # Bias must not exceed 40-bit
251 op = testutil.create_op_with_quant_tensors(Op.Conv2DBias, [1, 1, 1, 1], [1, 1, 1, 1], weights_shape=[1, 1, 1, 1])
252 op.attrs = {"stride_w": 1, "stride_h": 1}
253 bias = Tensor([1, 1, 1, 1], DataType.int64, "bias")
254 bias.quant_values = np.array([0x1FF_FFFF_FFFF])
255 op.add_input_tensor(bias)
256 assert not support.is_operator_supported(op)
257
258
259def test_constraint_batch_size():
260 op = testutil.create_op_with_quant_tensors(Op.Conv2D, [2, 8, 8, 8], [1, 8, 8, 8], weights_shape=[1, 1, 1, 1])
261 op.attrs = {"stride_w": 1, "stride_h": 1}
262 assert not support.is_operator_supported(op)