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