Artsiom Ablavatski | 0f1dc18 | 2020-11-05 19:21:50 -0800 | [diff] [blame] | 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: |
| 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 | |
| 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 | |
| 38 | inline size_t input_height() const { |
| 39 | return this->input_height_; |
| 40 | } |
| 41 | |
| 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 | |
| 48 | inline size_t input_width() const { |
| 49 | return this->input_width_; |
| 50 | } |
| 51 | |
Marat Dukhan | bb781b6 | 2020-11-12 13:34:05 -0800 | [diff] [blame] | 52 | inline size_t output_height() const { |
| 53 | return input_height() * block_size(); |
| 54 | } |
| 55 | |
| 56 | inline size_t output_width() const { |
| 57 | return input_width() * block_size(); |
| 58 | } |
| 59 | |
Artsiom Ablavatski | 0f1dc18 | 2020-11-05 19:21:50 -0800 | [diff] [blame] | 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 | |
| 66 | inline size_t block_size() const { |
| 67 | return this->block_size_; |
| 68 | } |
| 69 | |
Marat Dukhan | bb781b6 | 2020-11-12 13:34:05 -0800 | [diff] [blame] | 70 | inline size_t input_channels() const { |
| 71 | return output_channels() * block_size() * block_size(); |
| 72 | } |
| 73 | |
| 74 | inline DepthToSpaceOperatorTester& output_channels(size_t output_channels) { |
| 75 | assert(output_channels != 0); |
| 76 | this->output_channels_ = output_channels; |
Artsiom Ablavatski | 0f1dc18 | 2020-11-05 19:21:50 -0800 | [diff] [blame] | 77 | return *this; |
| 78 | } |
| 79 | |
Marat Dukhan | bb781b6 | 2020-11-12 13:34:05 -0800 | [diff] [blame] | 80 | inline size_t output_channels() const { |
| 81 | return this->output_channels_; |
Artsiom Ablavatski | 0f1dc18 | 2020-11-05 19:21:50 -0800 | [diff] [blame] | 82 | } |
| 83 | |
| 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 | |
| 90 | inline size_t batch_size() const { |
| 91 | return this->batch_size_; |
| 92 | } |
| 93 | |
Marat Dukhan | 188d104 | 2020-11-24 23:39:40 -0800 | [diff] [blame] | 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; |
Artsiom Ablavatski | 0f1dc18 | 2020-11-05 19:21:50 -0800 | [diff] [blame] | 98 | } |
| 99 | |
Marat Dukhan | 188d104 | 2020-11-24 23:39:40 -0800 | [diff] [blame] | 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 | |
| 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 | |
| 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 | } |
Artsiom Ablavatski | 0f1dc18 | 2020-11-05 19:21:50 -0800 | [diff] [blame] | 122 | } |
| 123 | |
| 124 | inline DepthToSpaceOperatorTester& iterations(size_t iterations) { |
| 125 | this->iterations_ = iterations; |
| 126 | return *this; |
| 127 | } |
| 128 | |
| 129 | inline size_t iterations() const { |
| 130 | return this->iterations_; |
| 131 | } |
| 132 | |
Marat Dukhan | 0e52117 | 2020-11-25 13:10:04 -0800 | [diff] [blame] | 133 | void TestNHWCxX32() const { |
| 134 | std::random_device random_device; |
| 135 | auto rng = std::mt19937(random_device()); |
| 136 | auto i32rng = std::bind(std::uniform_int_distribution<int32_t>(), rng); |
| 137 | |
| 138 | std::vector<int32_t> input( |
| 139 | (batch_size() * input_height() * input_width() - 1) * input_channels_stride() + input_channels()); |
| 140 | std::vector<int32_t> output( |
| 141 | (batch_size() * output_height() * output_width() - 1) * output_channels_stride() + output_channels()); |
| 142 | for (size_t iteration = 0; iteration < iterations(); iteration++) { |
| 143 | std::generate(input.begin(), input.end(), std::ref(i32rng)); |
| 144 | std::fill(output.begin(), output.end(), INT32_C(0xDEADBEAF)); |
| 145 | |
| 146 | // Create, setup, run, and destroy Depth To Space operator. |
| 147 | ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */)); |
| 148 | xnn_operator_t depth_to_space_op = nullptr; |
| 149 | |
| 150 | ASSERT_EQ(xnn_status_success, |
| 151 | xnn_create_depth_to_space_nhwc_x32( |
| 152 | output_channels(), input_channels_stride(), output_channels_stride(), |
| 153 | block_size(), 0, &depth_to_space_op)); |
| 154 | ASSERT_NE(nullptr, depth_to_space_op); |
| 155 | |
| 156 | // Smart pointer to automatically delete depth_to_space_op. |
| 157 | std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_depth_to_space_op(depth_to_space_op, xnn_delete_operator); |
| 158 | |
| 159 | ASSERT_EQ(xnn_status_success, |
| 160 | xnn_setup_depth_to_space_nhwc_x32( |
| 161 | depth_to_space_op, |
| 162 | batch_size(), input_height(), input_width(), |
| 163 | input.data(), output.data(), nullptr /* thread pool */)); |
| 164 | |
| 165 | ASSERT_EQ(xnn_status_success, |
| 166 | xnn_run_operator(depth_to_space_op, nullptr /* thread pool */)); |
| 167 | |
| 168 | // Verify results. |
| 169 | for (size_t i = 0; i < batch_size(); i++) { |
| 170 | for (size_t iy = 0; iy < input_height(); iy++) { |
| 171 | for (size_t by = 0; by < block_size(); by++) { |
| 172 | for (size_t ix = 0; ix < input_width(); ix++) { |
| 173 | for (size_t bx = 0; bx < block_size(); bx++) { |
| 174 | for (size_t oc = 0; oc < output_channels(); oc++) { |
| 175 | const size_t input_index = |
| 176 | ((i * input_height() + iy) * input_width() + ix) * input_channels_stride() + |
| 177 | (by * block_size() + bx) * output_channels() + oc; |
| 178 | const size_t output_index = |
| 179 | ((i * output_height() + iy * block_size() + by) * output_width() + ix * block_size() + bx) * |
| 180 | output_channels_stride() + oc; |
| 181 | ASSERT_EQ(output[output_index], input[input_index]) |
| 182 | << "batch: " << i << " / " << batch_size() |
| 183 | << ", input x: " << ix << " / " << input_width() |
| 184 | << ", input y: " << iy << " / " << input_height() |
| 185 | << ", block x: " << bx << " / " << block_size() |
| 186 | << ", block y: " << by << " / " << block_size() |
| 187 | << ", output channel: " << oc << " / " << output_channels() |
| 188 | << ", input stride: " << input_channels_stride() |
| 189 | << ", output stride: " << output_channels_stride(); |
| 190 | } |
| 191 | } |
| 192 | } |
| 193 | } |
| 194 | } |
| 195 | } |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | void TestNCHW2NHWCxX32() const { |
Artsiom Ablavatski | 0f1dc18 | 2020-11-05 19:21:50 -0800 | [diff] [blame] | 200 | std::random_device random_device; |
| 201 | auto rng = std::mt19937(random_device()); |
Marat Dukhan | bb781b6 | 2020-11-12 13:34:05 -0800 | [diff] [blame] | 202 | auto i32rng = std::bind(std::uniform_int_distribution<int32_t>(), rng); |
Artsiom Ablavatski | 0f1dc18 | 2020-11-05 19:21:50 -0800 | [diff] [blame] | 203 | |
Marat Dukhan | 188d104 | 2020-11-24 23:39:40 -0800 | [diff] [blame] | 204 | std::vector<int32_t> input(XNN_EXTRA_BYTES / sizeof(uint32_t) + |
| 205 | ((batch_size() - 1) * input_channels_stride() + input_channels()) * input_height() * input_width()); |
| 206 | std::vector<int32_t> output( |
| 207 | (batch_size() * output_height() * output_width() - 1) * output_channels_stride() + output_channels()); |
Artsiom Ablavatski | 0f1dc18 | 2020-11-05 19:21:50 -0800 | [diff] [blame] | 208 | for (size_t iteration = 0; iteration < iterations(); iteration++) { |
Marat Dukhan | bb781b6 | 2020-11-12 13:34:05 -0800 | [diff] [blame] | 209 | std::generate(input.begin(), input.end(), std::ref(i32rng)); |
| 210 | std::fill(output.begin(), output.end(), INT32_C(0xDEADBEAF)); |
Artsiom Ablavatski | 0f1dc18 | 2020-11-05 19:21:50 -0800 | [diff] [blame] | 211 | |
| 212 | // Create, setup, run, and destroy Depth To Space operator. |
| 213 | ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */)); |
| 214 | xnn_operator_t depth_to_space_op = nullptr; |
| 215 | |
| 216 | ASSERT_EQ(xnn_status_success, |
Marat Dukhan | 13b68f2 | 2020-11-12 11:55:19 -0800 | [diff] [blame] | 217 | xnn_create_depth_to_space_nchw2nhwc_x32( |
Marat Dukhan | 188d104 | 2020-11-24 23:39:40 -0800 | [diff] [blame] | 218 | output_channels(), input_channels_stride(), output_channels_stride(), |
Artsiom Ablavatski | 0f1dc18 | 2020-11-05 19:21:50 -0800 | [diff] [blame] | 219 | block_size(), 0, &depth_to_space_op)); |
| 220 | ASSERT_NE(nullptr, depth_to_space_op); |
| 221 | |
| 222 | // Smart pointer to automatically delete depth_to_space_op. |
| 223 | std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_depth_to_space_op(depth_to_space_op, xnn_delete_operator); |
| 224 | |
| 225 | ASSERT_EQ(xnn_status_success, |
Marat Dukhan | 13b68f2 | 2020-11-12 11:55:19 -0800 | [diff] [blame] | 226 | xnn_setup_depth_to_space_nchw2nhwc_x32( |
Marat Dukhan | bb781b6 | 2020-11-12 13:34:05 -0800 | [diff] [blame] | 227 | depth_to_space_op, |
| 228 | batch_size(), input_height(), input_width(), |
Artsiom Ablavatski | 0f1dc18 | 2020-11-05 19:21:50 -0800 | [diff] [blame] | 229 | input.data(), output.data(), nullptr /* thread pool */)); |
| 230 | |
| 231 | ASSERT_EQ(xnn_status_success, |
| 232 | xnn_run_operator(depth_to_space_op, nullptr /* thread pool */)); |
| 233 | |
| 234 | // Verify results. |
Marat Dukhan | bb781b6 | 2020-11-12 13:34:05 -0800 | [diff] [blame] | 235 | for (size_t i = 0; i < batch_size(); i++) { |
| 236 | for (size_t iy = 0; iy < input_height(); iy++) { |
| 237 | for (size_t by = 0; by < block_size(); by++) { |
| 238 | for (size_t ix = 0; ix < input_width(); ix++) { |
| 239 | for (size_t bx = 0; bx < block_size(); bx++) { |
| 240 | for (size_t oc = 0; oc < output_channels(); oc++) { |
Marat Dukhan | 0e52117 | 2020-11-25 13:10:04 -0800 | [diff] [blame] | 241 | const size_t input_index = |
Marat Dukhan | 188d104 | 2020-11-24 23:39:40 -0800 | [diff] [blame] | 242 | i * input_channels_stride() * input_height() * input_width() + |
Marat Dukhan | 77e93a2 | 2021-02-26 11:13:55 -0800 | [diff] [blame] | 243 | (((by * block_size() + bx) * output_channels() + oc) * input_height() + iy) * input_width() + ix; |
Marat Dukhan | 0e52117 | 2020-11-25 13:10:04 -0800 | [diff] [blame] | 244 | const size_t output_index = |
Marat Dukhan | 188d104 | 2020-11-24 23:39:40 -0800 | [diff] [blame] | 245 | ((i * output_height() + iy * block_size() + by) * output_width() + ix * block_size() + bx) * |
| 246 | output_channels_stride() + oc; |
Marat Dukhan | 0e52117 | 2020-11-25 13:10:04 -0800 | [diff] [blame] | 247 | ASSERT_EQ(output[output_index], input[input_index]) |
Marat Dukhan | 188d104 | 2020-11-24 23:39:40 -0800 | [diff] [blame] | 248 | << "batch: " << i << " / " << batch_size() |
| 249 | << ", input x: " << ix << " / " << input_width() |
| 250 | << ", input y: " << iy << " / " << input_height() |
| 251 | << ", block x: " << bx << " / " << block_size() |
| 252 | << ", block y: " << by << " / " << block_size() |
Marat Dukhan | 0e52117 | 2020-11-25 13:10:04 -0800 | [diff] [blame] | 253 | << ", output channel: " << oc << " / " << output_channels() |
| 254 | << ", input stride: " << input_channels_stride() |
| 255 | << ", output stride: " << output_channels_stride(); |
Artsiom Ablavatski | 0f1dc18 | 2020-11-05 19:21:50 -0800 | [diff] [blame] | 256 | } |
| 257 | } |
| 258 | } |
| 259 | } |
| 260 | } |
| 261 | } |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | private: |
| 266 | size_t input_height_{1}; |
| 267 | size_t input_width_{1}; |
Marat Dukhan | bb781b6 | 2020-11-12 13:34:05 -0800 | [diff] [blame] | 268 | size_t output_channels_{1}; |
Artsiom Ablavatski | 0f1dc18 | 2020-11-05 19:21:50 -0800 | [diff] [blame] | 269 | size_t block_size_{2}; |
| 270 | size_t batch_size_{1}; |
Marat Dukhan | 188d104 | 2020-11-24 23:39:40 -0800 | [diff] [blame] | 271 | size_t input_channels_stride_{0}; |
| 272 | size_t output_channels_stride_{0}; |
Artsiom Ablavatski | 0f1dc18 | 2020-11-05 19:21:50 -0800 | [diff] [blame] | 273 | size_t iterations_{1}; |
| 274 | }; |