• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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