1 /*
2 * Copyright (c) 2018-2021 Arm Limited.
3 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24 #include "arm_compute/core/Types.h"
25 #include "src/gpu/cl/kernels/ClIm2ColKernel.h"
26 #include "tests/CL/CLAccessor.h"
27 #include "tests/CL/Helper.h"
28 #include "tests/framework/Asserts.h"
29 #include "tests/framework/Macros.h"
30 #include "tests/framework/datasets/Datasets.h"
31 #include "tests/validation/Validation.h"
32 #include "tests/validation/fixtures/Im2ColFixture.h"
33
34 namespace arm_compute
35 {
36 namespace test
37 {
38 namespace validation
39 {
40 TEST_SUITE(CL)
41 TEST_SUITE(Im2Col)
42
43 using ClIm2Col = ClSynthetizeOperatorWithBorder<opencl::kernels::ClIm2ColKernel>;
44
45 /** Negative tests
46 *
47 * A series of validation tests on configurations which according to the API specification
48 * the function should fail against.
49 *
50 * Checks performed in order:
51 * - Pass unsupported data type for input
52 * - Pass a quantized input and ask to compress the bias into the resulting matrix
53 * - Pass a dilation factor of 0
54 * - Check NHWC data layout while requesting to perform a grouped operation
55 * - Check NCHW grouped operation when the number of channels is not multiple of the groups
56 * - Pass an invalid output shape
57 */
TEST_CASE(Negative,framework::DatasetMode::ALL)58 TEST_CASE(Negative, framework::DatasetMode::ALL)
59 {
60 // Unsupported data type
61 {
62 const auto input = TensorInfo(TensorShape(10U, 12U, 1U, 2U), 1, DataType::SIZET);
63 const auto output = TensorInfo(TensorShape(9U, 10U, 12U, 2U), 1, DataType::F32);
64 const auto conv_size = Size2D(3, 3);
65 const bool has_bias = false;
66 const auto status = opencl::kernels::ClIm2ColKernel::validate(&input, &output, conv_size, PadStrideInfo(), has_bias);
67 ARM_COMPUTE_EXPECT(bool(status) == false, framework::LogLevel::ERRORS);
68 }
69
70 // Passing quantized input and ask to merge the bias in the output
71 {
72 const auto input = TensorInfo(TensorShape(10U, 12U, 1U, 2U), 1, DataType::QASYMM8);
73 const auto output = TensorInfo(TensorShape(9U, 80U, 2U), 1, DataType::QASYMM8);
74 const auto conv_size = Size2D(3, 3);
75 const bool has_bias = true;
76 const auto status = opencl::kernels::ClIm2ColKernel::validate(&input, &output, conv_size, PadStrideInfo(), has_bias);
77 ARM_COMPUTE_EXPECT(bool(status) == false, framework::LogLevel::ERRORS);
78 }
79
80 // Invalid dilation
81 {
82 const auto input = TensorInfo(TensorShape(10U, 12U, 1U, 2U), 1, DataType::F32);
83 const auto output = TensorInfo(TensorShape(9U, 80U, 2U), 1, DataType::F32);
84 const auto conv_size = Size2D(3, 3);
85 const auto dilation = Size2D(0, 1);
86 const bool has_bias = false;
87 const auto status = opencl::kernels::ClIm2ColKernel::validate(&input, &output, conv_size, PadStrideInfo(), has_bias, dilation);
88 ARM_COMPUTE_EXPECT(bool(status) == false, framework::LogLevel::ERRORS);
89 }
90
91 // NHWC and grouping greater than 1
92 {
93 const auto input = TensorInfo(TensorShape(10U, 12U, 1U, 2U), 1, DataType::F32, DataLayout::NHWC);
94 const auto output = TensorInfo(TensorShape(9U, 80U, 2U), 1, DataType::F32);
95 const auto conv_size = Size2D(3, 3);
96 const auto dilation = Size2D(1, 1);
97 const bool has_bias = false;
98 const unsigned int num_groups = 2;
99 const auto status = opencl::kernels::ClIm2ColKernel::validate(&input, &output, conv_size, PadStrideInfo(), has_bias, dilation, num_groups);
100 ARM_COMPUTE_EXPECT(bool(status) == false, framework::LogLevel::ERRORS);
101 }
102
103 // NCWH and channels % num_groups !=0
104 {
105 const auto input = TensorInfo(TensorShape(10U, 12U, 1U, 2U), 1, DataType::F32, DataLayout::NCHW);
106 const auto output = TensorInfo(TensorShape(9U, 80U, 2U), 1, DataType::F32);
107 const auto conv_size = Size2D(3, 3);
108 const auto dilation = Size2D(1, 1);
109 const bool has_bias = false;
110 const unsigned int num_groups = 2;
111 const auto status = opencl::kernels::ClIm2ColKernel::validate(&input, &output, conv_size, PadStrideInfo(), has_bias, dilation, num_groups);
112 ARM_COMPUTE_EXPECT(bool(status) == false, framework::LogLevel::ERRORS);
113 }
114
115 // Invalid output shape
116 {
117 const auto input = TensorInfo(TensorShape(10U, 12U, 1U, 2U), 1, DataType::F32);
118 const auto output = TensorInfo(TensorShape(9U, 81U, 2U), 1, DataType::F32);
119 const auto conv_size = Size2D(3, 3);
120 const bool has_bias = false;
121 const auto status = opencl::kernels::ClIm2ColKernel::validate(&input, &output, conv_size, PadStrideInfo(), has_bias);
122 ARM_COMPUTE_EXPECT(bool(status) == false, framework::LogLevel::ERRORS);
123 }
124
125 // Kernel dimensions are too big
126 {
127 const auto input = TensorInfo(TensorShape(1U, 9U, 5U, 2U), 1, DataType::F32, DataLayout::NHWC);
128 const auto output = TensorInfo(TensorShape(1U, 1U, 1U, 2U), 1, DataType::F32, DataLayout::NHWC);
129 const auto conv_size = Size2D(9, 9);
130 const bool has_bias = false;
131 const auto status = opencl::kernels::ClIm2ColKernel::validate(&input, &output, conv_size, PadStrideInfo(), has_bias);
132 ARM_COMPUTE_EXPECT(bool(status) == false, framework::LogLevel::ERRORS);
133 }
134 }
135
136 template <typename T>
137 using ClIm2ColFixture = Im2ColOpValidationFixture<CLTensor, CLAccessor, ClIm2Col, T, true>;
138
139 TEST_SUITE(NHWC)
140
141 /** Test special kernel used for NHWC for 3x3 kernels
142 *
143 * @note 2 elements processed per iteration
144 *
145 * Three tests will be run:
146 * - Channels are multiple of elements processed
147 * - Channels larger and non multiple of elements used
148 * - Channels smaller and not multiple of elements used
149 *
150 * Kernel tested im2col3x3_nhwc
151 */
152 FIXTURE_DATA_TEST_CASE(W3x3,
153 ClIm2ColFixture<float>,
154 framework::DatasetMode::ALL,
155 combine(combine(combine(combine(combine(combine(
156 framework::dataset::make("InputShape",
157 {
158 TensorShape(5U, 7U, 2U, 2U), TensorShape(4U, 6U, 3U, 2U), TensorShape(5U, 3U, 1U, 2U),
159 }),
160 framework::dataset::make("DataType", DataType::F32)),
161 framework::dataset::make("Kernel", Size2D(3, 3))),
162 framework::dataset::make("PadStride", { PadStrideInfo(1, 2, 1, 2), PadStrideInfo(1, 1, 0, 0) })),
163 framework::dataset::make("QInfo", QuantizationInfo())),
164 framework::dataset::make("DataLayout", DataLayout::NHWC)),
165 framework::dataset::make("Groups", 1)))
166 {
167 // Validate output
168 validate(CLAccessor(_target), _reference);
169 }
170
171 /** Test special kernel used for NHWC for 9x9 kernels
172 *
173 * @note 2 elements processed per iteration
174 *
175 * Three tests will be run:
176 * - Channels are multiple of elements processed
177 * - Channels larger and non multiple of elements used
178 * - Channels smaller and not multiple of elements used
179 *
180 * Kernel tested im2col9x9_nhwc
181 */
182 FIXTURE_DATA_TEST_CASE(W9x9,
183 ClIm2ColFixture<float>,
184 framework::DatasetMode::ALL,
185 combine(combine(combine(combine(combine(combine(
186 framework::dataset::make("InputShape",
187 {
188 TensorShape(13U, 15U, 2U, 2U), TensorShape(15U, 12U, 3U, 2U), TensorShape(13U, 22U, 1U, 2U),
189 }),
190 framework::dataset::make("DataType", DataType::F32)),
191 framework::dataset::make("Kernel", Size2D(9, 9))),
192 framework::dataset::make("PadStride", { PadStrideInfo(2, 2, 1, 2), PadStrideInfo(1, 1, 0, 0) })),
193 framework::dataset::make("QInfo", QuantizationInfo())),
194 framework::dataset::make("DataLayout", DataLayout::NHWC)),
195 framework::dataset::make("Groups", 1)))
196 {
197 // Validate output
198 validate(CLAccessor(_target), _reference);
199 }
200
201 /** Test generic kernel used for NHWC
202 *
203 * @note 2 elements processed per iteration
204 *
205 * Three tests will be run:
206 * - Channels are multiple of elements processed
207 * - Channels larger and non multiple of elements used
208 * - Channels smaller and not multiple of elements used
209 *
210 * Kernel tested im2col_generic_nhwc
211 */
212 FIXTURE_DATA_TEST_CASE(Generic,
213 ClIm2ColFixture<float>,
214 framework::DatasetMode::ALL,
215 combine(combine(combine(combine(combine(combine(
216 framework::dataset::make("InputShape",
217 {
218 TensorShape(13U, 15U, 4U, 2U), TensorShape(15U, 12U, 7U, 1U), TensorShape(5U, 3U, 1U, 1U),
219 }),
220 framework::dataset::make("DataType", DataType::F32)),
221 framework::dataset::make("Kernel", Size2D(5, 3))),
222 framework::dataset::make("PadStride", { PadStrideInfo(2, 2, 1, 2), PadStrideInfo(1, 1, 0, 0) })),
223 framework::dataset::make("QInfo", QuantizationInfo())),
224 framework::dataset::make("DataLayout", DataLayout::NHWC)),
225 framework::dataset::make("Groups", 1)))
226 {
227 // Validate output
228 validate(CLAccessor(_target), _reference);
229 }
230 TEST_SUITE_END() // NHWC
231
TEST_SUITE(NCHW)232 TEST_SUITE(NCHW)
233
234 /** Test special kernel used for NCHW for 1x1 kernels with stride 1 and no padding
235 *
236 * @note 4 elements processed per iteration
237 *
238 * Three tests will be run:
239 * - Channels are multiple of elements processed
240 * - Channels larger and non multiple of elements used
241 * - Channels smaller and not multiple of elements used
242 *
243 * Kernel tested im2col1x1_stridex1_nchw
244 */
245 FIXTURE_DATA_TEST_CASE(W1x1_Stride1_NoPad,
246 ClIm2ColFixture<float>,
247 framework::DatasetMode::ALL,
248 combine(combine(combine(combine(combine(combine(
249 framework::dataset::make("InputShape", { TensorShape(4U, 4U, 3U, 2U), TensorShape(5U, 4U, 3U, 2U), TensorShape(3U, 4U, 3U, 2U) }),
250 framework::dataset::make("DataType", DataType::F32)),
251 framework::dataset::make("Kernel", Size2D(1, 1))),
252 framework::dataset::make("PadStride", PadStrideInfo(1, 1, 0, 0))),
253 framework::dataset::make("QInfo", QuantizationInfo())),
254 framework::dataset::make("DataLayout", DataLayout::NCHW)),
255 framework::dataset::make("Groups", 1)))
256 {
257 // Validate output
258 validate(CLAccessor(_target), _reference);
259 }
260
261 /** Test special kernel used for NCHW for 3x3 kernels
262 *
263 * @note 1 elements processed per iteration
264 *
265 * Executed single test as padding is required.
266 *
267 * Kernel tested im2col3x3_nchw
268 */
269 FIXTURE_DATA_TEST_CASE(W3x3,
270 ClIm2ColFixture<float>,
271 framework::DatasetMode::ALL,
272 combine(combine(combine(combine(combine(combine(
273 framework::dataset::make("InputShape", TensorShape(4U, 4U, 3U, 2U)),
274 framework::dataset::make("DataType", DataType::F32)),
275 framework::dataset::make("Kernel", Size2D(3, 3))),
276 framework::dataset::make("PadStride", PadStrideInfo(1, 2, 1, 2))),
277 framework::dataset::make("QInfo", QuantizationInfo())),
278 framework::dataset::make("DataLayout", DataLayout::NCHW)),
279 framework::dataset::make("Groups", { 1, 3 })))
280 {
281 // Validate output
282 validate(CLAccessor(_target), _reference);
283 }
284
285 /** Test special kernel used for NCHW for 5x5 kernels
286 *
287 * @note 1 elements processed per iteration
288 *
289 * Executed single test as padding is required.
290 *
291 * Kernel tested im2col5x5_nchw
292 */
293 FIXTURE_DATA_TEST_CASE(W5x5,
294 ClIm2ColFixture<float>,
295 framework::DatasetMode::ALL,
296 combine(combine(combine(combine(combine(combine(
297 framework::dataset::make("InputShape", TensorShape(7U, 4U, 3U, 2U)),
298 framework::dataset::make("DataType", DataType::F32)),
299 framework::dataset::make("Kernel", Size2D(5, 5))),
300 framework::dataset::make("PadStride", PadStrideInfo(2, 1, 2, 1))),
301 framework::dataset::make("QInfo", QuantizationInfo())),
302 framework::dataset::make("DataLayout", DataLayout::NCHW)),
303 framework::dataset::make("Groups", { 1, 3 })))
304 {
305 // Validate output
306 validate(CLAccessor(_target), _reference);
307 }
308
309 /** Test special kernel used for NCHW for 11x11 kernels when no padding present
310 *
311 * @note 1 elements processed per iteration
312 *
313 * Two tests will be run:
314 * - Without padding requirements
315 * - With padding requirements
316 *
317 * Kernel tested im2col11x11_padx0_pady0_nchw
318 */
319 FIXTURE_DATA_TEST_CASE(W11x11_NoPad,
320 ClIm2ColFixture<float>,
321 framework::DatasetMode::ALL,
322 combine(combine(combine(combine(combine(combine(
323 framework::dataset::make("InputShape", { TensorShape(11U, 11U, 2U, 2U), TensorShape(14U, 13U, 1U, 2U) }),
324 framework::dataset::make("DataType", DataType::F32)),
325 framework::dataset::make("Kernel", Size2D(11, 11))),
326 framework::dataset::make("PadStride", PadStrideInfo(1, 1, 0, 0))),
327 framework::dataset::make("QInfo", QuantizationInfo())),
328 framework::dataset::make("DataLayout", DataLayout::NCHW)),
329 framework::dataset::make("Groups", 1)))
330 {
331 // Validate output
332 validate(CLAccessor(_target), _reference);
333 }
334
335 /** Test special kernel used for NCHW for kernels which do not fall in the categories above and have no padding present
336 *
337 * @note 1 elements processed per iteration
338 *
339 * Executed single test as padding is required.
340 *
341 * Kernel tested im2col_generic_padx0_pady0_nchw
342 */
343 FIXTURE_DATA_TEST_CASE(GenericZeroPad,
344 ClIm2ColFixture<float>,
345 framework::DatasetMode::ALL,
346 combine(combine(combine(combine(combine(combine(
347 framework::dataset::make("InputShape", TensorShape(13U, 11U, 2U, 2U)),
348 framework::dataset::make("DataType", DataType::F32)),
349 framework::dataset::make("Kernel", Size2D(3, 2))),
350 framework::dataset::make("PadStride", PadStrideInfo(2, 1, 0, 0))),
351 framework::dataset::make("QInfo", QuantizationInfo())),
352 framework::dataset::make("DataLayout", DataLayout::NCHW)),
353 framework::dataset::make("Groups", { 1, 2 })))
354 {
355 // Validate output
356 validate(CLAccessor(_target), _reference);
357 }
358 TEST_SUITE_END() // NCHW
359
360 /** Generic NCHW/NHWC kernel
361 *
362 * @note 1 elements processed per iteration
363 *
364 * Padding is not needed thus executed sample tests with different kernels sizes
365 * and stride/padding information
366 *
367 * Kernel tested im2col_generic_(nchw|nhwc)
368 */
369 FIXTURE_DATA_TEST_CASE(Generic,
370 ClIm2ColFixture<float>,
371 framework::DatasetMode::ALL,
372 combine(combine(combine(combine(combine(combine(
373 framework::dataset::make("InputShape", TensorShape(13U, 11U, 5U, 2U)),
374 framework::dataset::make("DataType", DataType::F32)),
375 framework::dataset::make("Kernel", { Size2D(3, 2), Size2D(3, 5) })),
376 framework::dataset::make("PadStride", PadStrideInfo(2, 1, 2, 1))),
377 framework::dataset::make("QInfo", QuantizationInfo())),
378 framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })),
379 framework::dataset::make("Groups", 1)))
380 {
381 // Validate output
382 validate(CLAccessor(_target), _reference);
383 }
384
385 /** Tests to check that quantized padding value is set correctly
386 *
387 * Kernels tested:
388 * - im2col_generic_nhwc
389 * - im2col_generic_nchw
390 * - im2col5x5_nchw
391 * - im2col3x3_nhwc
392 * - im2col3x3_nchw
393 * - im2col9x9_nhwc
394 */
395 FIXTURE_DATA_TEST_CASE(Quantized,
396 ClIm2ColFixture<uint8_t>,
397 framework::DatasetMode::ALL,
398 combine(combine(combine(combine(combine(combine(
399 framework::dataset::make("InputShape", TensorShape(13U, 11U, 11U, 2U)),
400 framework::dataset::make("DataType", DataType::QASYMM8)),
401 framework::dataset::make("Kernel", { Size2D(1, 1), Size2D(3, 3), Size2D(5, 5), Size2D(3, 5), Size2D(9, 9) })),
402 framework::dataset::make("PadStride", { PadStrideInfo(1, 2, 1, 1) })),
403 framework::dataset::make("QInfo", QuantizationInfo(0.5f, 10))),
404 framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })),
405 framework::dataset::make("Groups", 1)))
406 {
407 // Validate output
408 validate(CLAccessor(_target), _reference);
409 }
410
411 /** Tests to check that half-precision execution
412 *
413 * Kernels tested:
414 * - im2col_generic_nhwc
415 * - im2col_generic_nchw
416 * - im2col5x5_nchw
417 * - im2col3x3_nhwc
418 * - im2col3x3_nchw
419 * - im2col9x9_nhwc
420 */
421 FIXTURE_DATA_TEST_CASE(FP16,
422 ClIm2ColFixture<half>,
423 framework::DatasetMode::ALL,
424 combine(combine(combine(combine(combine(combine(
425 framework::dataset::make("InputShape", TensorShape(13U, 11U, 11U, 2U)),
426 framework::dataset::make("DataType", DataType::F16)),
427 framework::dataset::make("Kernel", { Size2D(1, 1), Size2D(3, 3), Size2D(5, 5), Size2D(3, 5), Size2D(9, 9) })),
428 framework::dataset::make("PadStride", { PadStrideInfo(1, 2, 1, 1) })),
429 framework::dataset::make("QInfo", QuantizationInfo())),
430 framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })),
431 framework::dataset::make("Groups", 1)))
432 {
433 // Validate output
434 validate(CLAccessor(_target), _reference);
435 }
436
437 TEST_SUITE_END() // Im2Col
438 TEST_SUITE_END() // CL
439 } // namespace validation
440 } // namespace test
441 } // namespace arm_compute
442