1 // Copyright 2020 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 <cmath> 12 #include <cassert> 13 #include <cstddef> 14 #include <cstdlib> 15 #include <functional> 16 #include <random> 17 #include <vector> 18 19 #include <xnnpack.h> 20 21 22 class DepthToSpaceOperatorTester { 23 public: input_size(size_t input_height,size_t input_width)24 inline DepthToSpaceOperatorTester& input_size(size_t input_height, size_t input_width) { 25 assert(input_height >= 1); 26 assert(input_width >= 1); 27 this->input_height_ = input_height; 28 this->input_width_ = input_width; 29 return *this; 30 } 31 input_height(size_t input_height)32 inline DepthToSpaceOperatorTester& input_height(size_t input_height) { 33 assert(input_height >= 1); 34 this->input_height_ = input_height; 35 return *this; 36 } 37 input_height()38 inline size_t input_height() const { 39 return this->input_height_; 40 } 41 input_width(size_t input_width)42 inline DepthToSpaceOperatorTester& input_width(size_t input_width) { 43 assert(input_width >= 1); 44 this->input_width_ = input_width; 45 return *this; 46 } 47 input_width()48 inline size_t input_width() const { 49 return this->input_width_; 50 } 51 output_height()52 inline size_t output_height() const { 53 return input_height() * block_size(); 54 } 55 output_width()56 inline size_t output_width() const { 57 return input_width() * block_size(); 58 } 59 block_size(size_t block_size)60 inline DepthToSpaceOperatorTester& block_size(size_t block_size) { 61 assert(block_size >= 2); 62 this->block_size_ = block_size; 63 return *this; 64 } 65 block_size()66 inline size_t block_size() const { 67 return this->block_size_; 68 } 69 input_channels()70 inline size_t input_channels() const { 71 return output_channels() * block_size() * block_size(); 72 } 73 output_channels(size_t output_channels)74 inline DepthToSpaceOperatorTester& output_channels(size_t output_channels) { 75 assert(output_channels != 0); 76 this->output_channels_ = output_channels; 77 return *this; 78 } 79 output_channels()80 inline size_t output_channels() const { 81 return this->output_channels_; 82 } 83 batch_size(size_t batch_size)84 inline DepthToSpaceOperatorTester& batch_size(size_t batch_size) { 85 assert(batch_size != 0); 86 this->batch_size_ = batch_size; 87 return *this; 88 } 89 batch_size()90 inline size_t batch_size() const { 91 return this->batch_size_; 92 } 93 input_channels_stride(size_t input_channels_stride)94 inline DepthToSpaceOperatorTester& input_channels_stride(size_t input_channels_stride) { 95 assert(input_channels_stride >= 1); 96 this->input_channels_stride_ = input_channels_stride; 97 return *this; 98 } 99 input_channels_stride()100 inline size_t input_channels_stride() const { 101 if (this->input_channels_stride_ == 0) { 102 return input_channels(); 103 } else { 104 assert(this->input_channels_stride_ >= input_channels()); 105 return this->input_channels_stride_; 106 } 107 } 108 output_channels_stride(size_t output_channels_stride)109 inline DepthToSpaceOperatorTester& output_channels_stride(size_t output_channels_stride) { 110 assert(output_channels_stride >= 1); 111 this->output_channels_stride_ = output_channels_stride; 112 return *this; 113 } 114 output_channels_stride()115 inline size_t output_channels_stride() const { 116 if (this->output_channels_stride_ == 0) { 117 return output_channels(); 118 } else { 119 assert(this->output_channels_stride_ >= output_channels()); 120 return this->output_channels_stride_; 121 } 122 } 123 iterations(size_t iterations)124 inline DepthToSpaceOperatorTester& iterations(size_t iterations) { 125 this->iterations_ = iterations; 126 return *this; 127 } 128 iterations()129 inline size_t iterations() const { 130 return this->iterations_; 131 } 132 TestNHWCxX8()133 void TestNHWCxX8() const { 134 std::random_device random_device; 135 auto rng = std::mt19937(random_device()); 136 auto i8rng = std::bind( 137 std::uniform_int_distribution<int32_t>(std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max()), 138 std::ref(rng)); 139 140 std::vector<int8_t> input( 141 (batch_size() * input_height() * input_width() - 1) * input_channels_stride() + input_channels()); 142 std::vector<int8_t> output( 143 (batch_size() * output_height() * output_width() - 1) * output_channels_stride() + output_channels()); 144 for (size_t iteration = 0; iteration < iterations(); iteration++) { 145 std::generate(input.begin(), input.end(), std::ref(i8rng)); 146 std::fill(output.begin(), output.end(), INT8_C(0xAF)); 147 148 // Create, setup, run, and destroy Depth To Space operator. 149 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */)); 150 xnn_operator_t depth_to_space_op = nullptr; 151 152 ASSERT_EQ(xnn_status_success, 153 xnn_create_depth_to_space_nhwc_x8( 154 output_channels(), input_channels_stride(), output_channels_stride(), 155 block_size(), 0, &depth_to_space_op)); 156 ASSERT_NE(nullptr, depth_to_space_op); 157 158 // Smart pointer to automatically delete depth_to_space_op. 159 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_depth_to_space_op(depth_to_space_op, xnn_delete_operator); 160 161 ASSERT_EQ(xnn_status_success, 162 xnn_setup_depth_to_space_nhwc_x8( 163 depth_to_space_op, 164 batch_size(), input_height(), input_width(), 165 input.data(), output.data(), nullptr /* thread pool */)); 166 167 ASSERT_EQ(xnn_status_success, 168 xnn_run_operator(depth_to_space_op, nullptr /* thread pool */)); 169 170 // Verify results. 171 for (size_t i = 0; i < batch_size(); i++) { 172 for (size_t iy = 0; iy < input_height(); iy++) { 173 for (size_t by = 0; by < block_size(); by++) { 174 for (size_t ix = 0; ix < input_width(); ix++) { 175 for (size_t bx = 0; bx < block_size(); bx++) { 176 for (size_t oc = 0; oc < output_channels(); oc++) { 177 const size_t input_index = 178 ((i * input_height() + iy) * input_width() + ix) * input_channels_stride() + 179 (by * block_size() + bx) * output_channels() + oc; 180 const size_t output_index = 181 ((i * output_height() + iy * block_size() + by) * output_width() + ix * block_size() + bx) * 182 output_channels_stride() + oc; 183 ASSERT_EQ(int32_t(output[output_index]), int32_t(input[input_index])) 184 << "batch: " << i << " / " << batch_size() 185 << ", input x: " << ix << " / " << input_width() 186 << ", input y: " << iy << " / " << input_height() 187 << ", block x: " << bx << " / " << block_size() 188 << ", block y: " << by << " / " << block_size() 189 << ", output channel: " << oc << " / " << output_channels() 190 << ", input stride: " << input_channels_stride() 191 << ", output stride: " << output_channels_stride(); 192 } 193 } 194 } 195 } 196 } 197 } 198 } 199 } 200 TestNHWCxX16()201 void TestNHWCxX16() const { 202 std::random_device random_device; 203 auto rng = std::mt19937(random_device()); 204 auto i16rng = std::bind(std::uniform_int_distribution<int16_t>(), std::ref(rng)); 205 206 std::vector<int16_t> input( 207 (batch_size() * input_height() * input_width() - 1) * input_channels_stride() + input_channels()); 208 std::vector<int16_t> output( 209 (batch_size() * output_height() * output_width() - 1) * output_channels_stride() + output_channels()); 210 for (size_t iteration = 0; iteration < iterations(); iteration++) { 211 std::generate(input.begin(), input.end(), std::ref(i16rng)); 212 std::fill(output.begin(), output.end(), INT16_C(0xDEAD)); 213 214 // Create, setup, run, and destroy Depth To Space operator. 215 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */)); 216 xnn_operator_t depth_to_space_op = nullptr; 217 218 ASSERT_EQ(xnn_status_success, 219 xnn_create_depth_to_space_nhwc_x16( 220 output_channels(), input_channels_stride(), output_channels_stride(), 221 block_size(), 0, &depth_to_space_op)); 222 ASSERT_NE(nullptr, depth_to_space_op); 223 224 // Smart pointer to automatically delete depth_to_space_op. 225 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_depth_to_space_op(depth_to_space_op, xnn_delete_operator); 226 227 ASSERT_EQ(xnn_status_success, 228 xnn_setup_depth_to_space_nhwc_x16( 229 depth_to_space_op, 230 batch_size(), input_height(), input_width(), 231 input.data(), output.data(), nullptr /* thread pool */)); 232 233 ASSERT_EQ(xnn_status_success, 234 xnn_run_operator(depth_to_space_op, nullptr /* thread pool */)); 235 236 // Verify results. 237 for (size_t i = 0; i < batch_size(); i++) { 238 for (size_t iy = 0; iy < input_height(); iy++) { 239 for (size_t by = 0; by < block_size(); by++) { 240 for (size_t ix = 0; ix < input_width(); ix++) { 241 for (size_t bx = 0; bx < block_size(); bx++) { 242 for (size_t oc = 0; oc < output_channels(); oc++) { 243 const size_t input_index = 244 ((i * input_height() + iy) * input_width() + ix) * input_channels_stride() + 245 (by * block_size() + bx) * output_channels() + oc; 246 const size_t output_index = 247 ((i * output_height() + iy * block_size() + by) * output_width() + ix * block_size() + bx) * 248 output_channels_stride() + oc; 249 ASSERT_EQ(output[output_index], input[input_index]) 250 << "batch: " << i << " / " << batch_size() 251 << ", input x: " << ix << " / " << input_width() 252 << ", input y: " << iy << " / " << input_height() 253 << ", block x: " << bx << " / " << block_size() 254 << ", block y: " << by << " / " << block_size() 255 << ", output channel: " << oc << " / " << output_channels() 256 << ", input stride: " << input_channels_stride() 257 << ", output stride: " << output_channels_stride(); 258 } 259 } 260 } 261 } 262 } 263 } 264 } 265 } 266 TestNHWCxX32()267 void TestNHWCxX32() const { 268 std::random_device random_device; 269 auto rng = std::mt19937(random_device()); 270 auto i32rng = std::bind(std::uniform_int_distribution<int32_t>(), std::ref(rng)); 271 272 std::vector<int32_t> input( 273 (batch_size() * input_height() * input_width() - 1) * input_channels_stride() + input_channels()); 274 std::vector<int32_t> output( 275 (batch_size() * output_height() * output_width() - 1) * output_channels_stride() + output_channels()); 276 for (size_t iteration = 0; iteration < iterations(); iteration++) { 277 std::generate(input.begin(), input.end(), std::ref(i32rng)); 278 std::fill(output.begin(), output.end(), INT32_C(0xDEADBEAF)); 279 280 // Create, setup, run, and destroy Depth To Space operator. 281 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */)); 282 xnn_operator_t depth_to_space_op = nullptr; 283 284 ASSERT_EQ(xnn_status_success, 285 xnn_create_depth_to_space_nhwc_x32( 286 output_channels(), input_channels_stride(), output_channels_stride(), 287 block_size(), 0, &depth_to_space_op)); 288 ASSERT_NE(nullptr, depth_to_space_op); 289 290 // Smart pointer to automatically delete depth_to_space_op. 291 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_depth_to_space_op(depth_to_space_op, xnn_delete_operator); 292 293 ASSERT_EQ(xnn_status_success, 294 xnn_setup_depth_to_space_nhwc_x32( 295 depth_to_space_op, 296 batch_size(), input_height(), input_width(), 297 input.data(), output.data(), nullptr /* thread pool */)); 298 299 ASSERT_EQ(xnn_status_success, 300 xnn_run_operator(depth_to_space_op, nullptr /* thread pool */)); 301 302 // Verify results. 303 for (size_t i = 0; i < batch_size(); i++) { 304 for (size_t iy = 0; iy < input_height(); iy++) { 305 for (size_t by = 0; by < block_size(); by++) { 306 for (size_t ix = 0; ix < input_width(); ix++) { 307 for (size_t bx = 0; bx < block_size(); bx++) { 308 for (size_t oc = 0; oc < output_channels(); oc++) { 309 const size_t input_index = 310 ((i * input_height() + iy) * input_width() + ix) * input_channels_stride() + 311 (by * block_size() + bx) * output_channels() + oc; 312 const size_t output_index = 313 ((i * output_height() + iy * block_size() + by) * output_width() + ix * block_size() + bx) * 314 output_channels_stride() + oc; 315 ASSERT_EQ(output[output_index], input[input_index]) 316 << "batch: " << i << " / " << batch_size() 317 << ", input x: " << ix << " / " << input_width() 318 << ", input y: " << iy << " / " << input_height() 319 << ", block x: " << bx << " / " << block_size() 320 << ", block y: " << by << " / " << block_size() 321 << ", output channel: " << oc << " / " << output_channels() 322 << ", input stride: " << input_channels_stride() 323 << ", output stride: " << output_channels_stride(); 324 } 325 } 326 } 327 } 328 } 329 } 330 } 331 } 332 TestNCHW2NHWCxX32()333 void TestNCHW2NHWCxX32() const { 334 std::random_device random_device; 335 auto rng = std::mt19937(random_device()); 336 auto i32rng = std::bind(std::uniform_int_distribution<int32_t>(), std::ref(rng)); 337 338 std::vector<int32_t> input(XNN_EXTRA_BYTES / sizeof(uint32_t) + 339 ((batch_size() - 1) * input_channels_stride() + input_channels()) * input_height() * input_width()); 340 std::vector<int32_t> output( 341 (batch_size() * output_height() * output_width() - 1) * output_channels_stride() + output_channels()); 342 for (size_t iteration = 0; iteration < iterations(); iteration++) { 343 std::generate(input.begin(), input.end(), std::ref(i32rng)); 344 std::fill(output.begin(), output.end(), INT32_C(0xDEADBEAF)); 345 346 // Create, setup, run, and destroy Depth To Space operator. 347 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */)); 348 xnn_operator_t depth_to_space_op = nullptr; 349 350 ASSERT_EQ(xnn_status_success, 351 xnn_create_depth_to_space_nchw2nhwc_x32( 352 output_channels(), input_channels_stride(), output_channels_stride(), 353 block_size(), 0, &depth_to_space_op)); 354 ASSERT_NE(nullptr, depth_to_space_op); 355 356 // Smart pointer to automatically delete depth_to_space_op. 357 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_depth_to_space_op(depth_to_space_op, xnn_delete_operator); 358 359 ASSERT_EQ(xnn_status_success, 360 xnn_setup_depth_to_space_nchw2nhwc_x32( 361 depth_to_space_op, 362 batch_size(), input_height(), input_width(), 363 input.data(), output.data(), nullptr /* thread pool */)); 364 365 ASSERT_EQ(xnn_status_success, 366 xnn_run_operator(depth_to_space_op, nullptr /* thread pool */)); 367 368 // Verify results. 369 for (size_t i = 0; i < batch_size(); i++) { 370 for (size_t iy = 0; iy < input_height(); iy++) { 371 for (size_t by = 0; by < block_size(); by++) { 372 for (size_t ix = 0; ix < input_width(); ix++) { 373 for (size_t bx = 0; bx < block_size(); bx++) { 374 for (size_t oc = 0; oc < output_channels(); oc++) { 375 const size_t input_index = 376 i * input_channels_stride() * input_height() * input_width() + 377 (((by * block_size() + bx) * output_channels() + oc) * input_height() + iy) * input_width() + ix; 378 const size_t output_index = 379 ((i * output_height() + iy * block_size() + by) * output_width() + ix * block_size() + bx) * 380 output_channels_stride() + oc; 381 ASSERT_EQ(output[output_index], input[input_index]) 382 << "batch: " << i << " / " << batch_size() 383 << ", input x: " << ix << " / " << input_width() 384 << ", input y: " << iy << " / " << input_height() 385 << ", block x: " << bx << " / " << block_size() 386 << ", block y: " << by << " / " << block_size() 387 << ", output channel: " << oc << " / " << output_channels() 388 << ", input stride: " << input_channels_stride() 389 << ", output stride: " << output_channels_stride(); 390 } 391 } 392 } 393 } 394 } 395 } 396 } 397 } 398 399 private: 400 size_t input_height_{1}; 401 size_t input_width_{1}; 402 size_t output_channels_{1}; 403 size_t block_size_{2}; 404 size_t batch_size_{1}; 405 size_t input_channels_stride_{0}; 406 size_t output_channels_stride_{0}; 407 size_t iterations_{1}; 408 }; 409