2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "kernels/Conv2D.h"
18 #include "kernels/TestUtils.h"
20 namespace luci_interpreter
27 using namespace testing;
29 TEST(Conv2DTest, Float)
31 Shape input_shape{1, 4, 3, 2};
32 Shape filter_shape{2, 2, 2, 2};
34 std::vector<float> input_data{
35 1, 2, 3, 4, 5, 6, // row = 0
36 7, 8, 9, 10, 11, 12, // row = 1
37 13, 14, 15, 16, 17, 18, // row = 2
38 19, 20, 21, 22, 23, 24, // row = 3
40 std::vector<float> filter_data{
41 1, 2, -3, -4, // out = 0, row = 0
42 -5, 6, -7, 8, // out = 1, row = 0
43 4, -2, 3, -1, // out = 0, row = 1
44 -8, -6, 7, 5, // out = 1, row = 1
46 std::vector<float> bias_data{1, 2};
47 Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
48 Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
49 Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
50 Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
52 Conv2DParams params{};
53 params.padding = Padding::VALID;
54 params.stride_height = 2;
55 params.stride_width = 1;
56 params.dilation_height_factor = 1;
57 params.dilation_width_factor = 1;
58 params.activation = Activation::RELU;
60 Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
64 std::vector<float> ref_output_data{
65 11, 16, 7, 20, // row = 0
66 0, 40, 0, 44, // row = 1
68 std::vector<int32_t> ref_output_shape{1, 2, 2, 2};
69 EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
70 EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
73 TEST(Conv2DTest, FloatCheck)
75 Shape input_shape{2, 2, 4, 1};
76 Shape filter_shape{3, 2, 2, 1};
78 std::vector<float> input_data{
80 1, 1, 1, 1, // row = 1
81 2, 2, 2, 2, // row = 2
83 1, 2, 3, 4, // row = 1
84 1, 2, 3, 4, // row = 2
86 std::vector<float> filter_data{
87 1, 2, 3, 4, // first 2x2 filter
88 -1, 1, -1, 1, // second 2x2 filter
89 -1, -1, 1, 1, // third 2x2 filter
91 std::vector<float> bias_data{1, 2, 3};
92 Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
93 Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
94 Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
95 Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
97 Conv2DParams params{};
98 params.padding = Padding::VALID;
99 params.stride_height = 2;
100 params.stride_width = 2;
101 params.dilation_height_factor = 1;
102 params.dilation_width_factor = 1;
103 params.activation = Activation::NONE;
105 Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
109 std::vector<float> ref_output_data{
110 18, 2, 5, // first batch, left
111 18, 2, 5, // first batch, right
112 17, 4, 3, // second batch, left
113 37, 4, 3, // second batch, right
115 std::vector<int32_t> ref_output_shape{2, 1, 2, 3};
116 EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(ref_output_data));
117 EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
120 TEST(Conv2DTest, Uint8)
122 std::vector<float> input_data{
124 1, 1, 1, 1, // row = 1
125 2, 2, 2, 2, // row = 2
127 1, 2, 3, 4, // row = 1
128 1, 2, 3, 4, // row = 2
130 std::vector<float> filter_data{
131 1, 2, 3, 4, // first 2x2 filter
132 -1, 1, -1, 1, // second 2x2 filter
133 -1, -1, 1, 1, // third 2x2 filter
135 std::vector<float> bias_data{1, 2, 3};
137 std::pair<float, int32_t> input_quant_param = quantizationParams<uint8_t>(-63.5, 64);
138 std::pair<float, int32_t> output_quant_param = quantizationParams<uint8_t>(-127, 128);
140 Tensor input_tensor = makeInputTensor<DataType::U8>({2, 2, 4, 1}, input_quant_param.first,
141 input_quant_param.second, input_data);
142 Tensor filter_tensor = makeInputTensor<DataType::U8>({3, 2, 2, 1}, input_quant_param.first,
143 input_quant_param.second, filter_data);
144 Tensor bias_tensor = makeInputTensor<DataType::S32>(
145 {3}, input_quant_param.first * input_quant_param.first, 0, bias_data);
146 Tensor output_tensor =
147 makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second);
149 Conv2DParams params{};
150 params.padding = Padding::VALID;
151 params.stride_height = 2;
152 params.stride_width = 2;
153 params.dilation_height_factor = 1;
154 params.dilation_width_factor = 1;
155 params.activation = Activation::NONE;
157 Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
161 std::vector<float> ref_output_data{
162 18, 2, 5, // first batch, left
163 18, 2, 5, // first batch, right
164 17, 4, 3, // second batch, left
165 37, 4, 3, // second batch, right
167 std::vector<int32_t> ref_output_shape{2, 1, 2, 3};
168 EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
169 EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
172 TEST(Conv2DTest, Uint8_CWQ)
174 const int output_channels = 3;
175 std::vector<float> input_data{
177 1, 1, 1, 1, // row = 1
178 2, 2, 2, 2, // row = 2
180 1, 2, 3, 4, // row = 1
181 1, 2, 3, 4, // row = 2
183 std::vector<float> filter_data{
184 1, 2, 3, 4, // first 2x2 filter
185 -1, 1, -1, 1, // second 2x2 filter
186 -1, -1, 1, 1, // third 2x2 filter
188 std::vector<float> bias_data{1, 2, 3};
189 Shape filter_shape{output_channels, 2, 2, 1};
191 std::pair<float, int32_t> input_quant_param = quantizationParams<uint8_t>(0, 4);
192 std::pair<float, int32_t> output_quant_param = quantizationParams<uint8_t>(-127, 128);
194 std::vector<std::pair<float, int32_t>> filter_quant_params;
195 filter_quant_params.push_back(quantizationParams<uint8_t>(0, 4));
196 filter_quant_params.push_back(quantizationParams<uint8_t>(-1, 1));
197 filter_quant_params.push_back(quantizationParams<uint8_t>(-1, 1));
199 std::vector<float> filter_scales;
200 std::vector<int32_t> filter_zerops;
201 for (auto iter : filter_quant_params)
203 filter_scales.push_back(iter.first);
204 filter_zerops.push_back(iter.second);
207 std::vector<float> bias_scales;
208 for (int i = 0; i < output_channels; ++i)
209 bias_scales.push_back(filter_quant_params[i].first * input_quant_param.first);
210 std::vector<int32_t> zerop(output_channels, 0);
212 Tensor input_tensor = makeInputTensor<DataType::U8>({2, 2, 4, 1}, input_quant_param.first,
213 input_quant_param.second, input_data);
214 Tensor filter_tensor =
215 makeInputTensor<DataType::U8>(filter_shape, filter_scales, filter_zerops, 0, filter_data);
217 makeInputTensor<DataType::S32>({output_channels}, bias_scales, zerop, 0, bias_data);
218 Tensor output_tensor =
219 makeOutputTensor(DataType::U8, output_quant_param.first, output_quant_param.second);
221 Conv2DParams params{};
222 params.padding = Padding::VALID;
223 params.stride_height = 2;
224 params.stride_width = 2;
225 params.dilation_height_factor = 1;
226 params.dilation_width_factor = 1;
227 params.activation = Activation::NONE;
229 Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
233 std::vector<float> ref_output_data{
234 18, 2, 5, // first batch, left
235 18, 2, 5, // first batch, right
236 17, 4, 3, // second batch, left
237 37, 4, 3, // second batch, right
239 std::vector<int32_t> ref_output_shape{2, 1, 2, 3};
240 EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
241 EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
244 TEST(Conv2DTest, SInt16)
246 Shape input_shape{1, 4, 3, 2};
247 Shape filter_shape{2, 2, 2, 2};
249 std::vector<int32_t> ref_output_shape{1, 2, 2, 2};
251 std::vector<float> input_data{
252 1, 2, 3, 4, 5, 6, // row = 0
253 7, 8, 9, 10, 11, 12, // row = 1
254 13, 14, 15, 16, 17, 18, // row = 2
255 19, 20, 21, 22, 23, 24, // row = 3
257 std::vector<float> filter_data{
258 1, 2, -3, -4, // out = 0, row = 0
259 -5, 6, -7, 8, // out = 1, row = 0
260 4, -2, 3, -1, // out = 0, row = 1
261 -8, -6, 7, 5, // out = 1, row = 1
263 std::vector<float> bias_data{1, 2};
264 std::vector<float> ref_output_data{
265 11, 16, 7, 20, // row = 0
266 0, 40, 0, 44, // row = 1
269 Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, 0.25, 0, input_data);
270 Tensor filter_tensor = makeInputTensor<DataType::S16>(filter_shape, 0.2, 0, filter_data);
271 Tensor bias_tensor = makeInputTensor<DataType::S64>(bias_shape, 0.25 * 0.2, 0, bias_data);
272 Tensor output_tensor = makeOutputTensor(DataType::S16, 0.5, 0);
274 Conv2DParams params{};
275 params.padding = Padding::VALID;
276 params.stride_height = 2;
277 params.stride_width = 1;
278 params.dilation_height_factor = 1;
279 params.dilation_width_factor = 1;
280 params.activation = Activation::RELU;
282 Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
286 EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
287 EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
290 TEST(Conv2DTest, SInt16_CWQ_weights)
292 Shape input_shape{1, 2, 2, 2}; // Batch x H x W x C
293 Shape filter_shape{3, 1, 1, 2}; // Out channels x H x W x In Channels
295 std::vector<int32_t> ref_output_shape{1, 2, 2, 3};
297 std::vector<float> input_data{
298 1, 2, // row = 0, col 0
299 3, 4, // row = 0, col 1
300 5, 6, // row = 1, col 0
301 7, 8, // row = 1, col 1
303 std::vector<float> filter_data{
308 std::vector<float> bias_data{1, 10, 5};
309 std::vector<float> ref_output_data{
310 0, 5, 4, // row 0, col 0
311 1, 1, 8, // row 0, col 1
312 3, 0, 12, // row 1, col 0
313 5, 0, 16, // row 1, col 1
316 float input_scale = 0.25f;
317 float output_scale = 0.05f;
318 std::vector<float> filter_scales = {0.25f, 0.2f, 0.1f};
319 std::vector<float> bias_scales;
320 for (int i = 0; i < filter_scales.size(); ++i)
321 bias_scales.push_back(filter_scales[i] * input_scale);
322 std::vector<int32_t> zerop = {0, 0, 0};
324 Tensor input_tensor = makeInputTensor<DataType::S16>(input_shape, input_scale, 0, input_data);
325 Tensor filter_tensor =
326 makeInputTensor<DataType::S16>(filter_shape, filter_scales, zerop, 0, filter_data);
327 Tensor bias_tensor = makeInputTensor<DataType::S64>(bias_shape, bias_scales, zerop, 0, bias_data);
328 Tensor output_tensor = makeOutputTensor(DataType::S16, output_scale, 0);
330 Conv2DParams params{};
331 params.padding = Padding::VALID;
332 params.stride_height = 1;
333 params.stride_width = 1;
334 params.dilation_height_factor = 1;
335 params.dilation_width_factor = 1;
336 params.activation = Activation::RELU;
338 Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
342 EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
343 EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_output_data));
346 TEST(Conv2DTest, Unsupported_Type_Configure_NEG)
348 Shape input_shape{1, 4, 3, 2};
349 Shape filter_shape{2, 2, 2, 2};
351 std::vector<int32_t> input_data{
352 1, 2, 3, 4, 5, 6, // row = 0
353 7, 8, 9, 10, 11, 12, // row = 1
354 13, 14, 15, 16, 17, 18, // row = 2
355 19, 20, 21, 22, 23, 24, // row = 3
357 std::vector<float> filter_data{
358 1, 2, -3, -4, // out = 0, row = 0
359 -5, 6, -7, 8, // out = 1, row = 0
360 4, -2, 3, -1, // out = 0, row = 1
361 -8, -6, 7, 5, // out = 1, row = 1
363 std::vector<float> bias_data{1, 2};
364 Tensor input_tensor = makeInputTensor<DataType::S32>(input_shape, input_data);
365 Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
366 Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
367 Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
369 Conv2DParams params{};
370 params.padding = Padding::VALID;
371 params.stride_height = 2;
372 params.stride_width = 1;
373 params.dilation_height_factor = 1;
374 params.dilation_width_factor = 1;
375 params.activation = Activation::RELU;
377 Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
378 EXPECT_ANY_THROW(kernel.configure());
381 TEST(Conv2DTest, Invalid_Bias_Type_NEG)
383 Shape input_shape{1, 4, 3, 2};
384 Shape filter_shape{2, 2, 2, 2};
386 std::vector<float> input_data{
387 1, 2, 3, 4, 5, 6, // row = 0
388 7, 8, 9, 10, 11, 12, // row = 1
389 13, 14, 15, 16, 17, 18, // row = 2
390 19, 20, 21, 22, 23, 24, // row = 3
392 std::vector<float> filter_data{
393 1, 2, -3, -4, // out = 0, row = 0
394 -5, 6, -7, 8, // out = 1, row = 0
395 4, -2, 3, -1, // out = 0, row = 1
396 -8, -6, 7, 5, // out = 1, row = 1
398 std::vector<uint8_t> bias_data{1, 2};
399 Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
400 Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
401 Tensor bias_tensor = makeInputTensor<DataType::U8>(bias_shape, bias_data);
402 Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
404 Conv2DParams params{};
405 params.padding = Padding::VALID;
406 params.stride_height = 2;
407 params.stride_width = 1;
408 params.dilation_height_factor = 1;
409 params.dilation_width_factor = 1;
410 params.activation = Activation::RELU;
412 Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
413 EXPECT_ANY_THROW(kernel.configure());
416 TEST(Conv2DTest, Invalid_Bias_Data_NEG)
418 Shape input_shape{1, 4, 3, 2};
419 Shape filter_shape{2, 2, 2, 2};
421 std::vector<float> input_data{
422 1, 2, 3, 4, 5, 6, // row = 0
423 7, 8, 9, 10, 11, 12, // row = 1
424 13, 14, 15, 16, 17, 18, // row = 2
425 19, 20, 21, 22, 23, 24, // row = 3
427 std::vector<float> filter_data{
428 1, 2, -3, -4, // out = 0, row = 0
429 -5, 6, -7, 8, // out = 1, row = 0
430 4, -2, 3, -1, // out = 0, row = 1
431 -8, -6, 7, 5, // out = 1, row = 1
433 std::vector<float> bias_data{1, 2, 3};
434 Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
435 Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
436 Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
437 Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
439 Conv2DParams params{};
440 params.padding = Padding::VALID;
441 params.stride_height = 2;
442 params.stride_width = 1;
443 params.dilation_height_factor = 1;
444 params.dilation_width_factor = 1;
445 params.activation = Activation::RELU;
447 Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
448 EXPECT_ANY_THROW(kernel.configure());
451 TEST(Conv2DTest, Invalid_Input_Shape_NEG)
453 Shape input_shape{1, 4, 6, 1};
454 Shape filter_shape{2, 2, 2, 2};
456 std::vector<float> input_data{
457 1, 2, 3, 4, 5, 6, // row = 0
458 7, 8, 9, 10, 11, 12, // row = 1
459 13, 14, 15, 16, 17, 18, // row = 2
460 19, 20, 21, 22, 23, 24, // row = 3
462 std::vector<float> filter_data{
463 1, 2, -3, -4, // out = 0, row = 0
464 -5, 6, -7, 8, // out = 1, row = 0
465 4, -2, 3, -1, // out = 0, row = 1
466 -8, -6, 7, 5, // out = 1, row = 1
468 std::vector<float> bias_data{1, 2};
469 Tensor input_tensor = makeInputTensor<DataType::FLOAT32>(input_shape, input_data);
470 Tensor filter_tensor = makeInputTensor<DataType::FLOAT32>(filter_shape, filter_data);
471 Tensor bias_tensor = makeInputTensor<DataType::FLOAT32>(bias_shape, bias_data);
472 Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
474 Conv2DParams params{};
475 params.padding = Padding::VALID;
476 params.stride_height = 2;
477 params.stride_width = 1;
478 params.dilation_height_factor = 1;
479 params.dilation_width_factor = 1;
480 params.activation = Activation::RELU;
482 Conv2D kernel(&input_tensor, &filter_tensor, &bias_tensor, &output_tensor, params);
483 EXPECT_ANY_THROW(kernel.configure());
487 } // namespace kernels
488 } // namespace luci_interpreter