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 #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 SpaceToDepthOperatorTester { 23 public: input_size(size_t input_height,size_t input_width)24 inline SpaceToDepthOperatorTester& 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 SpaceToDepthOperatorTester& 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 SpaceToDepthOperatorTester& 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 assert(input_height() % block_size() == 0); 54 return input_height() / block_size(); 55 } 56 output_width()57 inline size_t output_width() const { 58 assert(input_width() % block_size() == 0); 59 return input_width() / block_size(); 60 } 61 block_size(size_t block_size)62 inline SpaceToDepthOperatorTester& block_size(size_t block_size) { 63 assert(block_size >= 2); 64 this->block_size_ = block_size; 65 return *this; 66 } 67 block_size()68 inline size_t block_size() const { 69 return this->block_size_; 70 } 71 input_channels(size_t input_channels)72 inline SpaceToDepthOperatorTester& input_channels(size_t input_channels) { 73 assert(input_channels != 0); 74 this->input_channels_ = input_channels; 75 return *this; 76 } 77 input_channels()78 inline size_t input_channels() const { 79 return this->input_channels_; 80 } 81 output_channels()82 inline size_t output_channels() const { 83 return input_channels() * block_size() * block_size(); 84 } 85 batch_size(size_t batch_size)86 inline SpaceToDepthOperatorTester& batch_size(size_t batch_size) { 87 assert(batch_size != 0); 88 this->batch_size_ = batch_size; 89 return *this; 90 } 91 batch_size()92 inline size_t batch_size() const { 93 return this->batch_size_; 94 } 95 input_channels_stride(size_t input_channels_stride)96 inline SpaceToDepthOperatorTester& input_channels_stride(size_t input_channels_stride) { 97 assert(input_channels_stride >= 1); 98 this->input_channels_stride_ = input_channels_stride; 99 return *this; 100 } 101 input_channels_stride()102 inline size_t input_channels_stride() const { 103 if (this->input_channels_stride_ == 0) { 104 return input_channels(); 105 } else { 106 assert(this->input_channels_stride_ >= input_channels()); 107 return this->input_channels_stride_; 108 } 109 } 110 output_channels_stride(size_t output_channels_stride)111 inline SpaceToDepthOperatorTester& output_channels_stride(size_t output_channels_stride) { 112 assert(output_channels_stride >= 1); 113 this->output_channels_stride_ = output_channels_stride; 114 return *this; 115 } 116 output_channels_stride()117 inline size_t output_channels_stride() const { 118 if (this->output_channels_stride_ == 0) { 119 return output_channels(); 120 } else { 121 assert(this->output_channels_stride_ >= output_channels()); 122 return this->output_channels_stride_; 123 } 124 } 125 iterations(size_t iterations)126 inline SpaceToDepthOperatorTester& iterations(size_t iterations) { 127 this->iterations_ = iterations; 128 return *this; 129 } 130 iterations()131 inline size_t iterations() const { 132 return this->iterations_; 133 } 134 TestNHWCxX8()135 void TestNHWCxX8() const { 136 std::vector<int8_t> input( 137 (batch_size() * input_height() * input_width() - 1) * input_channels_stride() + input_channels()); 138 std::vector<int8_t> output( 139 (batch_size() * output_height() * output_width() - 1) * output_channels_stride() + output_channels()); 140 for (size_t iteration = 0; iteration < iterations(); iteration++) { 141 std::iota(input.begin(), input.end(), 0); 142 std::fill(output.begin(), output.end(), INT8_C(0xAF)); 143 144 // Create, setup, run, and destroy Depth To Space operator. 145 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */)); 146 xnn_operator_t space_to_depth_op = nullptr; 147 148 ASSERT_EQ(xnn_status_success, 149 xnn_create_space_to_depth_nhwc_x8( 150 input_channels(), input_channels_stride(), output_channels_stride(), 151 block_size(), 0, &space_to_depth_op)); 152 ASSERT_NE(nullptr, space_to_depth_op); 153 154 // Smart pointer to automatically delete space_to_depth_op. 155 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_space_to_depth_op(space_to_depth_op, xnn_delete_operator); 156 157 ASSERT_EQ(xnn_status_success, 158 xnn_setup_space_to_depth_nhwc_x8( 159 space_to_depth_op, 160 batch_size(), input_height(), input_width(), 161 input.data(), output.data(), nullptr /* thread pool */)); 162 163 ASSERT_EQ(xnn_status_success, 164 xnn_run_operator(space_to_depth_op, nullptr /* thread pool */)); 165 166 // Verify results. 167 for (size_t i = 0; i < batch_size(); i++) { 168 for (size_t iy = 0; iy < output_height(); iy++) { 169 for (size_t ix = 0; ix < output_width(); ix++) { 170 for (size_t by = 0; by < block_size(); by++) { 171 for (size_t bx = 0; bx < block_size(); bx++) { 172 for (size_t oc = 0; oc < input_channels(); oc++) { 173 const size_t input_index = oc 174 + bx * input_channels_stride() 175 + ix * block_size() * input_channels_stride() 176 + by * output_width() * block_size() * input_channels_stride() 177 + iy * block_size() * output_width() * block_size() * input_channels_stride() 178 + i * output_height() * block_size() * output_width() * block_size() * input_channels_stride(); 179 const size_t output_index = oc 180 + bx * input_channels() 181 + by * input_channels() * block_size() 182 + ix * output_channels_stride() 183 + iy * output_width() * output_channels_stride() 184 + i * output_height() * output_width() * output_channels_stride(); 185 186 ASSERT_EQ(int32_t(output[output_index]), int32_t(input[input_index])) 187 << "batch: " << i << " / " << batch_size() 188 << ", output x: " << ix << " / " << output_width() 189 << ", output y: " << iy << " / " << output_height() 190 << ", block x: " << bx << " / " << block_size() 191 << ", block y: " << by << " / " << block_size() 192 << ", input channel: " << oc << " / " << input_channels() 193 << ", input stride: " << input_channels_stride() 194 << ", output stride: " << output_channels_stride(); 195 } 196 } 197 } 198 } 199 } 200 } 201 } 202 } 203 TestNHWCxX16()204 void TestNHWCxX16() const { 205 std::vector<int16_t> input( 206 (batch_size() * input_height() * input_width() - 1) * input_channels_stride() + input_channels()); 207 std::vector<int16_t> output( 208 (batch_size() * output_height() * output_width() - 1) * output_channels_stride() + output_channels()); 209 for (size_t iteration = 0; iteration < iterations(); iteration++) { 210 std::iota(input.begin(), input.end(), 0); 211 std::fill(output.begin(), output.end(), INT16_C(0xDEAD)); 212 213 // Create, setup, run, and destroy Depth To Space operator. 214 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */)); 215 xnn_operator_t space_to_depth_op = nullptr; 216 217 ASSERT_EQ(xnn_status_success, 218 xnn_create_space_to_depth_nhwc_x16( 219 input_channels(), input_channels_stride(), output_channels_stride(), 220 block_size(), 0, &space_to_depth_op)); 221 ASSERT_NE(nullptr, space_to_depth_op); 222 223 // Smart pointer to automatically delete space_to_depth_op. 224 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_space_to_depth_op(space_to_depth_op, xnn_delete_operator); 225 226 ASSERT_EQ(xnn_status_success, 227 xnn_setup_space_to_depth_nhwc_x16( 228 space_to_depth_op, 229 batch_size(), input_height(), input_width(), 230 input.data(), output.data(), nullptr /* thread pool */)); 231 232 ASSERT_EQ(xnn_status_success, 233 xnn_run_operator(space_to_depth_op, nullptr /* thread pool */)); 234 235 // Verify results. 236 for (size_t i = 0; i < batch_size(); i++) { 237 for (size_t iy = 0; iy < output_height(); iy++) { 238 for (size_t ix = 0; ix < output_width(); ix++) { 239 for (size_t by = 0; by < block_size(); by++) { 240 for (size_t bx = 0; bx < block_size(); bx++) { 241 for (size_t oc = 0; oc < input_channels(); oc++) { 242 const size_t input_index = oc 243 + bx * input_channels_stride() 244 + ix * block_size() * input_channels_stride() 245 + by * output_width() * block_size() * input_channels_stride() 246 + iy * block_size() * output_width() * block_size() * input_channels_stride() 247 + i * output_height() * block_size() * output_width() * block_size() * input_channels_stride(); 248 const size_t output_index = oc 249 + bx * input_channels() 250 + by * input_channels() * block_size() 251 + ix * output_channels_stride() 252 + iy * output_width() * output_channels_stride() 253 + i * output_height() * output_width() * output_channels_stride(); 254 255 ASSERT_EQ(int32_t(output[output_index]), int32_t(input[input_index])) 256 << "batch: " << i << " / " << batch_size() 257 << ", output x: " << ix << " / " << output_width() 258 << ", output y: " << iy << " / " << output_height() 259 << ", block x: " << bx << " / " << block_size() 260 << ", block y: " << by << " / " << block_size() 261 << ", input channel: " << oc << " / " << input_channels() 262 << ", input stride: " << input_channels_stride() 263 << ", output stride: " << output_channels_stride(); 264 } 265 } 266 } 267 } 268 } 269 } 270 } 271 } 272 TestNHWCxX32()273 void TestNHWCxX32() const { 274 std::vector<int32_t> input( 275 (batch_size() * input_height() * input_width() - 1) * input_channels_stride() + input_channels()); 276 std::vector<int32_t> output( 277 (batch_size() * output_height() * output_width() - 1) * output_channels_stride() + output_channels()); 278 for (size_t iteration = 0; iteration < iterations(); iteration++) { 279 std::iota(input.begin(), input.end(), 0); 280 std::fill(output.begin(), output.end(), INT32_C(0xDEADBEEF)); 281 282 // Create, setup, run, and destroy Depth To Space operator. 283 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */)); 284 xnn_operator_t space_to_depth_op = nullptr; 285 286 ASSERT_EQ(xnn_status_success, 287 xnn_create_space_to_depth_nhwc_x32( 288 input_channels(), input_channels_stride(), output_channels_stride(), 289 block_size(), 0, &space_to_depth_op)); 290 ASSERT_NE(nullptr, space_to_depth_op); 291 292 // Smart pointer to automatically delete space_to_depth_op. 293 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_space_to_depth_op(space_to_depth_op, xnn_delete_operator); 294 295 ASSERT_EQ(xnn_status_success, 296 xnn_setup_space_to_depth_nhwc_x32( 297 space_to_depth_op, 298 batch_size(), input_height(), input_width(), 299 input.data(), output.data(), nullptr /* thread pool */)); 300 301 ASSERT_EQ(xnn_status_success, 302 xnn_run_operator(space_to_depth_op, nullptr /* thread pool */)); 303 304 // Verify results. 305 for (size_t i = 0; i < batch_size(); i++) { 306 for (size_t iy = 0; iy < output_height(); iy++) { 307 for (size_t ix = 0; ix < output_width(); ix++) { 308 for (size_t by = 0; by < block_size(); by++) { 309 for (size_t bx = 0; bx < block_size(); bx++) { 310 for (size_t oc = 0; oc < input_channels(); oc++) { 311 const size_t input_index = oc 312 + bx * input_channels_stride() 313 + ix * block_size() * input_channels_stride() 314 + by * output_width() * block_size() * input_channels_stride() 315 + iy * block_size() * output_width() * block_size() * input_channels_stride() 316 + i * output_height() * block_size() * output_width() * block_size() * input_channels_stride(); 317 const size_t output_index = oc 318 + bx * input_channels() 319 + by * input_channels() * block_size() 320 + ix * output_channels_stride() 321 + iy * output_width() * output_channels_stride() 322 + i * output_height() * output_width() * output_channels_stride(); 323 324 ASSERT_EQ(int32_t(output[output_index]), int32_t(input[input_index])) 325 << "batch: " << i << " / " << batch_size() 326 << ", output x: " << ix << " / " << output_width() 327 << ", output y: " << iy << " / " << output_height() 328 << ", block x: " << bx << " / " << block_size() 329 << ", block y: " << by << " / " << block_size() 330 << ", input channel: " << oc << " / " << input_channels() 331 << ", input stride: " << input_channels_stride() 332 << ", output stride: " << output_channels_stride(); 333 } 334 } 335 } 336 } 337 } 338 } 339 } 340 } 341 342 private: 343 size_t input_height_{1}; 344 size_t input_width_{1}; 345 size_t input_channels_{1}; 346 size_t block_size_{2}; 347 size_t batch_size_{1}; 348 size_t input_channels_stride_{0}; 349 size_t output_channels_stride_{0}; 350 size_t iterations_{1}; 351 }; 352