blob: a9e3743ed3312dd7509f5e1274403da24c616a41 [file] [log] [blame]
XNNPACK Teamb455b122019-09-27 18:10:33 -07001// Copyright (c) Facebook, Inc. and its affiliates.
2// All rights reserved.
3//
4// Copyright 2019 Google LLC
5//
6// This source code is licensed under the BSD-style license found in the
7// LICENSE file in the root directory of this source tree.
8
9#pragma once
10
11#include <gtest/gtest.h>
12
13#include <algorithm>
14#include <cmath>
15#include <cassert>
16#include <cstddef>
17#include <cstdlib>
18#include <functional>
19#include <random>
20#include <vector>
21
22#include <xnnpack.h>
23
24
25class AveragePoolingOperatorTester {
26 public:
27 inline AveragePoolingOperatorTester& padding(uint32_t padding) {
28 this->padding_top_ = padding;
29 this->padding_right_ = padding;
30 this->padding_bottom_ = padding;
31 this->padding_left_ = padding;
32 return *this;
33 }
34
35 inline AveragePoolingOperatorTester& padding(uint32_t padding_height, uint32_t padding_width) {
36 this->padding_top_ = padding_height;
37 this->padding_right_ = padding_width;
38 this->padding_bottom_ = padding_height;
39 this->padding_left_ = padding_width;
40 return *this;
41 }
42
43 inline AveragePoolingOperatorTester& padding_height(uint32_t padding_height) {
44 this->padding_top_ = padding_height;
45 this->padding_bottom_ = padding_height;
46 return *this;
47 }
48
49 inline AveragePoolingOperatorTester& padding_width(uint32_t padding_width) {
50 this->padding_right_ = padding_width;
51 this->padding_left_ = padding_width;
52 return *this;
53 }
54
55 inline AveragePoolingOperatorTester& padding_top(uint32_t padding_top) {
56 this->padding_top_ = padding_top;
57 return *this;
58 }
59
60 inline uint32_t padding_top() const {
61 return this->padding_top_;
62 }
63
64 inline AveragePoolingOperatorTester& padding_right(uint32_t padding_right) {
65 this->padding_right_ = padding_right;
66 return *this;
67 }
68
69 inline uint32_t padding_right() const {
70 return this->padding_right_;
71 }
72
73 inline AveragePoolingOperatorTester& padding_bottom(uint32_t padding_bottom) {
74 this->padding_bottom_ = padding_bottom;
75 return *this;
76 }
77
78 inline uint32_t padding_bottom() const {
79 return this->padding_bottom_;
80 }
81
82 inline AveragePoolingOperatorTester& padding_left(uint32_t padding_left) {
83 this->padding_left_ = padding_left;
84 return *this;
85 }
86
87 inline uint32_t padding_left() const {
88 return this->padding_left_;
89 }
90
91 inline AveragePoolingOperatorTester& input_size(size_t input_height, size_t input_width) {
92 assert(input_height >= 1);
93 assert(input_width >= 1);
94 this->input_height_ = input_height;
95 this->input_width_ = input_width;
96 return *this;
97 }
98
99 inline AveragePoolingOperatorTester& input_height(size_t input_height) {
100 assert(input_height >= 1);
101 this->input_height_ = input_height;
102 return *this;
103 }
104
105 inline size_t input_height() const {
106 return this->input_height_;
107 }
108
109 inline AveragePoolingOperatorTester& input_width(size_t input_width) {
110 assert(input_width >= 1);
111 this->input_width_ = input_width;
112 return *this;
113 }
114
115 inline size_t input_width() const {
116 return this->input_width_;
117 }
118
119 inline AveragePoolingOperatorTester& channels(size_t channels) {
120 assert(channels != 0);
121 this->channels_ = channels;
122 return *this;
123 }
124
125 inline size_t channels() const {
126 return this->channels_;
127 }
128
129 inline AveragePoolingOperatorTester& batch_size(size_t batch_size) {
130 assert(batch_size != 0);
131 this->batch_size_ = batch_size;
132 return *this;
133 }
134
135 inline size_t batch_size() const {
136 return this->batch_size_;
137 }
138
139 inline AveragePoolingOperatorTester& pooling_size(uint32_t pooling_size) {
140 assert(pooling_size >= 1);
141 this->pooling_height_ = pooling_size;
142 this->pooling_width_ = pooling_size;
143 return *this;
144 }
145
146 inline AveragePoolingOperatorTester& pooling_size(uint32_t pooling_height, uint32_t pooling_width) {
147 assert(pooling_height >= 1);
148 assert(pooling_width >= 1);
149 this->pooling_height_ = pooling_height;
150 this->pooling_width_ = pooling_width;
151 return *this;
152 }
153
154 inline AveragePoolingOperatorTester& pooling_height(uint32_t pooling_height) {
155 assert(pooling_height >= 1);
156 this->pooling_height_ = pooling_height;
157 return *this;
158 }
159
160 inline uint32_t pooling_height() const {
161 return this->pooling_height_;
162 }
163
164 inline AveragePoolingOperatorTester& pooling_width(uint32_t pooling_width) {
165 assert(pooling_width >= 1);
166 this->pooling_width_ = pooling_width;
167 return *this;
168 }
169
170 inline uint32_t pooling_width() const {
171 return this->pooling_width_;
172 }
173
174 inline AveragePoolingOperatorTester& stride(uint32_t stride) {
175 assert(stride >= 1);
176 this->stride_height_ = stride;
177 this->stride_width_ = stride;
178 return *this;
179 }
180
181 inline AveragePoolingOperatorTester& stride(uint32_t stride_height, uint32_t stride_width) {
182 assert(stride_height >= 1);
183 assert(stride_width >= 1);
184 this->stride_height_ = stride_height;
185 this->stride_width_ = stride_width;
186 return *this;
187 }
188
189 inline AveragePoolingOperatorTester& stride_height(uint32_t stride_height) {
190 assert(stride_height >= 1);
191 this->stride_height_ = stride_height;
192 return *this;
193 }
194
195 inline uint32_t stride_height() const {
196 return this->stride_height_;
197 }
198
199 inline AveragePoolingOperatorTester& stride_width(uint32_t stride_width) {
200 assert(stride_width >= 1);
201 this->stride_width_ = stride_width;
202 return *this;
203 }
204
205 inline uint32_t stride_width() const {
206 return this->stride_width_;
207 }
208
209 inline size_t output_height() const {
210 const size_t padded_input_height = padding_top() + input_height() + padding_bottom();
211 if (padded_input_height <= pooling_height()) {
212 return 1;
213 } else {
214 return (padded_input_height - pooling_height()) / stride_height() + 1;
215 }
216 }
217
218 inline size_t output_width() const {
219 const size_t padded_input_width = padding_left() + input_width() + padding_right();
220 if (padded_input_width <= pooling_width()) {
221 return 1;
222 } else {
223 return (padded_input_width - pooling_width()) / stride_width() + 1;
224 }
225 }
226
227 inline AveragePoolingOperatorTester& input_pixel_stride(size_t input_pixel_stride) {
228 assert(input_pixel_stride != 0);
229 this->input_pixel_stride_ = input_pixel_stride;
230 return *this;
231 }
232
233 inline size_t input_pixel_stride() const {
234 if (this->input_pixel_stride_ == 0) {
235 return channels();
236 } else {
237 assert(this->input_pixel_stride_ >= channels());
238 return this->input_pixel_stride_;
239 }
240 }
241
242 inline AveragePoolingOperatorTester& output_pixel_stride(size_t output_pixel_stride) {
243 assert(output_pixel_stride != 0);
244 this->output_pixel_stride_ = output_pixel_stride;
245 return *this;
246 }
247
248 inline size_t output_pixel_stride() const {
249 if (this->output_pixel_stride_ == 0) {
250 return channels();
251 } else {
252 assert(this->output_pixel_stride_ >= channels());
253 return this->output_pixel_stride_;
254 }
255 }
256
257 inline AveragePoolingOperatorTester& next_input_size(uint32_t next_input_height, uint32_t next_input_width) {
258 assert(next_input_height >= 1);
259 assert(next_input_width >= 1);
260 this->next_input_height_ = next_input_height;
261 this->next_input_width_ = next_input_width;
262 return *this;
263 }
264
265 inline AveragePoolingOperatorTester& next_input_height(uint32_t next_input_height) {
266 assert(next_input_height >= 1);
267 this->next_input_height_ = next_input_height;
268 return *this;
269 }
270
271 inline uint32_t next_input_height() const {
272 if (this->next_input_height_ == 0) {
273 return input_height();
274 } else {
275 return this->next_input_height_;
276 }
277 }
278
279 inline AveragePoolingOperatorTester& next_input_width(uint32_t next_input_width) {
280 assert(next_input_width >= 1);
281 this->next_input_width_ = next_input_width;
282 return *this;
283 }
284
285 inline uint32_t next_input_width() const {
286 if (this->next_input_width_ == 0) {
287 return input_width();
288 } else {
289 return this->next_input_width_;
290 }
291 }
292
293 inline size_t next_output_height() const {
294 const size_t padded_next_input_height = padding_top() + next_input_height() + padding_bottom();
295 if (padded_next_input_height <= pooling_height()) {
296 return 1;
297 } else {
298 return (padded_next_input_height - pooling_height()) / stride_height() + 1;
299 }
300 }
301
302 inline size_t next_output_width() const {
303 const size_t padded_next_input_width = padding_left() + next_input_width() + padding_right();
304 if (padded_next_input_width <= pooling_width()) {
305 return 1;
306 } else {
307 return (padded_next_input_width - pooling_width()) / stride_width() + 1;
308 }
309 }
310
311 inline AveragePoolingOperatorTester& next_batch_size(size_t next_batch_size) {
312 assert(next_batch_size >= 1);
313 this->next_batch_size_ = next_batch_size;
314 return *this;
315 }
316
317 inline size_t next_batch_size() const {
318 if (this->next_batch_size_ == 0) {
319 return batch_size();
320 } else {
321 return this->next_batch_size_;
322 }
323 }
324
325 inline AveragePoolingOperatorTester& input_scale(float input_scale) {
326 assert(input_scale > 0.0f);
327 assert(std::isnormal(input_scale));
328 this->input_scale_ = input_scale;
329 return *this;
330 }
331
332 inline float input_scale() const {
333 return this->input_scale_;
334 }
335
336 inline AveragePoolingOperatorTester& input_zero_point(uint8_t input_zero_point) {
337 this->input_zero_point_ = input_zero_point;
338 return *this;
339 }
340
341 inline uint8_t input_zero_point() const {
342 return this->input_zero_point_;
343 }
344
345 inline AveragePoolingOperatorTester& output_scale(float output_scale) {
346 assert(output_scale > 0.0f);
347 assert(std::isnormal(output_scale));
348 this->output_scale_ = output_scale;
349 return *this;
350 }
351
352 inline float output_scale() const {
353 return this->output_scale_;
354 }
355
356 inline AveragePoolingOperatorTester& output_zero_point(uint8_t output_zero_point) {
357 this->output_zero_point_ = output_zero_point;
358 return *this;
359 }
360
361 inline uint8_t output_zero_point() const {
362 return this->output_zero_point_;
363 }
364
365 inline AveragePoolingOperatorTester& qmin(uint8_t qmin) {
366 this->qmin_ = qmin;
367 return *this;
368 }
369
370 inline uint8_t qmin() const {
371 return this->qmin_;
372 }
373
374 inline AveragePoolingOperatorTester& qmax(uint8_t qmax) {
375 this->qmax_ = qmax;
376 return *this;
377 }
378
379 inline uint8_t qmax() const {
380 return this->qmax_;
381 }
382
383 inline AveragePoolingOperatorTester& iterations(size_t iterations) {
384 this->iterations_ = iterations;
385 return *this;
386 }
387
388 inline size_t iterations() const {
389 return this->iterations_;
390 }
391
392 void TestQ8() const {
393 std::random_device random_device;
394 auto rng = std::mt19937(random_device());
395 auto u8rng = std::bind(std::uniform_int_distribution<uint8_t>(), rng);
396
397 std::vector<uint8_t> input((batch_size() * input_height() * input_width() - 1) * input_pixel_stride() + channels() + XNN_EXTRA_BYTES / sizeof(uint8_t));
398 std::vector<uint8_t> output((batch_size() * output_height() * output_width() - 1) * output_pixel_stride() + channels());
399 std::vector<float> output_ref(batch_size() * output_height() * output_width() * channels());
400 for (size_t iteration = 0; iteration < iterations(); iteration++) {
401 std::generate(input.begin(), input.end(), std::ref(u8rng));
402 std::fill(output.begin(), output.end(), 0xA5);
403
404 // Compute reference results.
405 const double scale = double(input_scale()) / (double(output_scale()) * double(pooling_height() * pooling_width()));
406 for (size_t i = 0; i < batch_size(); i++) {
407 for (size_t oy = 0; oy < output_height(); oy++) {
408 for (size_t ox = 0; ox < output_width(); ox++) {
409 for (size_t c = 0; c < channels(); c++) {
410 double acc = 0.0f;
411 for (size_t py = 0; py < pooling_height(); py++) {
412 const size_t iy = oy * stride_height() + py - padding_top();
413 for (size_t px = 0; px < pooling_width(); px++) {
414 const size_t ix = ox * stride_width() + px - padding_left();
Marat Dukhane0df8312019-10-22 18:16:56 -0700415 if (ix < input_width() && iy < input_height()) {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700416 acc += double(int32_t(input[((i * input_height() + iy) * input_width() + ix) * input_pixel_stride() + c]) - int32_t(input_zero_point()));
417 }
418 }
419 }
420 output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] = float(acc * scale + double(output_zero_point()));
421 output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] =
422 std::min<float>(output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c], float(qmax()));
423 output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] =
424 std::max<float>(output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c], float(qmin()));
425 }
426 }
427 }
428 }
429
430 // Create, setup, run, and destroy Average Pooling operator.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800431 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700432 xnn_operator_t average_pooling_op = nullptr;
433
434 ASSERT_EQ(xnn_status_success,
435 xnn_create_average_pooling2d_nhwc_q8(
436 padding_top(), padding_right(), padding_bottom(), padding_left(),
437 pooling_height(), pooling_width(),
438 stride_height(), stride_width(),
439 channels(), input_pixel_stride(), output_pixel_stride(),
440 input_zero_point(), input_scale(),
441 output_zero_point(), output_scale(),
442 qmin(), qmax(),
443 0, &average_pooling_op));
444 ASSERT_NE(nullptr, average_pooling_op);
445
446 // Smart pointer to automatically delete average_pooling_op.
447 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_average_pooling_op(average_pooling_op, xnn_delete_operator);
448
449 ASSERT_EQ(xnn_status_success,
450 xnn_setup_average_pooling2d_nhwc_q8(
451 average_pooling_op,
452 batch_size(), input_height(), input_width(),
453 input.data(), output.data(),
454 nullptr /* thread pool */));
455
456 ASSERT_EQ(xnn_status_success,
457 xnn_run_operator(average_pooling_op, nullptr /* thread pool */));
458
459 // Verify results.
460 for (size_t i = 0; i < batch_size(); i++) {
461 for (size_t y = 0; y < output_height(); y++) {
462 for (size_t x = 0; x < output_width(); x++) {
463 for (size_t c = 0; c < channels(); c++) {
464 ASSERT_LE(uint32_t(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c]), uint32_t(qmax()));
465 ASSERT_GE(uint32_t(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c]), uint32_t(qmin()));
466 ASSERT_NEAR(float(int32_t(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c])),
467 output_ref[((i * output_height() + y) * output_width() + x) * channels() + c], 0.80f) <<
468 "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
469 }
470 }
471 }
472 }
473 }
474 }
475
476 void TestF32() const {
477 std::random_device random_device;
478 auto rng = std::mt19937(random_device());
479 auto f32rng = std::bind(std::uniform_real_distribution<float>(), rng);
480
481 std::vector<float> input((batch_size() * input_height() * input_width() - 1) * input_pixel_stride() + channels() + XNN_EXTRA_BYTES / sizeof(float));
482 std::vector<float> output((batch_size() * output_height() * output_width() - 1) * output_pixel_stride() + channels());
483 std::vector<float> output_ref(batch_size() * output_height() * output_width() * channels());
484 for (size_t iteration = 0; iteration < iterations(); iteration++) {
485 std::generate(input.begin(), input.end(), std::ref(f32rng));
486 std::fill(output.begin(), output.end(), std::nanf(""));
487
488 // Compute reference results, without clamping.
489 for (size_t i = 0; i < batch_size(); i++) {
490 for (size_t oy = 0; oy < output_height(); oy++) {
491 for (size_t ox = 0; ox < output_width(); ox++) {
492 for (size_t c = 0; c < channels(); c++) {
493 float acc = 0.0f;
Marat Dukhane0df8312019-10-22 18:16:56 -0700494 int32_t n = 0;
XNNPACK Teamb455b122019-09-27 18:10:33 -0700495 for (size_t py = 0; py < pooling_height(); py++) {
496 const size_t iy = oy * stride_height() + py - padding_top();
497 for (size_t px = 0; px < pooling_width(); px++) {
498 const size_t ix = ox * stride_width() + px - padding_left();
Marat Dukhane0df8312019-10-22 18:16:56 -0700499 if (ix < input_width() && iy < input_height()) {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700500 acc += input[((i * input_height() + iy) * input_width() + ix) * input_pixel_stride() + c];
501 n += 1;
502 }
503 }
504 }
505 output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] = acc / float(n);
506 }
507 }
508 }
509 }
510
511 // Compute clamping parameters.
512 const float accumulated_min = *std::min_element(output_ref.cbegin(), output_ref.cend());
513 const float accumulated_max = *std::max_element(output_ref.cbegin(), output_ref.cend());
514 const float accumulated_range = accumulated_max - accumulated_min;
515 const float output_min = accumulated_range == 0.0f ?
516 -std::numeric_limits<float>::infinity() :
517 accumulated_min + accumulated_range / 255.0f * float(qmin());
518 const float output_max = accumulated_range == 0.0f ?
519 +std::numeric_limits<float>::infinity() :
520 accumulated_max - accumulated_range / 255.0f * float(255 - qmax());
521
522 // Clamp reference results.
523 for (float& value : output_ref) {
524 value = std::max(std::min(value, output_max), output_min);
525 }
526
527 // Create, setup, run, and destroy Average Pooling operator.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800528 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700529 xnn_operator_t average_pooling_op = nullptr;
530
531 ASSERT_EQ(xnn_status_success,
532 xnn_create_average_pooling2d_nhwc_f32(
533 padding_top(), padding_right(), padding_bottom(), padding_left(),
534 pooling_height(), pooling_width(),
535 stride_height(), stride_width(),
536 channels(), input_pixel_stride(), output_pixel_stride(),
537 output_min, output_max,
538 0, &average_pooling_op));
539 ASSERT_NE(nullptr, average_pooling_op);
540
541 // Smart pointer to automatically delete average_pooling_op.
542 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_average_pooling_op(average_pooling_op, xnn_delete_operator);
543
544 ASSERT_EQ(xnn_status_success,
545 xnn_setup_average_pooling2d_nhwc_f32(
546 average_pooling_op,
547 batch_size(), input_height(), input_width(),
548 input.data(), output.data(),
549 nullptr /* thread pool */));
550
551 ASSERT_EQ(xnn_status_success,
552 xnn_run_operator(average_pooling_op, nullptr /* thread pool */));
553
554 // Verify results.
555 for (size_t i = 0; i < batch_size(); i++) {
556 for (size_t y = 0; y < output_height(); y++) {
557 for (size_t x = 0; x < output_width(); x++) {
558 for (size_t c = 0; c < channels(); c++) {
559 ASSERT_LE(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c], output_max);
560 ASSERT_GE(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c], output_min);
561 ASSERT_NEAR(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c],
562 output_ref[((i * output_height() + y) * output_width() + x) * channels() + c],
563 std::abs(output_ref[((i * output_height() + y) * output_width() + x) * channels() + c]) * 1.0e-6f) <<
564 "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
565 }
566 }
567 }
568 }
569 }
570 }
571
572 void TestSetupQ8() const {
573 std::random_device random_device;
574 auto rng = std::mt19937(random_device());
575 auto u8rng = std::bind(std::uniform_int_distribution<uint8_t>(), rng);
576
577 std::vector<uint8_t> input(XNN_EXTRA_BYTES / sizeof(uint8_t) + std::max(
578 (batch_size() * input_height() * input_width() - 1) * input_pixel_stride() + channels(),
579 (next_batch_size() * next_input_height() * next_input_width() - 1) * input_pixel_stride() + channels()));
580 std::vector<uint8_t> output(std::max(
581 (batch_size() * output_height() * output_width() - 1) * output_pixel_stride() + channels(),
582 (next_batch_size() * next_output_height() * next_output_width() - 1) * output_pixel_stride() + channels()));
583 std::vector<float> output_ref(batch_size() * output_height() * output_width() * channels());
584 std::vector<float> next_output_ref(next_batch_size() * next_output_height() * next_output_width() * channels());
585 for (size_t iteration = 0; iteration < iterations(); iteration++) {
586 std::generate(input.begin(), input.end(), std::ref(u8rng));
587 std::fill(output.begin(), output.end(), 0xA5);
588
589 // Compute reference results.
590 const double scale = double(input_scale()) / (double(output_scale()) * double(pooling_height() * pooling_width()));
591 for (size_t i = 0; i < batch_size(); i++) {
592 for (size_t oy = 0; oy < output_height(); oy++) {
593 for (size_t ox = 0; ox < output_width(); ox++) {
594 for (size_t c = 0; c < channels(); c++) {
595 double acc = 0.0f;
596 for (size_t py = 0; py < pooling_height(); py++) {
597 const size_t iy = oy * stride_height() + py - padding_top();
598 for (size_t px = 0; px < pooling_width(); px++) {
599 const size_t ix = ox * stride_width() + px - padding_left();
Marat Dukhane0df8312019-10-22 18:16:56 -0700600 if (ix < input_width() && iy < input_height()) {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700601 acc += double(int32_t(input[((i * input_height() + iy) * input_width() + ix) * input_pixel_stride() + c]) - int32_t(input_zero_point()));
602 }
603 }
604 }
605 output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] = float(acc * scale + double(output_zero_point()));
606 output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] =
607 std::min<float>(output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c], float(qmax()));
608 output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] =
609 std::max<float>(output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c], float(qmin()));
610 }
611 }
612 }
613 }
614
615 // Create, setup, and run Average Pooling operator once.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800616 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700617 xnn_operator_t average_pooling_op = nullptr;
618
619 ASSERT_EQ(xnn_status_success,
620 xnn_create_average_pooling2d_nhwc_q8(
621 padding_top(), padding_right(), padding_bottom(), padding_left(),
622 pooling_height(), pooling_width(),
623 stride_height(), stride_width(),
624 channels(), input_pixel_stride(), output_pixel_stride(),
625 input_zero_point(), input_scale(),
626 output_zero_point(), output_scale(),
627 qmin(), qmax(),
628 0, &average_pooling_op));
629 ASSERT_NE(nullptr, average_pooling_op);
630
631 ASSERT_EQ(xnn_status_success,
632 xnn_setup_average_pooling2d_nhwc_q8(
633 average_pooling_op,
634 batch_size(), input_height(), input_width(),
635 input.data(), output.data(),
636 nullptr /* thread pool */));
637
638 ASSERT_EQ(xnn_status_success,
639 xnn_run_operator(average_pooling_op, nullptr /* thread pool */));
640
641 // Verify results of the first run.
642 for (size_t i = 0; i < batch_size(); i++) {
643 for (size_t y = 0; y < output_height(); y++) {
644 for (size_t x = 0; x < output_width(); x++) {
645 for (size_t c = 0; c < channels(); c++) {
646 ASSERT_LE(uint32_t(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c]), uint32_t(qmax()));
647 ASSERT_GE(uint32_t(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c]), uint32_t(qmin()));
648 ASSERT_NEAR(float(int32_t(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c])),
649 output_ref[((i * output_height() + y) * output_width() + x) * channels() + c], 0.80f) <<
650 "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
651 }
652 }
653 }
654 }
655
656 // Re-generate data for the second run.
657 std::generate(input.begin(), input.end(), std::ref(u8rng));
658 std::fill(output.begin(), output.end(), 0xA5);
659
660 // Compute reference results for the second run.
661 for (size_t i = 0; i < next_batch_size(); i++) {
662 for (size_t oy = 0; oy < next_output_height(); oy++) {
663 for (size_t ox = 0; ox < next_output_width(); ox++) {
664 for (size_t c = 0; c < channels(); c++) {
665 double acc = 0.0f;
666 for (size_t py = 0; py < pooling_height(); py++) {
667 const size_t iy = oy * stride_height() + py - padding_top();
668 for (size_t px = 0; px < pooling_width(); px++) {
669 const size_t ix = ox * stride_width() + px - padding_left();
Marat Dukhane0df8312019-10-22 18:16:56 -0700670 if (ix < next_input_width() && iy < next_input_height()) {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700671 acc += double(int32_t(input[((i * next_input_height() + iy) * next_input_width() + ix) * input_pixel_stride() + c]) - int32_t(input_zero_point()));
672 }
673 }
674 }
675 next_output_ref[((i * next_output_height() + oy) * next_output_width() + ox) * channels() + c] = float(acc * scale + double(output_zero_point()));
676 next_output_ref[((i * next_output_height() + oy) * next_output_width() + ox) * channels() + c] =
677 std::min<float>(next_output_ref[((i * next_output_height() + oy) * next_output_width() + ox) * channels() + c], float(qmax()));
678 next_output_ref[((i * next_output_height() + oy) * next_output_width() + ox) * channels() + c] =
679 std::max<float>(next_output_ref[((i * next_output_height() + oy) * next_output_width() + ox) * channels() + c], float(qmin()));
680 }
681 }
682 }
683 }
684
685 // Setup and run Average Pooling operator the second time, and destroy the operator.
686 ASSERT_EQ(xnn_status_success,
687 xnn_setup_average_pooling2d_nhwc_q8(
688 average_pooling_op,
689 next_batch_size(), next_input_height(), next_input_width(),
690 input.data(), output.data(),
691 nullptr /* thread pool */));
692
693 ASSERT_EQ(xnn_status_success,
694 xnn_run_operator(average_pooling_op, nullptr /* thread pool */));
695
696 ASSERT_EQ(xnn_status_success,
697 xnn_delete_operator(average_pooling_op));
698 average_pooling_op = nullptr;
699
700 // Verify results of the second run.
701 for (size_t i = 0; i < next_batch_size(); i++) {
702 for (size_t y = 0; y < next_output_height(); y++) {
703 for (size_t x = 0; x < next_output_width(); x++) {
704 for (size_t c = 0; c < channels(); c++) {
705 ASSERT_LE(uint32_t(output[((i * next_output_height() + y) * next_output_width() + x) * output_pixel_stride() + c]), uint32_t(qmax()));
706 ASSERT_GE(uint32_t(output[((i * next_output_height() + y) * next_output_width() + x) * output_pixel_stride() + c]), uint32_t(qmin()));
707 ASSERT_NEAR(float(int32_t(output[((i * next_output_height() + y) * next_output_width() + x) * output_pixel_stride() + c])),
708 next_output_ref[((i * next_output_height() + y) * next_output_width() + x) * channels() + c], 0.80f) <<
709 "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
710 }
711 }
712 }
713 }
714 }
715 }
716
717 void TestSetupF32() const {
718 std::random_device random_device;
719 auto rng = std::mt19937(random_device());
720 auto f32rng = std::bind(std::uniform_real_distribution<float>(), rng);
721
722 std::vector<float> input(XNN_EXTRA_BYTES / sizeof(float) + std::max(
723 (batch_size() * input_height() * input_width() - 1) * input_pixel_stride() + channels(),
724 (next_batch_size() * next_input_height() * next_input_width() - 1) * input_pixel_stride() + channels()));
725 std::vector<float> output(std::max(
726 (batch_size() * output_height() * output_width() - 1) * output_pixel_stride() + channels(),
727 (next_batch_size() * next_output_height() * next_output_width() - 1) * output_pixel_stride() + channels()));
728 std::vector<float> output_ref(batch_size() * output_height() * output_width() * channels());
729 std::vector<float> next_output_ref(next_batch_size() * next_output_height() * next_output_width() * channels());
730 for (size_t iteration = 0; iteration < iterations(); iteration++) {
731 std::generate(input.begin(), input.end(), std::ref(f32rng));
732 std::fill(output.begin(), output.end(), std::nanf(""));
733
734 // Compute reference results, without clamping.
735 for (size_t i = 0; i < batch_size(); i++) {
736 for (size_t oy = 0; oy < output_height(); oy++) {
737 for (size_t ox = 0; ox < output_width(); ox++) {
738 for (size_t c = 0; c < channels(); c++) {
739 float acc = 0.0f;
740 size_t n = 0;
741 for (size_t py = 0; py < pooling_height(); py++) {
742 const size_t iy = oy * stride_height() + py - padding_top();
743 for (size_t px = 0; px < pooling_width(); px++) {
744 const size_t ix = ox * stride_width() + px - padding_left();
Marat Dukhane0df8312019-10-22 18:16:56 -0700745 if (ix < input_width() && iy < input_height()) {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700746 acc += input[((i * input_height() + iy) * input_width() + ix) * input_pixel_stride() + c];
747 n += 1;
748 }
749 }
750 }
751 output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] = acc / float(n);
752 }
753 }
754 }
755 }
756
757 // Compute clamping parameters.
758 const float accumulated_min = *std::min_element(output_ref.cbegin(), output_ref.cend());
759 const float accumulated_max = *std::max_element(output_ref.cbegin(), output_ref.cend());
760 const float accumulated_range = accumulated_max - accumulated_min;
761 const float output_min = accumulated_range == 0.0f ?
762 -std::numeric_limits<float>::infinity() :
763 accumulated_min + accumulated_range / 255.0f * float(qmin());
764 const float output_max = accumulated_range == 0.0f ?
765 +std::numeric_limits<float>::infinity() :
766 accumulated_max - accumulated_range / 255.0f * float(255 - qmax());
767
768 // Clamp reference results.
769 for (float& value : output_ref) {
770 value = std::max(std::min(value, output_max), output_min);
771 }
772
773 // Create, setup, and run Average Pooling operator once.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800774 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700775 xnn_operator_t average_pooling_op = nullptr;
776
777 ASSERT_EQ(xnn_status_success,
778 xnn_create_average_pooling2d_nhwc_f32(
779 padding_top(), padding_right(), padding_bottom(), padding_left(),
780 pooling_height(), pooling_width(),
781 stride_height(), stride_width(),
782 channels(), input_pixel_stride(), output_pixel_stride(),
783 output_min, output_max,
784 0, &average_pooling_op));
785 ASSERT_NE(nullptr, average_pooling_op);
786
787 ASSERT_EQ(xnn_status_success,
788 xnn_setup_average_pooling2d_nhwc_f32(
789 average_pooling_op,
790 batch_size(), input_height(), input_width(),
791 input.data(), output.data(),
792 nullptr /* thread pool */));
793
794 ASSERT_EQ(xnn_status_success,
795 xnn_run_operator(average_pooling_op, nullptr /* thread pool */));
796
797 // Verify results of the first run.
798 for (size_t i = 0; i < batch_size(); i++) {
799 for (size_t y = 0; y < output_height(); y++) {
800 for (size_t x = 0; x < output_width(); x++) {
801 for (size_t c = 0; c < channels(); c++) {
802 ASSERT_LE(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c], output_max);
803 ASSERT_GE(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c], output_min);
804 ASSERT_NEAR(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c],
805 output_ref[((i * output_height() + y) * output_width() + x) * channels() + c],
806 std::abs(output_ref[((i * output_height() + y) * output_width() + x) * channels() + c]) * 1.0e-6f) <<
807 "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
808 }
809 }
810 }
811 }
812
813 // Re-generate data for the second run.
814 std::generate(input.begin(), input.end(), std::ref(f32rng));
815 std::fill(output.begin(), output.end(), std::nanf(""));
816
817 // Compute reference results for the second run.
818 for (size_t i = 0; i < next_batch_size(); i++) {
819 for (size_t oy = 0; oy < next_output_height(); oy++) {
820 for (size_t ox = 0; ox < next_output_width(); ox++) {
821 for (size_t c = 0; c < channels(); c++) {
822 float acc = 0.0f;
823 int32_t n = 0;
824 for (size_t py = 0; py < pooling_height(); py++) {
825 const size_t iy = oy * stride_height() + py - padding_top();
826 for (size_t px = 0; px < pooling_width(); px++) {
827 const size_t ix = ox * stride_width() + px - padding_left();
Marat Dukhane0df8312019-10-22 18:16:56 -0700828 if (ix < next_input_width() && iy < next_input_height()) {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700829 acc += input[((i * next_input_height() + iy) * next_input_width() + ix) * input_pixel_stride() + c];
830 n += 1;
831 }
832 }
833 }
834 next_output_ref[((i * next_output_height() + oy) * next_output_width() + ox) * channels() + c] =
835 std::max(std::min(acc / float(n), output_max), output_min);
836 }
837 }
838 }
839 }
840
841 // Setup and run Average Pooling operator the second time, and destroy the operator.
842 ASSERT_EQ(xnn_status_success,
843 xnn_setup_average_pooling2d_nhwc_f32(
844 average_pooling_op,
845 next_batch_size(), next_input_height(), next_input_width(),
846 input.data(), output.data(),
847 nullptr /* thread pool */));
848
849 ASSERT_EQ(xnn_status_success,
850 xnn_run_operator(average_pooling_op, nullptr /* thread pool */));
851
852 ASSERT_EQ(xnn_status_success,
853 xnn_delete_operator(average_pooling_op));
854 average_pooling_op = nullptr;
855
856 // Verify results of the second run.
857 for (size_t i = 0; i < next_batch_size(); i++) {
858 for (size_t y = 0; y < next_output_height(); y++) {
859 for (size_t x = 0; x < next_output_width(); x++) {
860 for (size_t c = 0; c < channels(); c++) {
861 ASSERT_LE(output[((i * next_output_height() + y) * next_output_width() + x) * output_pixel_stride() + c], output_max);
862 ASSERT_GE(output[((i * next_output_height() + y) * next_output_width() + x) * output_pixel_stride() + c], output_min);
863 ASSERT_NEAR(output[((i * next_output_height() + y) * next_output_width() + x) * output_pixel_stride() + c],
864 next_output_ref[((i * next_output_height() + y) * next_output_width() + x) * channels() + c],
865 std::abs(next_output_ref[((i * next_output_height() + y) * next_output_width() + x) * channels() + c]) * 1.0e-6f) <<
866 "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
867 }
868 }
869 }
870 }
871 }
872 }
873
874 private:
875 uint32_t padding_top_{0};
876 uint32_t padding_right_{0};
877 uint32_t padding_bottom_{0};
878 uint32_t padding_left_{0};
879 size_t input_height_{1};
880 size_t input_width_{1};
881 size_t channels_{1};
882 size_t batch_size_{1};
883 size_t input_pixel_stride_{0};
884 size_t output_pixel_stride_{0};
885 uint32_t pooling_height_{1};
886 uint32_t pooling_width_{1};
887 uint32_t stride_height_{1};
888 uint32_t stride_width_{1};
889 size_t next_input_height_{0};
890 size_t next_input_width_{0};
891 size_t next_batch_size_{0};
892 float input_scale_{1.0f};
893 float output_scale_{1.0f};
894 uint8_t input_zero_point_{121};
895 uint8_t output_zero_point_{133};
896 uint8_t qmin_{0};
897 uint8_t qmax_{255};
898 size_t iterations_{1};
899};