1 // 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 22 class UnpoolingOperatorTester { 23 public: padding(uint32_t padding)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 padding(uint32_t padding_height,uint32_t padding_width)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 padding_height(uint32_t padding_height)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 padding_width(uint32_t padding_width)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 padding_top(uint32_t padding_top)52 inline UnpoolingOperatorTester& padding_top(uint32_t padding_top) { 53 this->padding_top_ = padding_top; 54 return *this; 55 } 56 padding_top()57 inline uint32_t padding_top() const { 58 return this->padding_top_; 59 } 60 padding_right(uint32_t padding_right)61 inline UnpoolingOperatorTester& padding_right(uint32_t padding_right) { 62 this->padding_right_ = padding_right; 63 return *this; 64 } 65 padding_right()66 inline uint32_t padding_right() const { 67 return this->padding_right_; 68 } 69 padding_bottom(uint32_t padding_bottom)70 inline UnpoolingOperatorTester& padding_bottom(uint32_t padding_bottom) { 71 this->padding_bottom_ = padding_bottom; 72 return *this; 73 } 74 padding_bottom()75 inline uint32_t padding_bottom() const { 76 return this->padding_bottom_; 77 } 78 padding_left(uint32_t padding_left)79 inline UnpoolingOperatorTester& padding_left(uint32_t padding_left) { 80 this->padding_left_ = padding_left; 81 return *this; 82 } 83 padding_left()84 inline uint32_t padding_left() const { 85 return this->padding_left_; 86 } 87 input_size(size_t input_height,size_t input_width)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 input_height(size_t input_height)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 input_height()102 inline size_t input_height() const { 103 return this->input_height_; 104 } 105 input_width(size_t input_width)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 input_width()112 inline size_t input_width() const { 113 return this->input_width_; 114 } 115 channels(size_t channels)116 inline UnpoolingOperatorTester& channels(size_t channels) { 117 assert(channels != 0); 118 this->channels_ = channels; 119 return *this; 120 } 121 channels()122 inline size_t channels() const { 123 return this->channels_; 124 } 125 batch_size(size_t batch_size)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 batch_size()132 inline size_t batch_size() const { 133 return this->batch_size_; 134 } 135 pooling_size(uint32_t pooling_size)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 pooling_size(uint32_t pooling_height,uint32_t pooling_width)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 pooling_height(uint32_t pooling_height)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 pooling_height()157 inline uint32_t pooling_height() const { 158 return this->pooling_height_; 159 } 160 pooling_width(uint32_t pooling_width)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 pooling_width()167 inline uint32_t pooling_width() const { 168 return this->pooling_width_; 169 } 170 output_height()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 output_width()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 input_pixel_stride(size_t input_pixel_stride)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 input_pixel_stride()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 output_pixel_stride(size_t output_pixel_stride)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 output_pixel_stride()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 next_input_size(uint32_t next_input_height,uint32_t next_input_width)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 next_input_height(uint32_t next_input_height)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 next_input_height()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 next_input_width(uint32_t next_input_width)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 next_input_width()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 next_output_height()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 next_output_width()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 next_batch_size(size_t next_batch_size)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 next_batch_size()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 iterations(size_t iterations)271 inline UnpoolingOperatorTester& iterations(size_t iterations) { 272 this->iterations_ = iterations; 273 return *this; 274 } 275 iterations()276 inline size_t iterations() const { 277 return this->iterations_; 278 } 279 TestX32()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. 314 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */)); 315 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 TestSetupX32()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. 395 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */)); 396 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 }; 499