blob: b6fe2ae05a62895ce49a62ccd442268e80439160 [file] [log] [blame]
XNNPACK Teamb455b122019-09-27 18:10:33 -07001// Copyright 2019 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#pragma once
7
8#include <gtest/gtest.h>
9
10#include <algorithm>
11#include <cassert>
12#include <cstddef>
13#include <cstdlib>
14#include <functional>
15#include <limits>
16#include <random>
17#include <vector>
18
19#include <xnnpack.h>
20
21
22class UnpoolingOperatorTester {
23 public:
24 inline UnpoolingOperatorTester& padding(uint32_t padding) {
25 this->padding_top_ = padding;
26 this->padding_right_ = padding;
27 this->padding_bottom_ = padding;
28 this->padding_left_ = padding;
29 return *this;
30 }
31
32 inline UnpoolingOperatorTester& padding(uint32_t padding_height, uint32_t padding_width) {
33 this->padding_top_ = padding_height;
34 this->padding_right_ = padding_width;
35 this->padding_bottom_ = padding_height;
36 this->padding_left_ = padding_width;
37 return *this;
38 }
39
40 inline UnpoolingOperatorTester& padding_height(uint32_t padding_height) {
41 this->padding_top_ = padding_height;
42 this->padding_bottom_ = padding_height;
43 return *this;
44 }
45
46 inline UnpoolingOperatorTester& padding_width(uint32_t padding_width) {
47 this->padding_right_ = padding_width;
48 this->padding_left_ = padding_width;
49 return *this;
50 }
51
52 inline UnpoolingOperatorTester& padding_top(uint32_t padding_top) {
53 this->padding_top_ = padding_top;
54 return *this;
55 }
56
57 inline uint32_t padding_top() const {
58 return this->padding_top_;
59 }
60
61 inline UnpoolingOperatorTester& padding_right(uint32_t padding_right) {
62 this->padding_right_ = padding_right;
63 return *this;
64 }
65
66 inline uint32_t padding_right() const {
67 return this->padding_right_;
68 }
69
70 inline UnpoolingOperatorTester& padding_bottom(uint32_t padding_bottom) {
71 this->padding_bottom_ = padding_bottom;
72 return *this;
73 }
74
75 inline uint32_t padding_bottom() const {
76 return this->padding_bottom_;
77 }
78
79 inline UnpoolingOperatorTester& padding_left(uint32_t padding_left) {
80 this->padding_left_ = padding_left;
81 return *this;
82 }
83
84 inline uint32_t padding_left() const {
85 return this->padding_left_;
86 }
87
88 inline UnpoolingOperatorTester& input_size(size_t input_height, size_t input_width) {
89 assert(input_height >= 1);
90 assert(input_width >= 1);
91 this->input_height_ = input_height;
92 this->input_width_ = input_width;
93 return *this;
94 }
95
96 inline UnpoolingOperatorTester& input_height(size_t input_height) {
97 assert(input_height >= 1);
98 this->input_height_ = input_height;
99 return *this;
100 }
101
102 inline size_t input_height() const {
103 return this->input_height_;
104 }
105
106 inline UnpoolingOperatorTester& input_width(size_t input_width) {
107 assert(input_width >= 1);
108 this->input_width_ = input_width;
109 return *this;
110 }
111
112 inline size_t input_width() const {
113 return this->input_width_;
114 }
115
116 inline UnpoolingOperatorTester& channels(size_t channels) {
117 assert(channels != 0);
118 this->channels_ = channels;
119 return *this;
120 }
121
122 inline size_t channels() const {
123 return this->channels_;
124 }
125
126 inline UnpoolingOperatorTester& batch_size(size_t batch_size) {
127 assert(batch_size != 0);
128 this->batch_size_ = batch_size;
129 return *this;
130 }
131
132 inline size_t batch_size() const {
133 return this->batch_size_;
134 }
135
136 inline UnpoolingOperatorTester& pooling_size(uint32_t pooling_size) {
137 assert(pooling_size >= 1);
138 this->pooling_height_ = pooling_size;
139 this->pooling_width_ = pooling_size;
140 return *this;
141 }
142
143 inline UnpoolingOperatorTester& pooling_size(uint32_t pooling_height, uint32_t pooling_width) {
144 assert(pooling_height >= 1);
145 assert(pooling_width >= 1);
146 this->pooling_height_ = pooling_height;
147 this->pooling_width_ = pooling_width;
148 return *this;
149 }
150
151 inline UnpoolingOperatorTester& pooling_height(uint32_t pooling_height) {
152 assert(pooling_height >= 1);
153 this->pooling_height_ = pooling_height;
154 return *this;
155 }
156
157 inline uint32_t pooling_height() const {
158 return this->pooling_height_;
159 }
160
161 inline UnpoolingOperatorTester& pooling_width(uint32_t pooling_width) {
162 assert(pooling_width >= 1);
163 this->pooling_width_ = pooling_width;
164 return *this;
165 }
166
167 inline uint32_t pooling_width() const {
168 return this->pooling_width_;
169 }
170
171 inline size_t output_height() const {
172 const size_t padding_height = padding_top() + padding_bottom();
173 return std::max<size_t>(input_height() * pooling_height(), padding_height) - padding_height;
174 }
175
176 inline size_t output_width() const {
177 const size_t padding_width = padding_left() + padding_right();
178 return std::max<size_t>(input_width() * pooling_width(), padding_width) - padding_width;
179 }
180
181 inline UnpoolingOperatorTester& input_pixel_stride(size_t input_pixel_stride) {
182 assert(input_pixel_stride != 0);
183 this->input_pixel_stride_ = input_pixel_stride;
184 return *this;
185 }
186
187 inline size_t input_pixel_stride() const {
188 if (this->input_pixel_stride_ == 0) {
189 return channels();
190 } else {
191 assert(this->input_pixel_stride_ >= channels());
192 return this->input_pixel_stride_;
193 }
194 }
195
196 inline UnpoolingOperatorTester& output_pixel_stride(size_t output_pixel_stride) {
197 assert(output_pixel_stride != 0);
198 this->output_pixel_stride_ = output_pixel_stride;
199 return *this;
200 }
201
202 inline size_t output_pixel_stride() const {
203 if (this->output_pixel_stride_ == 0) {
204 return channels();
205 } else {
206 assert(this->output_pixel_stride_ >= channels());
207 return this->output_pixel_stride_;
208 }
209 }
210
211 inline UnpoolingOperatorTester& next_input_size(uint32_t next_input_height, uint32_t next_input_width) {
212 assert(next_input_height >= 1);
213 assert(next_input_width >= 1);
214 this->next_input_height_ = next_input_height;
215 this->next_input_width_ = next_input_width;
216 return *this;
217 }
218
219 inline UnpoolingOperatorTester& next_input_height(uint32_t next_input_height) {
220 assert(next_input_height >= 1);
221 this->next_input_height_ = next_input_height;
222 return *this;
223 }
224
225 inline uint32_t next_input_height() const {
226 if (this->next_input_height_ == 0) {
227 return input_height();
228 } else {
229 return this->next_input_height_;
230 }
231 }
232
233 inline UnpoolingOperatorTester& next_input_width(uint32_t next_input_width) {
234 assert(next_input_width >= 1);
235 this->next_input_width_ = next_input_width;
236 return *this;
237 }
238
239 inline uint32_t next_input_width() const {
240 if (this->next_input_width_ == 0) {
241 return input_width();
242 } else {
243 return this->next_input_width_;
244 }
245 }
246
247 inline size_t next_output_height() const {
248 const size_t padding_height = padding_top() + padding_bottom();
249 return std::max<size_t>(next_input_height() * pooling_height(), padding_height) - padding_height;
250 }
251
252 inline size_t next_output_width() const {
253 const size_t padding_width = padding_left() + padding_right();
254 return std::max<size_t>(next_input_width() * pooling_width(), padding_width) - padding_width;
255 }
256
257 inline UnpoolingOperatorTester& next_batch_size(size_t next_batch_size) {
258 assert(next_batch_size >= 1);
259 this->next_batch_size_ = next_batch_size;
260 return *this;
261 }
262
263 inline size_t next_batch_size() const {
264 if (this->next_batch_size_ == 0) {
265 return batch_size();
266 } else {
267 return this->next_batch_size_;
268 }
269 }
270
271 inline UnpoolingOperatorTester& iterations(size_t iterations) {
272 this->iterations_ = iterations;
273 return *this;
274 }
275
276 inline size_t iterations() const {
277 return this->iterations_;
278 }
279
280 void TestX32() const {
281 std::random_device random_device;
282 auto rng = std::mt19937(random_device());
283 auto u32rng = std::bind(std::uniform_int_distribution<uint32_t>(), std::ref(rng));
284 auto idx_rng = std::bind(std::uniform_int_distribution<uint32_t>(0, pooling_height() * pooling_width() - 1), std::ref(rng));
285
286 std::vector<uint32_t> input((batch_size() * input_height() * input_width() - 1) * input_pixel_stride() + channels());
287 std::vector<uint32_t> index(batch_size() * input_height() * input_width() * channels());
288 std::vector<uint32_t> output((batch_size() * output_height() * output_width() - 1) * output_pixel_stride() + channels());
289 std::vector<uint32_t> output_ref(batch_size() * output_height() * output_width() * channels());
290 for (size_t iteration = 0; iteration < iterations(); iteration++) {
291 std::generate(input.begin(), input.end(), std::ref(u32rng));
292 std::generate(index.begin(), index.end(), std::ref(idx_rng));
293 std::generate(output.begin(), output.end(), std::ref(u32rng));
294
295 // Compute reference results.
296 std::fill(output_ref.begin(), output_ref.end(), 0);
297 for (size_t i = 0; i < batch_size(); i++) {
298 for (size_t iy = 0; iy < input_height(); iy++) {
299 for (size_t ix = 0; ix < input_width(); ix++) {
300 for (size_t c = 0; c < channels(); c++) {
301 const uint32_t pooling_index = index[((i * input_height() + iy) * input_width() + ix) * channels() + c];
302 const uint32_t py = pooling_index % pooling_height();
303 const uint32_t px = pooling_index / pooling_height();
304 const size_t oy = std::min(std::max<size_t>(iy * pooling_height() + py, padding_top()) - padding_top(), output_height() - 1);
305 const size_t ox = std::min(std::max<size_t>(ix * pooling_width() + px, padding_left()) - padding_left(), output_width() - 1);
306 output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] =
307 input[((i * input_height() + iy) * input_width() + ix) * input_pixel_stride() + c];
308 }
309 }
310 }
311 }
312
313 // Create, setup, run, and destroy Unpooling operator.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800314 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700315 xnn_operator_t unpooling_op = nullptr;
316
317 ASSERT_EQ(xnn_status_success,
318 xnn_create_unpooling2d_nhwc_x32(
319 padding_top(), padding_right(), padding_bottom(), padding_left(),
320 pooling_height(), pooling_width(),
321 channels(), input_pixel_stride(), output_pixel_stride(),
322 0, &unpooling_op));
323 ASSERT_NE(nullptr, unpooling_op);
324
325 // Smart pointer to automatically delete unpooling_op.
326 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_unpooling_op(unpooling_op, xnn_delete_operator);
327
328 ASSERT_EQ(xnn_status_success,
329 xnn_setup_unpooling2d_nhwc_x32(
330 unpooling_op,
331 batch_size(), input_height(), input_width(),
332 input.data(), index.data(), output.data(),
333 nullptr /* thread pool */));
334
335 ASSERT_EQ(xnn_status_success,
336 xnn_run_operator(unpooling_op, nullptr /* thread pool */));
337
338 // Verify results.
339 for (size_t i = 0; i < batch_size(); i++) {
340 for (size_t c = 0; c < channels(); c++) {
341 for (size_t y = 0; y < output_height(); y++) {
342 for (size_t x = 0; x < output_width(); x++) {
343 EXPECT_EQ(output_ref[((i * output_height() + y) * output_width() + x) * channels() + c],
344 output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c]) <<
345 "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
346 }
347 }
348 }
349 }
350 }
351 }
352
353 void TestSetupX32() const {
354 std::random_device random_device;
355 auto rng = std::mt19937(random_device());
356 auto u32rng = std::bind(std::uniform_int_distribution<uint32_t>(), std::ref(rng));
357 auto idx_rng = std::bind(std::uniform_int_distribution<uint32_t>(0, pooling_height() * pooling_width() - 1), std::ref(rng));
358
359 std::vector<uint32_t> input(std::max(
360 (batch_size() * input_height() * input_width() - 1) * input_pixel_stride() + channels(),
361 (next_batch_size() * next_input_height() * next_input_width() - 1) * input_pixel_stride() + channels()));
362 std::vector<uint32_t> index(std::max(
363 batch_size() * input_height() * input_width() * channels(),
364 next_batch_size() * next_input_height() * next_input_width() * channels()));
365 std::vector<uint32_t> output(std::max(
366 (batch_size() * output_height() * output_width() - 1) * output_pixel_stride() + channels(),
367 (next_batch_size() * next_output_height() * next_output_width() - 1) * output_pixel_stride() * channels()));
368 std::vector<uint32_t> output_ref(batch_size() * output_height() * output_width() * channels());
369 std::vector<uint32_t> next_output_ref(next_batch_size() * next_output_height() * next_output_width() * channels());
370
371 for (size_t iteration = 0; iteration < iterations(); iteration++) {
372 std::generate(input.begin(), input.end(), std::ref(u32rng));
373 std::generate(index.begin(), index.end(), std::ref(idx_rng));
374 std::generate(output.begin(), output.end(), std::ref(u32rng));
375
376 // Compute reference results.
377 std::fill(output_ref.begin(), output_ref.end(), 0);
378 for (size_t i = 0; i < batch_size(); i++) {
379 for (size_t iy = 0; iy < input_height(); iy++) {
380 for (size_t ix = 0; ix < input_width(); ix++) {
381 for (size_t c = 0; c < channels(); c++) {
382 const uint32_t pooling_index = index[((i * input_height() + iy) * input_width() + ix) * channels() + c];
383 const uint32_t py = pooling_index % pooling_height();
384 const uint32_t px = pooling_index / pooling_height();
385 const size_t oy = std::min(std::max<size_t>(iy * pooling_height() + py, padding_top()) - padding_top(), output_height() - 1);
386 const size_t ox = std::min(std::max<size_t>(ix * pooling_width() + px, padding_left()) - padding_left(), output_width() - 1);
387 output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] =
388 input[((i * input_height() + iy) * input_width() + ix) * input_pixel_stride() + c];
389 }
390 }
391 }
392 }
393
394 // Create, setup, and run Unpooling operator once.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800395 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700396 xnn_operator_t unpooling_op = nullptr;
397
398 ASSERT_EQ(xnn_status_success,
399 xnn_create_unpooling2d_nhwc_x32(
400 padding_top(), padding_right(), padding_bottom(), padding_left(),
401 pooling_height(), pooling_width(),
402 channels(), input_pixel_stride(), output_pixel_stride(),
403 0, &unpooling_op));
404 ASSERT_NE(nullptr, unpooling_op);
405
406 // Smart pointer to automatically delete unpooling_op.
407 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_unpooling_op(unpooling_op, xnn_delete_operator);
408
409 ASSERT_EQ(xnn_status_success,
410 xnn_setup_unpooling2d_nhwc_x32(
411 unpooling_op,
412 batch_size(), input_height(), input_width(),
413 input.data(), index.data(), output.data(),
414 nullptr /* thread pool */));
415
416 ASSERT_EQ(xnn_status_success,
417 xnn_run_operator(unpooling_op, nullptr /* thread pool */));
418
419 // Verify results of the first run.
420 for (size_t i = 0; i < batch_size(); i++) {
421 for (size_t c = 0; c < channels(); c++) {
422 for (size_t y = 0; y < output_height(); y++) {
423 for (size_t x = 0; x < output_width(); x++) {
424 EXPECT_EQ(output_ref[((i * output_height() + y) * output_width() + x) * channels() + c],
425 output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c]) <<
426 "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
427 }
428 }
429 }
430 }
431
432 // Re-generate data for the second run.
433 std::generate(input.begin(), input.end(), std::ref(u32rng));
434 std::generate(index.begin(), index.end(), std::ref(idx_rng));
435 std::generate(output.begin(), output.end(), std::ref(u32rng));
436
437 // Compute reference results for the second run, including clamping.
438 std::fill(next_output_ref.begin(), next_output_ref.end(), 0);
439 for (size_t i = 0; i < next_batch_size(); i++) {
440 for (size_t iy = 0; iy < next_input_height(); iy++) {
441 for (size_t ix = 0; ix < next_input_width(); ix++) {
442 for (size_t c = 0; c < channels(); c++) {
443 const uint32_t pooling_index = index[((i * next_input_height() + iy) * next_input_width() + ix) * channels() + c];
444 const uint32_t py = pooling_index % pooling_height();
445 const uint32_t px = pooling_index / pooling_height();
446 const size_t oy = std::min(std::max<size_t>(iy * pooling_height() + py, padding_top()) - padding_top(), next_output_height() - 1);
447 const size_t ox = std::min(std::max<size_t>(ix * pooling_width() + px, padding_left()) - padding_left(), next_output_width() - 1);
448 next_output_ref[((i * next_output_height() + oy) * next_output_width() + ox) * channels() + c] =
449 input[((i * next_input_height() + iy) * next_input_width() + ix) * input_pixel_stride() + c];
450 }
451 }
452 }
453 }
454
455 // Setup and run Max Pooling operator the second time, and destroy the operator.
456 ASSERT_EQ(xnn_status_success,
457 xnn_setup_unpooling2d_nhwc_x32(
458 unpooling_op,
459 next_batch_size(), next_input_height(), next_input_width(),
460 input.data(), index.data(), output.data(),
461 nullptr /* thread pool */));
462
463 ASSERT_EQ(xnn_status_success,
464 xnn_run_operator(unpooling_op, nullptr /* thread pool */));
465
466 // Verify results of the second run.
467 for (size_t i = 0; i < next_batch_size(); i++) {
468 for (size_t c = 0; c < channels(); c++) {
469 for (size_t y = 0; y < next_output_height(); y++) {
470 for (size_t x = 0; x < next_output_width(); x++) {
471 EXPECT_EQ(next_output_ref[((i * next_output_height() + y) * next_output_width() + x) * channels() + c],
472 output[((i * next_output_height() + y) * next_output_width() + x) * output_pixel_stride() + c]) <<
473 "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
474 }
475 }
476 }
477 }
478 }
479 }
480
481 private:
482 uint32_t padding_top_{0};
483 uint32_t padding_right_{0};
484 uint32_t padding_bottom_{0};
485 uint32_t padding_left_{0};
486 size_t input_height_{1};
487 size_t input_width_{1};
488 size_t channels_{1};
489 size_t batch_size_{1};
490 size_t input_pixel_stride_{0};
491 size_t output_pixel_stride_{0};
492 uint32_t pooling_height_{1};
493 uint32_t pooling_width_{1};
494 size_t next_input_height_{0};
495 size_t next_input_width_{0};
496 size_t next_batch_size_{0};
497 size_t iterations_{1};
498};