Imported Upstream version 1.18.0
[platform/core/ml/nnfw.git] / tests / nnfw_api / src / one_op_tests / DepthwiseConv2D.cc
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "GenModelTest.h"
18
19 TEST_F(GenModelTest, OneOp_DepthwiseConv2D)
20 {
21   CircleGen cgen;
22   std::vector<float> weight_data{1, 2, 3, 4, -9, 10, -11, 12, 5, 6, 7, 8, 13, -14, 15, -16};
23   uint32_t weight_buf = cgen.addBuffer(weight_data);
24   std::vector<float> bias_data{1, 2, 3, 4};
25   uint32_t bias_buf = cgen.addBuffer(bias_data);
26   int in = cgen.addTensor({{1, 3, 2, 2}, circle::TensorType::TensorType_FLOAT32});
27   int weight = cgen.addTensor({{1, 2, 2, 4}, circle::TensorType::TensorType_FLOAT32, weight_buf});
28   int bias = cgen.addTensor({{1, 1, 1, 4}, circle::TensorType::TensorType_FLOAT32, bias_buf});
29   int out = cgen.addTensor({{1, 2, 1, 4}, circle::TensorType::TensorType_FLOAT32});
30   cgen.addOperatorDepthwiseConv2D({{in, weight, bias}, {out}}, circle::Padding_VALID, 1, 1, 2,
31                                   circle::ActivationFunctionType_NONE);
32   cgen.setInputsAndOutputs({in}, {out});
33
34   _context = std::make_unique<GenModelTestContext>(cgen.finish());
35   _context->addTestCase(uniformTCD<float>({{1, 2, 7, 8, 3, 4, 9, 10, 5, 6, 11, 12}},
36                                           {{71, -34, 99, -20, 91, -26, 127, -4}}));
37   _context->setBackends({"acl_cl", "acl_neon", "cpu", "xnnpack"});
38
39   SUCCEED();
40 }
41
42 TEST_F(GenModelTest, OneOp_DepthwiseConv2D_No_Multiplier)
43 {
44   CircleGen cgen;
45   std::vector<float> weight_data{0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f};
46   uint32_t weight_buf = cgen.addBuffer(weight_data);
47   std::vector<float> bias_data{0.5f, -0.5f};
48   uint32_t bias_buf = cgen.addBuffer(bias_data);
49   int in = cgen.addTensor({{1, 2, 2, 2}, circle::TensorType::TensorType_FLOAT32});
50   int weight = cgen.addTensor({{1, 3, 1, 2}, circle::TensorType::TensorType_FLOAT32, weight_buf});
51   int bias = cgen.addTensor({{1, 1, 1, 2}, circle::TensorType::TensorType_FLOAT32, bias_buf});
52   int out = cgen.addTensor({{1, 2, 2, 2}, circle::TensorType::TensorType_FLOAT32});
53   cgen.addOperatorDepthwiseConv2D({{in, weight, bias}, {out}}, circle::Padding_SAME, 1, 1, 1,
54                                   circle::ActivationFunctionType_NONE);
55   cgen.setInputsAndOutputs({in}, {out});
56
57   _context = std::make_unique<GenModelTestContext>(cgen.finish());
58   _context->addTestCase(
59     uniformTCD<float>({{0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f}},
60                       {{16.5f, 27.5f, 28.5f, 43.5f, 8.5f, 15.5f, 12.5f, 23.5f}}));
61   _context->setBackends({"acl_cl", "acl_neon", "cpu", "gpu_cl"});
62   SUCCEED();
63 }
64
65 TEST_F(GenModelTest, OneOp_DepthwiseConv2D_No_Multiplier_RELU6)
66 {
67   CircleGen cgen;
68   std::vector<float> weight_data{0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f};
69   uint32_t weight_buf = cgen.addBuffer(weight_data);
70   std::vector<float> bias_data{0.5f, -0.5f};
71   uint32_t bias_buf = cgen.addBuffer(bias_data);
72   int in = cgen.addTensor({{1, 2, 2, 2}, circle::TensorType::TensorType_FLOAT32});
73   int weight = cgen.addTensor({{1, 3, 1, 2}, circle::TensorType::TensorType_FLOAT32, weight_buf});
74   int bias = cgen.addTensor({{1, 1, 1, 2}, circle::TensorType::TensorType_FLOAT32, bias_buf});
75   int out = cgen.addTensor({{1, 2, 2, 2}, circle::TensorType::TensorType_FLOAT32});
76   cgen.addOperatorDepthwiseConv2D({{in, weight, bias}, {out}}, circle::Padding_SAME, 1, 1, 1,
77                                   circle::ActivationFunctionType_RELU6);
78   cgen.setInputsAndOutputs({in}, {out});
79
80   _context = std::make_unique<GenModelTestContext>(cgen.finish());
81   _context->addTestCase(uniformTCD<float>({{0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f}},
82                                           {{6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f}}));
83   _context->setBackends({"acl_cl", "acl_neon", "cpu", "gpu_cl"});
84   SUCCEED();
85 }
86
87 TEST_F(GenModelTest, OneOp_DepthwiseConv2D_3x3)
88 {
89   CircleGen cgen;
90   std::vector<float> weight_data{0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f,
91                                  1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f};
92   uint32_t weight_buf = cgen.addBuffer(weight_data);
93   std::vector<float> bias_data{0.0f, 0.0f};
94   uint32_t bias_buf = cgen.addBuffer(bias_data);
95   int in = cgen.addTensor({{1, 2, 2, 2}, circle::TensorType::TensorType_FLOAT32});
96   int weight = cgen.addTensor({{1, 3, 3, 2}, circle::TensorType::TensorType_FLOAT32, weight_buf});
97   int bias = cgen.addTensor({{1, 1, 1, 2}, circle::TensorType::TensorType_FLOAT32, bias_buf});
98   int out = cgen.addTensor({{1, 2, 2, 2}, circle::TensorType::TensorType_FLOAT32});
99   cgen.addOperatorDepthwiseConv2D({{in, weight, bias}, {out}}, circle::Padding_SAME, 1, 1, 1,
100                                   circle::ActivationFunctionType_NONE);
101   cgen.setInputsAndOutputs({in}, {out});
102
103   _context = std::make_unique<GenModelTestContext>(cgen.finish());
104   _context->addTestCase(
105     uniformTCD<float>({{0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f}},
106                       {{6.0f, 16.0f, 8.0f, 16.0f, 10.0f, 16.0f, 12.0f, 16.0f}}));
107   _context->setBackends({"acl_cl", "acl_neon", "cpu", "gpu_cl"});
108   SUCCEED();
109 }
110
111 TEST_F(GenModelTest, OneOp_DepthwiseConv2D_Dilation)
112 {
113   CircleGen cgen;
114   std::vector<float> weight_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
115   uint32_t weight_buf = cgen.addBuffer(weight_data);
116   std::vector<float> bias_data{0, 0, 0, 0};
117   uint32_t bias_buf = cgen.addBuffer(bias_data);
118   int in = cgen.addTensor({{1, 4, 4, 2}, circle::TensorType::TensorType_FLOAT32});
119   int weight = cgen.addTensor({{1, 2, 2, 4}, circle::TensorType::TensorType_FLOAT32, weight_buf});
120   int bias = cgen.addTensor({{1, 1, 1, 4}, circle::TensorType::TensorType_FLOAT32, bias_buf});
121   int out = cgen.addTensor({{1, 2, 2, 4}, circle::TensorType::TensorType_FLOAT32});
122   cgen.addOperatorDepthwiseConv2D({{in, weight, bias}, {out}}, circle::Padding_VALID, 1, 1, 2,
123                                   circle::ActivationFunctionType_NONE, 2, 2);
124   cgen.setInputsAndOutputs({in}, {out});
125
126   _context = std::make_unique<GenModelTestContext>(cgen.finish());
127   _context->addTestCase(uniformTCD<float>({{
128                                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
129                                             0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
130                                           }},
131                                           {{13, 14, 0, 0, 0, 0, 11, 12, 5, 6, 0, 0, 0, 0, 3, 4}}));
132   _context->setBackends({"acl_cl", "acl_neon", "cpu", "xnnpack"});
133
134   SUCCEED();
135 }
136
137 TEST_F(GenModelTest, OneOp_DepthwiseConv2D_Dilation_N_Stride)
138 {
139   CircleGen cgen;
140   std::vector<float> weight_data{1, 2, 3, 4};
141   uint32_t weight_buf = cgen.addBuffer(weight_data);
142   std::vector<float> bias_data{0, 0, 0, 0};
143   uint32_t bias_buf = cgen.addBuffer(bias_data);
144   int in = cgen.addTensor({{1, 6, 6, 1}, circle::TensorType::TensorType_FLOAT32});
145   int weight = cgen.addTensor({{1, 2, 2, 1}, circle::TensorType::TensorType_FLOAT32, weight_buf});
146   int bias = cgen.addTensor({{1, 1, 1, 1}, circle::TensorType::TensorType_FLOAT32, bias_buf});
147   int out = cgen.addTensor({{1, 3, 3, 1}, circle::TensorType::TensorType_FLOAT32});
148   cgen.addOperatorDepthwiseConv2D({{in, weight, bias}, {out}}, circle::Padding_SAME, 2, 2, 1,
149                                   circle::ActivationFunctionType_NONE, 3, 3);
150   cgen.setInputsAndOutputs({in}, {out});
151
152   _context = std::make_unique<GenModelTestContext>(cgen.finish());
153   _context->addTestCase(uniformTCD<float>({{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
154                                             0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
155                                           {{4, 0, 3, 0, 0, 0, 2, 0, 1}}));
156   _context->setBackends({"acl_cl", "acl_neon", "cpu", "xnnpack", "gpu_cl"});
157
158   SUCCEED();
159 }
160
161 TEST_F(GenModelTest, neg_OneOp_DepthwiseConv2D_Stride)
162 {
163   CircleGen cgen;
164   std::vector<float> weight_data{1, 2, 3, 4, -9, 10, -11, 12, 5, 6, 7, 8, 13, -14, 15, -16};
165   uint32_t weight_buf = cgen.addBuffer(weight_data);
166   std::vector<float> bias_data{1, 2, 3, 4};
167   uint32_t bias_buf = cgen.addBuffer(bias_data);
168   int in = cgen.addTensor({{1, 3, 2, 2}, circle::TensorType::TensorType_FLOAT32});
169   int weight = cgen.addTensor({{1, 2, 2, 4}, circle::TensorType::TensorType_FLOAT32, weight_buf});
170   int bias = cgen.addTensor({{1, 1, 1, 4}, circle::TensorType::TensorType_FLOAT32, bias_buf});
171   int out = cgen.addTensor({{1, 2, 1, 4}, circle::TensorType::TensorType_FLOAT32});
172   cgen.addOperatorDepthwiseConv2D({{in, weight, bias}, {out}}, circle::Padding_VALID, 0, 0, 2,
173                                   circle::ActivationFunctionType_NONE);
174   cgen.setInputsAndOutputs({in}, {out});
175
176   _context = std::make_unique<GenModelTestContext>(cgen.finish());
177   _context->expectFailModelLoad();
178
179   SUCCEED();
180 }
181
182 TEST_F(GenModelTest, neg_OneOp_DepthwiseConv2D_Dilation)
183 {
184   CircleGen cgen;
185   std::vector<float> weight_data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
186   uint32_t weight_buf = cgen.addBuffer(weight_data);
187   std::vector<float> bias_data{0, 0, 0, 0};
188   uint32_t bias_buf = cgen.addBuffer(bias_data);
189   int in = cgen.addTensor({{1, 4, 4, 2}, circle::TensorType::TensorType_FLOAT32});
190   int weight = cgen.addTensor({{1, 2, 2, 4}, circle::TensorType::TensorType_FLOAT32, weight_buf});
191   int bias = cgen.addTensor({{1, 1, 1, 4}, circle::TensorType::TensorType_FLOAT32, bias_buf});
192   int out = cgen.addTensor({{1, 2, 2, 4}, circle::TensorType::TensorType_FLOAT32});
193   cgen.addOperatorDepthwiseConv2D({{in, weight, bias}, {out}}, circle::Padding_VALID, 1, 1, 2,
194                                   circle::ActivationFunctionType_NONE, 0, 0);
195   cgen.setInputsAndOutputs({in}, {out});
196
197   _context = std::make_unique<GenModelTestContext>(cgen.finish());
198   _context->expectFailModelLoad();
199
200   SUCCEED();
201 }
202
203 TEST_F(GenModelTest, neg_OneOp_DepthwiseConv2D_Type)
204 {
205   CircleGen cgen;
206   std::vector<float> weight_data{1, 2, 3, 4, -9, 10, -11, 12, 5, 6, 7, 8, 13, -14, 15, -16};
207   uint32_t weight_buf = cgen.addBuffer(weight_data);
208   std::vector<float> bias_data{1, 2, 3, 4};
209   uint32_t bias_buf = cgen.addBuffer(bias_data);
210   int in = cgen.addTensor({{1, 3, 2, 2}, circle::TensorType::TensorType_FLOAT32});
211   int weight = cgen.addTensor({{1, 2, 2, 4}, circle::TensorType::TensorType_FLOAT32, weight_buf});
212   int bias = cgen.addTensor({{1, 1, 1, 4}, circle::TensorType::TensorType_FLOAT32, bias_buf});
213   int out = cgen.addTensor({{1, 2, 1, 4}, circle::TensorType::TensorType_UINT8});
214   cgen.addOperatorDepthwiseConv2D({{in, weight, bias}, {out}}, circle::Padding_VALID, 1, 1, 2,
215                                   circle::ActivationFunctionType_NONE);
216   cgen.setInputsAndOutputs({in}, {out});
217
218   _context = std::make_unique<GenModelTestContext>(cgen.finish());
219   _context->expectFailModelLoad();
220
221   SUCCEED();
222 }
223
224 // Generate a model for negative test cases
225 CircleBuffer genNegTestDepthwiseConv2DModel(circle::Padding padding, int stride_w, int stride_h,
226                                             int depth_multiplier,
227                                             circle::ActivationFunctionType actfn)
228 {
229   CircleGen cgen;
230   uint32_t ker_buf = cgen.addBuffer(std::vector<uint8_t>{0, 1, 2, 3, 0, 1, 2, 3});
231   uint32_t bias_buf = cgen.addBuffer(std::vector<int32_t>{0, 0});
232   int in = cgen.addTensor({{1, 2, 2, 2}, circle::TensorType_UINT8}, 0.5, 0);
233   int ker = cgen.addTensor({{1, 2, 2, 2}, circle::TensorType_UINT8, ker_buf}, 0.5, 0);
234   int bias = cgen.addTensor({{2}, circle::TensorType_INT32, bias_buf}, 0.25, 0);
235   int out = cgen.addTensor({{1, 1, 1, 2}, circle::TensorType_UINT8}, 1, 0);
236   cgen.addOperatorDepthwiseConv2D({{in, ker, bias}, {out}}, padding, stride_w, stride_h,
237                                   depth_multiplier, actfn, 0, 0);
238   cgen.setInputsAndOutputs({in}, {out});
239   return cgen.finish();
240 }
241
242 template <typename T> struct DepthwiseConv2DQuantTestParam
243 {
244   int stride = 1; // Used for both height and width
245   int input_depth = 1;
246   int depth_multiplier = 1;
247   std::vector<T> ref_output;
248 };
249
250 template <typename T>
251 class DepthwiseConv2DQuantTest
252   : public GenModelTest,
253     public ::testing::WithParamInterface<DepthwiseConv2DQuantTestParam<T>>
254 {
255 };
256
257 using DepthwiseConv2DQuantTestParamU8 = DepthwiseConv2DQuantTestParam<uint8_t>;
258 using DepthwiseConv2DQuantTestU8 = DepthwiseConv2DQuantTest<uint8_t>;
259
260 // Test with different InputDepth and DepthMultiplier. The values are intended to test optimized CPU
261 // kernels.
262 INSTANTIATE_TEST_CASE_P(
263   GenModelTest, DepthwiseConv2DQuantTestU8,
264   ::testing::Values(
265     // Stride == 1
266     DepthwiseConv2DQuantTestParamU8{1, 8, 1, std::vector<uint8_t>{0, 3, 5, 8, 0, 3, 5, 8}},
267     DepthwiseConv2DQuantTestParamU8{1, 4, 2, std::vector<uint8_t>{0, 0, 2, 3, 0, 2, 6, 9}},
268     DepthwiseConv2DQuantTestParamU8{
269       1, 2, 8, std::vector<uint8_t>{0, 1, 2, 3, 0, 1, 2, 3, 0, 2, 4, 6, 0, 2, 4, 6}},
270     DepthwiseConv2DQuantTestParamU8{1, 2, 2, std::vector<uint8_t>{0, 1, 4, 6}},
271     DepthwiseConv2DQuantTestParamU8{1, 2, 1, std::vector<uint8_t>{2, 5}},
272     DepthwiseConv2DQuantTestParamU8{1, 1, 2, std::vector<uint8_t>{2, 4}},
273     DepthwiseConv2DQuantTestParamU8{1, 1, 4, std::vector<uint8_t>{0, 2, 3, 5}},
274     DepthwiseConv2DQuantTestParamU8{1, 4, 1, std::vector<uint8_t>{0, 1, 4, 9}},
275     DepthwiseConv2DQuantTestParamU8{
276       1, 4, 4, std::vector<uint8_t>{0, 0, 0, 0, 0, 1, 2, 3, 0, 2, 4, 6, 0, 3, 6, 9}},
277     DepthwiseConv2DQuantTestParamU8{1, 12, 1,
278                                     std::vector<uint8_t>{0, 3, 7, 12, 0, 4, 7, 12, 0, 4, 9, 16}},
279     // Stride == 2
280     DepthwiseConv2DQuantTestParamU8{2, 4, 1, std::vector<uint8_t>{0, 1, 4, 9}},
281     DepthwiseConv2DQuantTestParamU8{2, 2, 1, std::vector<uint8_t>{2, 5}},
282     DepthwiseConv2DQuantTestParamU8{2, 1, 8, std::vector<uint8_t>{0, 2, 3, 5, 0, 2, 3, 5}},
283     DepthwiseConv2DQuantTestParamU8{2, 1, 32, std::vector<uint8_t>{0, 2, 3, 5, 0, 2, 3, 5, 0, 2, 3,
284                                                                    5, 0, 2, 3, 5, 0, 2, 3, 5, 0, 2,
285                                                                    3, 5, 0, 2, 3, 5, 0, 2, 3, 5}},
286     DepthwiseConv2DQuantTestParamU8{
287       2, 1, 20, std::vector<uint8_t>{0, 2, 3, 5, 0, 2, 3, 5, 0, 2, 3, 5, 0, 2, 3, 5, 0, 2, 3, 5}},
288     DepthwiseConv2DQuantTestParamU8{
289       2, 1, 16, std::vector<uint8_t>{0, 2, 3, 5, 0, 2, 3, 5, 0, 2, 3, 5, 0, 2, 3, 5}},
290     DepthwiseConv2DQuantTestParamU8{2, 8, 1, std::vector<uint8_t>{0, 3, 5, 8, 0, 3, 5, 8}},
291     DepthwiseConv2DQuantTestParamU8{
292       2, 8, 2, std::vector<uint8_t>{0, 3, 5, 8, 0, 3, 5, 8, 0, 3, 5, 8, 0, 3, 5, 8}},
293     DepthwiseConv2DQuantTestParamU8{
294       2, 16, 1, std::vector<uint8_t>{0, 3, 8, 16, 0, 4, 7, 12, 0, 3, 7, 13, 0, 4, 7, 12}}));
295
296 CircleBuffer genDepthwiseConv2DQuantU8Model(int stride, int input_depth, int depth_multiplier)
297 {
298   assert(1 <= stride && stride <= 2);
299   assert(1 <= input_depth && input_depth <= 16);
300   assert(1 <= depth_multiplier && depth_multiplier <= 32);
301
302   const int output_depth = input_depth * depth_multiplier;
303   assert(1 <= output_depth && output_depth <= 32);
304
305   CircleGen cgen;
306   uint32_t ker_buf = cgen.addBuffer(std::vector<uint8_t>{
307     0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1,
308     2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,
309     0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1,
310     2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,
311     0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3});
312   uint32_t bias_buf = cgen.addBuffer(std::vector<int32_t>(output_depth, 0));
313   int in = cgen.addTensor({{1, 2, 2, input_depth}, circle::TensorType_UINT8}, 0.5, 0);
314   int ker = cgen.addTensor({{1, 2, 2, output_depth}, circle::TensorType_UINT8, ker_buf}, 0.5, 0);
315   int bias = cgen.addTensor({{output_depth}, circle::TensorType_INT32, bias_buf}, 0.25, 0);
316   int out = cgen.addTensor({{1, 1, 1, output_depth}, circle::TensorType_UINT8}, 1, 0);
317   cgen.addOperatorDepthwiseConv2D({{in, ker, bias}, {out}}, circle::Padding::Padding_VALID, stride,
318                                   stride, depth_multiplier, circle::ActivationFunctionType_NONE);
319   cgen.setInputsAndOutputs({in}, {out});
320   return cgen.finish();
321 }
322
323 TEST_P(DepthwiseConv2DQuantTestU8, Test)
324 {
325   // Same input is used for all tests but output differs
326   static const std::vector<uint8_t> input64{
327     0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 5, 4, 3, 2, 5, 4, 3, 2, 5, 4, 3, 2, 5, 4, 3, 2,
328     2, 4, 6, 8, 2, 4, 6, 8, 2, 4, 6, 8, 2, 4, 6, 8, 2, 3, 5, 8, 8, 5, 3, 2, 1, 2, 3, 4, 5, 4, 3, 2};
329
330   auto &param = GetParam();
331   _context = std::make_unique<GenModelTestContext>(
332     genDepthwiseConv2DQuantU8Model(param.stride, param.input_depth, param.depth_multiplier));
333   std::vector<uint8_t> ref_input(input64.begin(), input64.begin() + param.input_depth * 4);
334   _context->addTestCase(uniformTCD<uint8_t>({ref_input}, {param.ref_output}));
335   _context->setBackends({"acl_cl", "acl_neon", "cpu"});
336
337   SUCCEED();
338 }
339
340 using DepthwiseConv2DQuantTestParamI8 = DepthwiseConv2DQuantTestParam<int8_t>;
341 using DepthwiseConv2DQuantTestI8 = DepthwiseConv2DQuantTest<int8_t>;
342
343 // Test with different InputDepth and DepthMultiplier. The values are intended to test optimized CPU
344 // kernels.
345 INSTANTIATE_TEST_CASE_P(
346   GenModelTest, DepthwiseConv2DQuantTestI8,
347   ::testing::Values(
348     // Stride == 1
349     DepthwiseConv2DQuantTestParamI8{1, 8, 1, std::vector<int8_t>{0, 3, 5, 8, 0, 3, 5, 8}},
350     DepthwiseConv2DQuantTestParamI8{1, 4, 2, std::vector<int8_t>{0, 0, 2, 3, 0, 2, 6, 9}},
351     DepthwiseConv2DQuantTestParamI8{
352       1, 2, 8, std::vector<int8_t>{0, 1, 2, 3, 0, 1, 2, 3, 0, 2, 4, 6, 0, 2, 4, 6}},
353     DepthwiseConv2DQuantTestParamI8{1, 2, 2, std::vector<int8_t>{0, 1, 4, 6}},
354     DepthwiseConv2DQuantTestParamI8{1, 2, 1, std::vector<int8_t>{2, 5}},
355     DepthwiseConv2DQuantTestParamI8{1, 1, 2, std::vector<int8_t>{2, 4}},
356     DepthwiseConv2DQuantTestParamI8{1, 1, 4, std::vector<int8_t>{0, 2, 3, 5}},
357     DepthwiseConv2DQuantTestParamI8{1, 4, 1, std::vector<int8_t>{0, 1, 4, 9}},
358     DepthwiseConv2DQuantTestParamI8{
359       1, 4, 4, std::vector<int8_t>{0, 0, 0, 0, 0, 1, 2, 3, 0, 2, 4, 6, 0, 3, 6, 9}},
360     DepthwiseConv2DQuantTestParamI8{1, 12, 1,
361                                     std::vector<int8_t>{0, 3, 7, 12, 0, 4, 7, 12, 0, 4, 9, 16}},
362     // Stride == 2
363     DepthwiseConv2DQuantTestParamI8{2, 4, 1, std::vector<int8_t>{0, 1, 4, 9}},
364     DepthwiseConv2DQuantTestParamI8{2, 2, 1, std::vector<int8_t>{2, 5}},
365     DepthwiseConv2DQuantTestParamI8{2, 1, 8, std::vector<int8_t>{0, 2, 3, 5, 0, 2, 3, 5}},
366     DepthwiseConv2DQuantTestParamI8{2, 1, 32, std::vector<int8_t>{0, 2, 3, 5, 0, 2, 3, 5, 0, 2, 3,
367                                                                   5, 0, 2, 3, 5, 0, 2, 3, 5, 0, 2,
368                                                                   3, 5, 0, 2, 3, 5, 0, 2, 3, 5}},
369     DepthwiseConv2DQuantTestParamI8{
370       2, 1, 20, std::vector<int8_t>{0, 2, 3, 5, 0, 2, 3, 5, 0, 2, 3, 5, 0, 2, 3, 5, 0, 2, 3, 5}},
371     DepthwiseConv2DQuantTestParamI8{
372       2, 1, 16, std::vector<int8_t>{0, 2, 3, 5, 0, 2, 3, 5, 0, 2, 3, 5, 0, 2, 3, 5}},
373     DepthwiseConv2DQuantTestParamI8{2, 8, 1, std::vector<int8_t>{0, 3, 5, 8, 0, 3, 5, 8}},
374     DepthwiseConv2DQuantTestParamI8{
375       2, 8, 2, std::vector<int8_t>{0, 3, 5, 8, 0, 3, 5, 8, 0, 3, 5, 8, 0, 3, 5, 8}},
376     DepthwiseConv2DQuantTestParamI8{
377       2, 16, 1, std::vector<int8_t>{0, 3, 8, 16, 0, 4, 7, 12, 0, 3, 7, 13, 0, 4, 7, 12}}));
378
379 CircleBuffer genDepthwiseConv2DQuantI8Model(int stride, int input_depth, int depth_multiplier)
380 {
381   assert(1 <= stride && stride <= 2);
382   assert(1 <= input_depth && input_depth <= 16);
383   assert(1 <= depth_multiplier && depth_multiplier <= 32);
384
385   const int output_depth = input_depth * depth_multiplier;
386   assert(1 <= output_depth && output_depth <= 32);
387
388   CircleGen cgen;
389   uint32_t ker_buf = cgen.addBuffer(std::vector<int8_t>{
390     0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1,
391     2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,
392     0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1,
393     2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,
394     0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3});
395   uint32_t bias_buf = cgen.addBuffer(std::vector<int32_t>(output_depth, 0));
396   int in = cgen.addTensor({{1, 2, 2, input_depth}, circle::TensorType_INT8}, 0.5, 0);
397   int ker = cgen.addTensor({{1, 2, 2, output_depth}, circle::TensorType_INT8, ker_buf}, 0.5, 0);
398   int bias = cgen.addTensor({{output_depth}, circle::TensorType_INT32, bias_buf}, 0.25, 0);
399   int out = cgen.addTensor({{1, 1, 1, output_depth}, circle::TensorType_INT8}, 1, 0);
400   cgen.addOperatorDepthwiseConv2D({{in, ker, bias}, {out}}, circle::Padding::Padding_VALID, stride,
401                                   stride, depth_multiplier, circle::ActivationFunctionType_NONE);
402   cgen.setInputsAndOutputs({in}, {out});
403   return cgen.finish();
404 }
405
406 TEST_P(DepthwiseConv2DQuantTestI8, Test)
407 {
408   // Same input is used for all tests but output differs
409   static const std::vector<int8_t> input64{
410     0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 5, 4, 3, 2, 5, 4, 3, 2, 5, 4, 3, 2, 5, 4, 3, 2,
411     2, 4, 6, 8, 2, 4, 6, 8, 2, 4, 6, 8, 2, 4, 6, 8, 2, 3, 5, 8, 8, 5, 3, 2, 1, 2, 3, 4, 5, 4, 3, 2};
412
413   auto &param = GetParam();
414   _context = std::make_unique<GenModelTestContext>(
415     genDepthwiseConv2DQuantI8Model(param.stride, param.input_depth, param.depth_multiplier));
416   std::vector<int8_t> ref_input(input64.begin(), input64.begin() + param.input_depth * 4);
417   _context->addTestCase(uniformTCD<int8_t>({ref_input}, {param.ref_output}));
418   _context->setBackends({"acl_cl", "acl_neon", "cpu"});
419
420   SUCCEED();
421 }
422
423 TEST_F(GenModelTest, neg_OneOp_DepthwiseConv2D_InvalidPaddingType)
424 {
425   _context = std::make_unique<GenModelTestContext>(genNegTestDepthwiseConv2DModel(
426     static_cast<circle::Padding>(99), 1, 1, 1, circle::ActivationFunctionType_NONE));
427   _context->expectFailModelLoad();
428   _context->setBackends({"acl_cl", "acl_neon", "cpu", "xnnpack"});
429
430   SUCCEED();
431 }
432
433 // TODO add other invalid operation tests like above
434
435 TEST_F(GenModelTest, neg_OneOp_DepthwiseConv2D_I8_NonZero_ZeroPoints)
436 {
437   CircleGen cgen;
438   std::vector<int8_t> weight_data{1, 2, 3, 4, 5, 6, 7, 8};
439   uint32_t weight_buf = cgen.addBuffer(weight_data);
440   std::vector<int32_t> bias_data{0, 2};
441   uint32_t bias_buf = cgen.addBuffer(bias_data);
442   int in = cgen.addTensor({{1, 3, 3, 2}, circle::TensorType::TensorType_INT8}, 0.5, 0);
443   std::vector<float> weight_scales = {0.5, 1};
444   std::vector<int64_t> weight_zeropoints = {0, 10};
445   int weight = cgen.addTensor({{1, 2, 2, 2}, circle::TensorType::TensorType_INT8, weight_buf},
446                               weight_scales, weight_zeropoints);
447   int bias = cgen.addTensor({{1, 1, 1, 2}, circle::TensorType::TensorType_INT32, bias_buf});
448   int out = cgen.addTensor({{1, 2, 2, 2}, circle::TensorType::TensorType_FLOAT32}, 1.0, 0);
449   cgen.addOperatorDepthwiseConv2D({{in, weight, bias}, {out}}, circle::Padding_VALID, 1, 1, 2,
450                                   circle::ActivationFunctionType_NONE);
451   cgen.setInputsAndOutputs({in}, {out});
452   _context = std::make_unique<GenModelTestContext>(cgen.finish());
453   _context->setBackends({"cpu"});
454   _context->expectFailModelLoad();
455
456   SUCCEED();
457 }