• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 Google LLC
2 //
3 // This source code is licensed under the BSD-style license found in the
4 // LICENSE file in the root directory of this source tree.
5 
6 #include <algorithm>
7 #include <array>
8 #include <functional>
9 #include <limits>
10 #include <memory>
11 #include <numeric>
12 #include <random>
13 #include <vector>
14 
15 #include <xnnpack.h>
16 #include <xnnpack/node-type.h>
17 #include <xnnpack/operator.h>
18 #include <xnnpack/requantization.h>
19 #include <xnnpack/subgraph.h>
20 
21 #include <gtest/gtest.h>
22 
23 template <typename T> class GlobalAveragePooling2DTest : public ::testing::Test {
24 protected:
GlobalAveragePooling2DTest()25   GlobalAveragePooling2DTest()
26   {
27     random_device = std::unique_ptr<std::random_device>(new std::random_device());
28     rng = std::mt19937((*random_device)());
29     shape_dist = std::uniform_int_distribution<size_t>(1, XNN_MAX_TENSOR_DIMS);
30     dim_dist = std::uniform_int_distribution<size_t>(1, 9);
31     f32dist = std::uniform_real_distribution<float>();
32     i8dist =
33       std::uniform_int_distribution<int32_t>(std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max());
34     u8dist =
35       std::uniform_int_distribution<int32_t>(std::numeric_limits<uint8_t>::min(), std::numeric_limits<uint8_t>::max());
36     scale_dist = std::uniform_real_distribution<float>(0.1f, 5.0f);
37 
38     input_dims = RandomShape(4);
39     output_dims = {input_dims[0], 1, 1, input_dims[3]};
40     input = std::vector<T>(XNN_EXTRA_BYTES / sizeof(T) + NumElements(input_dims));
41     operator_output = std::vector<T>(NumElements(output_dims));
42     subgraph_output = std::vector<T>(operator_output.size());
43     batch_size = input_dims[0];
44     input_width = input_dims[1] * input_dims[2];
45     channels = input_dims[3];
46   }
47 
RandomShape(size_t num_dims)48   std::vector<size_t> RandomShape(size_t num_dims)
49   {
50     std::vector<size_t> dims(num_dims);
51     std::generate(dims.begin(), dims.end(), [&] { return dim_dist(rng); });
52     return dims;
53   }
54 
NumElements(std::vector<size_t> & dims)55   size_t NumElements(std::vector<size_t>& dims)
56   {
57     return std::accumulate(dims.begin(), dims.end(), size_t(1), std::multiplies<size_t>());
58   }
59 
60   std::unique_ptr<std::random_device> random_device;
61   std::mt19937 rng;
62   std::uniform_int_distribution<size_t> shape_dist;
63   std::uniform_int_distribution<size_t> dim_dist;
64   std::uniform_real_distribution<float> f32dist;
65   std::uniform_real_distribution<float> scale_dist;
66   std::uniform_int_distribution<int32_t> i8dist;
67   std::uniform_int_distribution<int32_t> u8dist;
68 
69   float output_min = -std::numeric_limits<float>::infinity();
70   float output_max = std::numeric_limits<float>::infinity();
71   size_t batch_size;
72   size_t input_width;
73   size_t channels;
74 
75   std::vector<size_t> input_dims;
76   std::vector<size_t> output_dims;
77 
78   std::vector<T> input;
79   std::vector<T> operator_output;
80   std::vector<T> subgraph_output;
81 };
82 
83 using GlobalAveragePooling2DTestQS8 = GlobalAveragePooling2DTest<int8_t>;
84 using GlobalAveragePooling2DTestQU8 = GlobalAveragePooling2DTest<uint8_t>;
85 using GlobalAveragePooling2DTestF32 = GlobalAveragePooling2DTest<float>;
86 
TEST_F(GlobalAveragePooling2DTestQS8,define)87 TEST_F(GlobalAveragePooling2DTestQS8, define)
88 {
89   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
90 
91   xnn_subgraph_t subgraph = nullptr;
92   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(2, /*flags=*/0, &subgraph));
93   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
94 
95   uint32_t input_id = XNN_INVALID_NODE_ID;
96   ASSERT_EQ(
97     xnn_status_success, xnn_define_quantized_tensor_value(
98                           subgraph, xnn_datatype_qint8, 0, 1.0f, input_dims.size(), input_dims.data(), nullptr,
99                           /*external_id=*/0, /*flags=*/0, &input_id));
100   ASSERT_NE(input_id, XNN_INVALID_NODE_ID);
101 
102   uint32_t output_id = XNN_INVALID_NODE_ID;
103   ASSERT_EQ(
104     xnn_status_success, xnn_define_quantized_tensor_value(
105                           subgraph, xnn_datatype_qint8, 0, 1.0f, output_dims.size(), output_dims.data(), nullptr,
106                           /*external_id=*/1, /*flags=*/0, &output_id));
107   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
108 
109   ASSERT_EQ(
110     xnn_status_success,
111     xnn_define_global_average_pooling_2d(subgraph, output_min, output_max, input_id, output_id, /*flags=*/0));
112 
113   ASSERT_EQ(subgraph->num_nodes, 1);
114   const struct xnn_node* node = &subgraph->nodes[0];
115   ASSERT_EQ(node->type, xnn_node_type_global_average_pooling_2d);
116   ASSERT_EQ(node->compute_type, xnn_compute_type_qs8);
117   ASSERT_EQ(node->activation.output_min, output_min);
118   ASSERT_EQ(node->activation.output_max, output_max);
119   ASSERT_EQ(node->num_inputs, 1);
120   ASSERT_EQ(node->inputs[0], input_id);
121   ASSERT_EQ(node->num_outputs, 1);
122   ASSERT_EQ(node->outputs[0], output_id);
123   ASSERT_EQ(node->flags, 0);
124 }
125 
TEST_F(GlobalAveragePooling2DTestQU8,define)126 TEST_F(GlobalAveragePooling2DTestQU8, define)
127 {
128   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
129 
130   xnn_subgraph_t subgraph = nullptr;
131   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(2, /*flags=*/0, &subgraph));
132   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
133 
134   uint32_t input_id = XNN_INVALID_NODE_ID;
135   ASSERT_EQ(
136     xnn_status_success, xnn_define_quantized_tensor_value(
137                           subgraph, xnn_datatype_quint8, 0, 1.0f, input_dims.size(), input_dims.data(), nullptr,
138                           /*external_id=*/0, /*flags=*/0, &input_id));
139   ASSERT_NE(input_id, XNN_INVALID_NODE_ID);
140 
141   uint32_t output_id = XNN_INVALID_NODE_ID;
142   ASSERT_EQ(
143     xnn_status_success, xnn_define_quantized_tensor_value(
144                           subgraph, xnn_datatype_quint8, 0, 1.0f, output_dims.size(), output_dims.data(), nullptr,
145                           /*external_id=*/1, /*flags=*/0, &output_id));
146   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
147 
148   ASSERT_EQ(
149     xnn_status_success,
150     xnn_define_global_average_pooling_2d(subgraph, output_min, output_max, input_id, output_id, /*flags=*/0));
151 
152   ASSERT_EQ(subgraph->num_nodes, 1);
153   const struct xnn_node* node = &subgraph->nodes[0];
154   ASSERT_EQ(node->type, xnn_node_type_global_average_pooling_2d);
155   ASSERT_EQ(node->compute_type, xnn_compute_type_qu8);
156   ASSERT_EQ(node->activation.output_min, output_min);
157   ASSERT_EQ(node->activation.output_max, output_max);
158   ASSERT_EQ(node->num_inputs, 1);
159   ASSERT_EQ(node->inputs[0], input_id);
160   ASSERT_EQ(node->num_outputs, 1);
161   ASSERT_EQ(node->outputs[0], output_id);
162   ASSERT_EQ(node->flags, 0);
163 }
164 
TEST_F(GlobalAveragePooling2DTestF32,define)165 TEST_F(GlobalAveragePooling2DTestF32, define)
166 {
167   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
168 
169   xnn_subgraph_t subgraph = nullptr;
170   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(2, /*flags=*/0, &subgraph));
171   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
172 
173   uint32_t input_id = XNN_INVALID_NODE_ID;
174   ASSERT_EQ(
175     xnn_status_success, xnn_define_tensor_value(
176                           subgraph, xnn_datatype_fp32, input_dims.size(), input_dims.data(), nullptr,
177                           /*external_id=*/0, /*flags=*/0, &input_id));
178   ASSERT_NE(input_id, XNN_INVALID_NODE_ID);
179 
180   uint32_t output_id = XNN_INVALID_NODE_ID;
181   ASSERT_EQ(
182     xnn_status_success, xnn_define_tensor_value(
183                           subgraph, xnn_datatype_fp32, output_dims.size(), output_dims.data(), nullptr,
184                           /*external_id=*/1, /*flags=*/0, &output_id));
185   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
186 
187   ASSERT_EQ(
188     xnn_status_success,
189     xnn_define_global_average_pooling_2d(subgraph, output_min, output_max, input_id, output_id, /*flags=*/0));
190 
191   ASSERT_EQ(subgraph->num_nodes, 1);
192   const struct xnn_node* node = &subgraph->nodes[0];
193   ASSERT_EQ(node->type, xnn_node_type_global_average_pooling_2d);
194   ASSERT_EQ(node->compute_type, xnn_compute_type_fp32);
195   ASSERT_EQ(node->activation.output_min, output_min);
196   ASSERT_EQ(node->activation.output_max, output_max);
197   ASSERT_EQ(node->num_inputs, 1);
198   ASSERT_EQ(node->inputs[0], input_id);
199   ASSERT_EQ(node->num_outputs, 1);
200   ASSERT_EQ(node->outputs[0], output_id);
201   ASSERT_EQ(node->flags, 0);
202 }
203 
TEST_F(GlobalAveragePooling2DTestQS8,matches_operator_api)204 TEST_F(GlobalAveragePooling2DTestQS8, matches_operator_api)
205 {
206   const int32_t input_zero_point = i8dist(rng);
207   const int32_t output_zero_point = i8dist(rng);
208   const float input_scale = scale_dist(rng);
209   const float output_scale = scale_dist(rng);
210   const int8_t quantized_output_min = xnn_qs8_quantize(output_min, output_scale, output_zero_point);
211   const int8_t quantized_output_max = xnn_qs8_quantize(output_max, output_scale, output_zero_point);
212 
213   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
214 
215   xnn_operator_t op = nullptr;
216   const xnn_status status = xnn_create_global_average_pooling_nwc_qs8(
217     channels, channels, channels, input_zero_point, input_scale, output_zero_point, output_scale, quantized_output_min,
218     quantized_output_max,
219     /*flags=*/0, &op);
220   std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_op(op, xnn_delete_operator);
221 
222   if (status == xnn_status_unsupported_hardware) {
223     GTEST_SKIP();
224   }
225 
226   ASSERT_EQ(xnn_status_success, status);
227   ASSERT_NE(nullptr, op);
228   ASSERT_EQ(
229     xnn_status_success, xnn_setup_global_average_pooling_nwc_qs8(
230                           op, batch_size, input_width, input.data(), operator_output.data(),
231                           /*threadpool=*/nullptr));
232 
233   ASSERT_EQ(xnn_status_success, xnn_run_operator(op, /*threadpool=*/nullptr));
234 
235   xnn_subgraph_t subgraph = nullptr;
236   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(2, /*flags=*/0, &subgraph));
237   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
238 
239   uint32_t input_id = XNN_INVALID_NODE_ID;
240   ASSERT_EQ(
241     xnn_status_success,
242     xnn_define_quantized_tensor_value(
243       subgraph, xnn_datatype_qint8, input_zero_point, input_scale, input_dims.size(), input_dims.data(), nullptr,
244       /*external_id=*/0, XNN_VALUE_FLAG_EXTERNAL_INPUT, &input_id));
245   ASSERT_NE(input_id, XNN_INVALID_NODE_ID);
246 
247   uint32_t output_id = XNN_INVALID_NODE_ID;
248   ASSERT_EQ(
249     xnn_status_success,
250     xnn_define_quantized_tensor_value(
251       subgraph, xnn_datatype_qint8, output_zero_point, output_scale, output_dims.size(), output_dims.data(), nullptr,
252       /*external_id=*/1, XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id));
253   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
254 
255   ASSERT_EQ(
256     xnn_status_success,
257     xnn_define_global_average_pooling_2d(subgraph, output_min, output_max, input_id, output_id, /*flags=*/0));
258 
259   xnn_runtime_t runtime = nullptr;
260   ASSERT_EQ(xnn_status_success, xnn_create_runtime_v3(subgraph, nullptr, nullptr, /*flags=*/0, &runtime));
261   ASSERT_NE(nullptr, runtime);
262   std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime(runtime, xnn_delete_runtime);
263   std::array<xnn_external_value, 2> external = {
264     xnn_external_value{input_id, input.data()}, xnn_external_value{output_id, subgraph_output.data()}};
265   ASSERT_EQ(xnn_status_success, xnn_setup_runtime(runtime, external.size(), external.data()));
266   ASSERT_EQ(xnn_status_success, xnn_invoke_runtime(runtime));
267 
268   ASSERT_EQ(subgraph_output, operator_output);
269 }
270 
TEST_F(GlobalAveragePooling2DTestQU8,matches_operator_api)271 TEST_F(GlobalAveragePooling2DTestQU8, matches_operator_api)
272 {
273   const int32_t input_zero_point = u8dist(rng);
274   const int32_t output_zero_point = u8dist(rng);
275   const float input_scale = scale_dist(rng);
276   const float output_scale = scale_dist(rng);
277   const uint8_t quantized_output_min = xnn_qu8_quantize(output_min, output_scale, output_zero_point);
278   const uint8_t quantized_output_max = xnn_qu8_quantize(output_max, output_scale, output_zero_point);
279 
280   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
281 
282   xnn_operator_t op = nullptr;
283   const xnn_status status = xnn_create_global_average_pooling_nwc_qu8(
284     channels, channels, channels, input_zero_point, input_scale, output_zero_point, output_scale, quantized_output_min,
285     quantized_output_max,
286     /*flags=*/0, &op);
287   std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_op(op, xnn_delete_operator);
288 
289   if (status == xnn_status_unsupported_hardware) {
290     GTEST_SKIP();
291   }
292 
293   ASSERT_EQ(xnn_status_success, status);
294   ASSERT_NE(nullptr, op);
295   ASSERT_EQ(
296     xnn_status_success, xnn_setup_global_average_pooling_nwc_qu8(
297                           op, batch_size, input_width, input.data(), operator_output.data(),
298                           /*threadpool=*/nullptr));
299 
300   ASSERT_EQ(xnn_status_success, xnn_run_operator(op, /*threadpool=*/nullptr));
301 
302   xnn_subgraph_t subgraph = nullptr;
303   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(2, /*flags=*/0, &subgraph));
304   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
305 
306   uint32_t input_id = XNN_INVALID_NODE_ID;
307   ASSERT_EQ(
308     xnn_status_success,
309     xnn_define_quantized_tensor_value(
310       subgraph, xnn_datatype_quint8, input_zero_point, input_scale, input_dims.size(), input_dims.data(), nullptr,
311       /*external_id=*/0, XNN_VALUE_FLAG_EXTERNAL_INPUT, &input_id));
312   ASSERT_NE(input_id, XNN_INVALID_NODE_ID);
313 
314   uint32_t output_id = XNN_INVALID_NODE_ID;
315   ASSERT_EQ(
316     xnn_status_success,
317     xnn_define_quantized_tensor_value(
318       subgraph, xnn_datatype_quint8, output_zero_point, output_scale, output_dims.size(), output_dims.data(), nullptr,
319       /*external_id=*/1, XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id));
320   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
321 
322   ASSERT_EQ(
323     xnn_status_success,
324     xnn_define_global_average_pooling_2d(subgraph, output_min, output_max, input_id, output_id, /*flags=*/0));
325 
326   xnn_runtime_t runtime = nullptr;
327   ASSERT_EQ(xnn_status_success, xnn_create_runtime_v3(subgraph, nullptr, nullptr, /*flags=*/0, &runtime));
328   ASSERT_NE(nullptr, runtime);
329   std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime(runtime, xnn_delete_runtime);
330   std::array<xnn_external_value, 2> external = {
331     xnn_external_value{input_id, input.data()}, xnn_external_value{output_id, subgraph_output.data()}};
332   ASSERT_EQ(xnn_status_success, xnn_setup_runtime(runtime, external.size(), external.data()));
333   ASSERT_EQ(xnn_status_success, xnn_invoke_runtime(runtime));
334 
335   ASSERT_EQ(subgraph_output, operator_output);
336 }
337 
TEST_F(GlobalAveragePooling2DTestF32,matches_operator_api)338 TEST_F(GlobalAveragePooling2DTestF32, matches_operator_api)
339 {
340   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
341 
342   xnn_operator_t op = nullptr;
343 
344   std::generate(input.begin(), input.end(), [&]() { return f32dist(rng); });
345   std::fill(operator_output.begin(), operator_output.end(), nanf(""));
346   std::fill(subgraph_output.begin(), subgraph_output.end(), nanf(""));
347 
348   // Call operator API.
349   const xnn_status status = xnn_create_global_average_pooling_nwc_f32(
350     channels, channels, channels, output_min, output_max,
351     /*flags=*/0, &op);
352   std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_op(op, xnn_delete_operator);
353 
354   if (status == xnn_status_unsupported_hardware) {
355     GTEST_SKIP();
356   }
357 
358   ASSERT_EQ(xnn_status_success, status);
359   ASSERT_NE(nullptr, op);
360   ASSERT_EQ(
361     xnn_status_success, xnn_setup_global_average_pooling_nwc_f32(
362                           op, batch_size, input_width, input.data(), operator_output.data(),
363                           /*threadpool=*/nullptr));
364 
365   ASSERT_EQ(xnn_status_success, xnn_run_operator(op, /*threadpool=*/nullptr));
366 
367   // Call subgraph API.
368   xnn_subgraph_t subgraph = nullptr;
369   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(2, /*flags=*/0, &subgraph));
370   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
371 
372   uint32_t input_id = XNN_INVALID_NODE_ID;
373   ASSERT_EQ(
374     xnn_status_success, xnn_define_tensor_value(
375                           subgraph, xnn_datatype_fp32, input_dims.size(), input_dims.data(), nullptr,
376                           /*external_id=*/0, XNN_VALUE_FLAG_EXTERNAL_INPUT, &input_id));
377   ASSERT_NE(input_id, XNN_INVALID_NODE_ID);
378 
379   uint32_t output_id = XNN_INVALID_NODE_ID;
380   ASSERT_EQ(
381     xnn_status_success, xnn_define_tensor_value(
382                           subgraph, xnn_datatype_fp32, output_dims.size(), output_dims.data(), nullptr,
383                           /*external_id=*/1, XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id));
384   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
385   ASSERT_EQ(
386     xnn_status_success,
387     xnn_define_global_average_pooling_2d(subgraph, output_min, output_max, input_id, output_id, /*flags=*/0));
388 
389   xnn_runtime_t runtime = nullptr;
390   ASSERT_EQ(xnn_status_success, xnn_create_runtime_v3(subgraph, nullptr, nullptr, /*flags=*/0, &runtime));
391   ASSERT_NE(nullptr, runtime);
392   std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime(runtime, xnn_delete_runtime);
393   std::array<xnn_external_value, 2> external = {
394     xnn_external_value{input_id, input.data()}, xnn_external_value{output_id, subgraph_output.data()}};
395   ASSERT_EQ(xnn_status_success, xnn_setup_runtime(runtime, external.size(), external.data()));
396   ASSERT_EQ(xnn_status_success, xnn_invoke_runtime(runtime));
397 
398   ASSERT_EQ(subgraph_output, operator_output);
399 }
400