xref: /aosp_15_r20/external/XNNPACK/test/unpooling-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/requantization.h>
19 #include <xnnpack/subgraph.h>
20 
21 #include <gtest/gtest.h>
22 
23 template <class T, class BiasType = T> class Unpooling2DTestBase : public ::testing::Test {
24 protected:
Unpooling2DTestBase()25   Unpooling2DTestBase()
26   {
27     random_device = std::unique_ptr<std::random_device>(new std::random_device());
28     rng = std::mt19937((*random_device)());
29     input_size_dist = std::uniform_int_distribution<uint32_t>(10, 15);
30     kernel_size_dist = std::uniform_int_distribution<uint32_t>(1, 5);
31     stride_dist = std::uniform_int_distribution<uint32_t>(1, 3);
32     f32dist = std::uniform_real_distribution<float>(0.1f, 1.0f);
33     scale_dist = std::uniform_real_distribution<float>(1.0f, 5.0f);
34     i32dist = std::uniform_int_distribution<int32_t>(-10000, 10000);
35     u32dist = std::uniform_int_distribution<uint32_t>();
36 
37     batch_size = input_size_dist(rng);
38     input_height = input_size_dist(rng);
39     input_width = input_size_dist(rng);
40     pooling_height = 2;
41     pooling_width = 2;
42     channels = input_size_dist(rng);
43     output_height = xnn_compute_unpooling_output_dimension(input_height, padding_top + padding_bottom, pooling_height);
44     output_width = xnn_compute_unpooling_output_dimension(input_width, padding_left + padding_right, pooling_width);
45 
46     index_dist = std::uniform_int_distribution<uint32_t>(0, pooling_height * pooling_width - 1);
47 
48     input_value_dims = {{batch_size, input_height, input_width, channels}};
49     input_index_dims = {{batch_size, input_height, input_width, channels}};
50     output_dims = {{batch_size, output_height, output_width, channels}};
51 
52     input = std::vector<T>(XNN_EXTRA_BYTES / sizeof(T) + batch_size * input_height * input_width * channels);
53     input_index = std::vector<T>(batch_size * input_height * input_width * channels);
54     operator_output = std::vector<T>(batch_size * output_height * output_width * channels);
55     subgraph_output = std::vector<T>(batch_size * output_height * output_width * channels);
56   }
57 
58   std::unique_ptr<std::random_device> random_device;
59   std::mt19937 rng;
60   std::uniform_int_distribution<uint32_t> input_size_dist;
61   std::uniform_int_distribution<uint32_t> kernel_size_dist;
62   std::uniform_int_distribution<uint32_t> stride_dist;
63   std::uniform_int_distribution<int32_t> i32dist;
64   std::uniform_int_distribution<uint32_t> u32dist;
65   std::uniform_int_distribution<uint32_t> index_dist;
66   std::uniform_real_distribution<float> f32dist;
67   std::uniform_real_distribution<float> scale_dist;
68 
69   const uint32_t padding_top = 0;
70   const uint32_t padding_right = 0;
71   const uint32_t padding_bottom = 0;
72   const uint32_t padding_left = 0;
73   uint32_t batch_size;
74   uint32_t input_height;
75   uint32_t input_width;
76   uint32_t kernel_height;
77   uint32_t kernel_width;
78   uint32_t pooling_height;
79   uint32_t pooling_width;
80   uint32_t channels;
81   uint32_t output_height;
82   uint32_t output_width;
83 
84   std::array<size_t, 4> input_value_dims;
85   std::array<size_t, 4> input_index_dims;
86   std::array<size_t, 4> output_dims;
87 
88   std::vector<T> input;
89   std::vector<T> input_index;
90   std::vector<T> operator_output;
91   std::vector<T> subgraph_output;
92 };
93 
94 using Unpooling2DTestX32 = Unpooling2DTestBase<uint32_t>;
95 
TEST_F(Unpooling2DTestX32,define)96 TEST_F(Unpooling2DTestX32, define)
97 {
98   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
99 
100   xnn_subgraph_t subgraph = nullptr;
101   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(2, /*flags=*/0, &subgraph));
102   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
103 
104   uint32_t input_value_id = XNN_INVALID_NODE_ID;
105   ASSERT_EQ(
106     xnn_status_success, xnn_define_tensor_value(
107                           subgraph, xnn_datatype_fp32, input_value_dims.size(), input_value_dims.data(), nullptr,
108                           /*external_id=*/0, XNN_VALUE_FLAG_EXTERNAL_INPUT, &input_value_id));
109   ASSERT_NE(input_value_id, XNN_INVALID_NODE_ID);
110 
111   uint32_t input_index_id = XNN_INVALID_NODE_ID;
112   ASSERT_EQ(
113     xnn_status_success, xnn_define_tensor_value(
114                           subgraph, xnn_datatype_fp32, input_index_dims.size(), input_index_dims.data(),
115                           input_index.data(), XNN_INVALID_VALUE_ID, /*flags=*/0, &input_index_id));
116 
117   uint32_t output_id = XNN_INVALID_NODE_ID;
118   ASSERT_EQ(
119     xnn_status_success, xnn_define_tensor_value(
120                           subgraph, xnn_datatype_fp32, output_dims.size(), output_dims.data(), nullptr,
121                           /*external_id=*/1, XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id));
122   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
123 
124   ASSERT_EQ(
125     xnn_status_success, xnn_define_unpooling_2d(
126                           subgraph, padding_top, padding_right, padding_bottom, padding_left, pooling_height,
127                           pooling_width, input_value_id, input_index_id, output_id,
128                           /*flags=*/0));
129 
130   ASSERT_EQ(subgraph->num_nodes, 1);
131   const struct xnn_node* node = &subgraph->nodes[0];
132   ASSERT_EQ(node->type, xnn_node_type_unpooling_2d);
133   ASSERT_EQ(node->compute_type, xnn_compute_type_fp32);
134   ASSERT_EQ(node->params.pooling_2d.padding_top, padding_top);
135   ASSERT_EQ(node->params.pooling_2d.padding_right, padding_right);
136   ASSERT_EQ(node->params.pooling_2d.padding_bottom, padding_bottom);
137   ASSERT_EQ(node->params.pooling_2d.padding_left, padding_left);
138   ASSERT_EQ(node->params.pooling_2d.pooling_height, pooling_height);
139   ASSERT_EQ(node->params.pooling_2d.pooling_width, pooling_width);
140   ASSERT_EQ(node->num_inputs, 2);
141   ASSERT_EQ(node->inputs[0], input_value_id);
142   ASSERT_EQ(node->inputs[1], input_index_id);
143   ASSERT_EQ(node->num_outputs, 1);
144   ASSERT_EQ(node->outputs[0], output_id);
145   ASSERT_EQ(node->flags, 0);
146 }
147 
TEST_F(Unpooling2DTestX32,matches_operator_api)148 TEST_F(Unpooling2DTestX32, matches_operator_api)
149 {
150   xnn_operator_t op = nullptr;
151 
152   std::generate(input.begin(), input.end(), [&]() { return u32dist(rng); });
153   std::generate(input_index.begin(), input_index.end(), [&]() { return index_dist(rng); });
154   std::generate(operator_output.begin(), operator_output.end(), [&]() { return u32dist(rng); });
155   std::generate(subgraph_output.begin(), subgraph_output.end(), [&]() { return u32dist(rng); });
156 
157   ASSERT_EQ(xnn_status_success, xnn_initialize(/*allocator=*/nullptr));
158 
159   // Call operator API.
160   const xnn_status status = xnn_create_unpooling2d_nhwc_x32(
161     padding_top, padding_right, padding_bottom, padding_left, pooling_height, pooling_width, channels, channels,
162     channels, /*flags=*/0, &op);
163   std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_op(op, xnn_delete_operator);
164 
165   if (status == xnn_status_unsupported_hardware) {
166     GTEST_SKIP();
167   }
168 
169   ASSERT_EQ(xnn_status_success, status);
170   ASSERT_NE(nullptr, op);
171   ASSERT_EQ(
172     xnn_status_success,
173     xnn_setup_unpooling2d_nhwc_x32(
174       op, batch_size, input_height, input_width, input.data(), input_index.data(), operator_output.data(),
175       /*threadpool=*/nullptr));
176 
177   ASSERT_EQ(xnn_status_success, xnn_run_operator(op, /*threadpool=*/nullptr));
178 
179   // Call subgraph API.
180   xnn_subgraph_t subgraph = nullptr;
181   ASSERT_EQ(xnn_status_success, xnn_create_subgraph(2, /*flags=*/0, &subgraph));
182   std::unique_ptr<xnn_subgraph, decltype(&xnn_delete_subgraph)> auto_subgraph(subgraph, xnn_delete_subgraph);
183 
184   uint32_t input_value_id = XNN_INVALID_NODE_ID;
185   ASSERT_EQ(
186     xnn_status_success, xnn_define_tensor_value(
187                           subgraph, xnn_datatype_fp32, input_value_dims.size(), input_value_dims.data(), nullptr,
188                           /*external_id=*/0, XNN_VALUE_FLAG_EXTERNAL_INPUT, &input_value_id));
189   ASSERT_NE(input_value_id, XNN_INVALID_NODE_ID);
190 
191   uint32_t input_index_id = XNN_INVALID_NODE_ID;
192   ASSERT_EQ(
193     xnn_status_success, xnn_define_tensor_value(
194                           subgraph, xnn_datatype_fp32, input_index_dims.size(), input_index_dims.data(),
195                           input_index.data(), XNN_INVALID_VALUE_ID, /*flags=*/0, &input_index_id));
196 
197   uint32_t output_id = XNN_INVALID_NODE_ID;
198   ASSERT_EQ(
199     xnn_status_success, xnn_define_tensor_value(
200                           subgraph, xnn_datatype_fp32, output_dims.size(), output_dims.data(), nullptr,
201                           /*external_id=*/1, XNN_VALUE_FLAG_EXTERNAL_OUTPUT, &output_id));
202   ASSERT_NE(output_id, XNN_INVALID_NODE_ID);
203 
204   ASSERT_EQ(
205     xnn_status_success, xnn_define_unpooling_2d(
206                           subgraph, padding_top, padding_right, padding_bottom, padding_left, pooling_height,
207                           pooling_width, input_value_id, input_index_id, output_id,
208                           /*flags=*/0));
209 
210   xnn_runtime_t runtime = nullptr;
211   ASSERT_EQ(xnn_status_success, xnn_create_runtime_v3(subgraph, nullptr, nullptr, /*flags=*/0, &runtime));
212   ASSERT_NE(nullptr, runtime);
213   std::unique_ptr<xnn_runtime, decltype(&xnn_delete_runtime)> auto_runtime(runtime, xnn_delete_runtime);
214   std::array<xnn_external_value, 2> external = {
215     xnn_external_value{input_value_id, input.data()}, xnn_external_value{output_id, subgraph_output.data()}};
216   ASSERT_EQ(xnn_status_success, xnn_setup_runtime(runtime, external.size(), external.data()));
217   ASSERT_EQ(xnn_status_success, xnn_invoke_runtime(runtime));
218 
219   ASSERT_EQ(subgraph_output, operator_output);
220 }
221