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