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