blob: 3e47a533e2003cbaf72051da36071f3ac121505a [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 <cassert>
15#include <cstddef>
16#include <cstdlib>
17#include <functional>
18#include <limits>
19#include <random>
20#include <vector>
21
22#include <xnnpack.h>
23
24
25class MaxPoolingOperatorTester {
26 public:
27 inline MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& 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 MaxPoolingOperatorTester& dilation(uint32_t dilation) {
210 assert(dilation >= 1);
211 this->dilation_height_ = dilation;
212 this->dilation_width_ = dilation;
213 return *this;
214 }
215
216 inline MaxPoolingOperatorTester& dilation(uint32_t dilation_height, uint32_t dilation_width) {
217 assert(dilation_height >= 1);
218 assert(dilation_width >= 1);
219 this->dilation_height_ = dilation_height;
220 this->dilation_width_ = dilation_width;
221 return *this;
222 }
223
224 inline MaxPoolingOperatorTester& dilation_height(uint32_t dilation_height) {
225 assert(dilation_height >= 1);
226 this->dilation_height_ = dilation_height;
227 return *this;
228 }
229
230 inline uint32_t dilation_height() const {
231 return this->dilation_height_;
232 }
233
234 inline MaxPoolingOperatorTester& dilation_width(uint32_t dilation_width) {
235 assert(dilation_width >= 1);
236 this->dilation_width_ = dilation_width;
237 return *this;
238 }
239
240 inline uint32_t dilation_width() const {
241 return this->dilation_width_;
242 }
243
244 inline uint32_t dilated_pooling_height() const {
245 return (pooling_height() - 1) * dilation_height() + 1;
246 }
247
248 inline uint32_t dilated_pooling_width() const {
249 return (pooling_width() - 1) * dilation_width() + 1;
250 }
251
252 inline size_t output_height() const {
253 const size_t padded_input_height = padding_top() + input_height() + padding_bottom();
254 if (padded_input_height <= dilated_pooling_height()) {
255 return 1;
256 } else {
257 return (padded_input_height - dilated_pooling_height()) / stride_height() + 1;
258 }
259 }
260
261 inline size_t output_width() const {
262 const size_t padded_input_width = padding_left() + input_width() + padding_right();
263 if (padded_input_width <= dilated_pooling_width()) {
264 return 1;
265 } else {
266 return (padded_input_width - dilated_pooling_width()) / stride_width() + 1;
267 }
268 }
269
270 inline MaxPoolingOperatorTester& input_pixel_stride(size_t input_pixel_stride) {
271 assert(input_pixel_stride != 0);
272 this->input_pixel_stride_ = input_pixel_stride;
273 return *this;
274 }
275
276 inline size_t input_pixel_stride() const {
277 if (this->input_pixel_stride_ == 0) {
278 return channels();
279 } else {
280 assert(this->input_pixel_stride_ >= channels());
281 return this->input_pixel_stride_;
282 }
283 }
284
285 inline MaxPoolingOperatorTester& output_pixel_stride(size_t output_pixel_stride) {
286 assert(output_pixel_stride != 0);
287 this->output_pixel_stride_ = output_pixel_stride;
288 return *this;
289 }
290
291 inline size_t output_pixel_stride() const {
292 if (this->output_pixel_stride_ == 0) {
293 return channels();
294 } else {
295 assert(this->output_pixel_stride_ >= channels());
296 return this->output_pixel_stride_;
297 }
298 }
299
300 inline MaxPoolingOperatorTester& next_input_size(uint32_t next_input_height, uint32_t next_input_width) {
301 assert(next_input_height >= 1);
302 assert(next_input_width >= 1);
303 this->next_input_height_ = next_input_height;
304 this->next_input_width_ = next_input_width;
305 return *this;
306 }
307
308 inline MaxPoolingOperatorTester& next_input_height(uint32_t next_input_height) {
309 assert(next_input_height >= 1);
310 this->next_input_height_ = next_input_height;
311 return *this;
312 }
313
314 inline uint32_t next_input_height() const {
315 if (this->next_input_height_ == 0) {
316 return input_height();
317 } else {
318 return this->next_input_height_;
319 }
320 }
321
322 inline MaxPoolingOperatorTester& next_input_width(uint32_t next_input_width) {
323 assert(next_input_width >= 1);
324 this->next_input_width_ = next_input_width;
325 return *this;
326 }
327
328 inline uint32_t next_input_width() const {
329 if (this->next_input_width_ == 0) {
330 return input_width();
331 } else {
332 return this->next_input_width_;
333 }
334 }
335
336 inline size_t next_output_height() const {
337 const size_t padded_next_input_height = padding_top() + next_input_height() + padding_bottom();
338 if (padded_next_input_height <= dilated_pooling_height()) {
339 return 1;
340 } else {
341 return (padded_next_input_height - dilated_pooling_height()) / stride_height() + 1;
342 }
343 }
344
345 inline size_t next_output_width() const {
346 const size_t padded_next_input_width = padding_left() + next_input_width() + padding_right();
347 if (padded_next_input_width <= dilated_pooling_width()) {
348 return 1;
349 } else {
350 return (padded_next_input_width - dilated_pooling_width()) / stride_width() + 1;
351 }
352 }
353
354 inline MaxPoolingOperatorTester& next_batch_size(size_t next_batch_size) {
355 assert(next_batch_size >= 1);
356 this->next_batch_size_ = next_batch_size;
357 return *this;
358 }
359
360 inline size_t next_batch_size() const {
361 if (this->next_batch_size_ == 0) {
362 return batch_size();
363 } else {
364 return this->next_batch_size_;
365 }
366 }
367
368 inline MaxPoolingOperatorTester& qmin(uint8_t qmin) {
369 this->qmin_ = qmin;
370 return *this;
371 }
372
373 inline uint8_t qmin() const {
374 return this->qmin_;
375 }
376
377 inline MaxPoolingOperatorTester& qmax(uint8_t qmax) {
378 this->qmax_ = qmax;
379 return *this;
380 }
381
382 inline uint8_t qmax() const {
383 return this->qmax_;
384 }
385
386 inline MaxPoolingOperatorTester& iterations(size_t iterations) {
387 this->iterations_ = iterations;
388 return *this;
389 }
390
391 inline size_t iterations() const {
392 return this->iterations_;
393 }
394
395 void TestU8() const {
396 std::random_device random_device;
397 auto rng = std::mt19937(random_device());
398 auto u8rng = std::bind(std::uniform_int_distribution<uint8_t>(), rng);
399
400 std::vector<uint8_t> input((batch_size() * input_height() * input_width() - 1) * input_pixel_stride() + channels() + XNN_EXTRA_BYTES / sizeof(uint8_t));
401 std::vector<uint8_t> output((batch_size() * output_height() * output_width() - 1) * output_pixel_stride() + channels() + XNN_EXTRA_BYTES / sizeof(uint8_t));
402 std::vector<uint8_t> output_ref(batch_size() * output_height() * output_width() * channels());
403 for (size_t iteration = 0; iteration < iterations(); iteration++) {
404 std::generate(input.begin(), input.end(), std::ref(u8rng));
405 std::fill(output.begin(), output.end(), 0xA5);
406
407 // Compute reference results.
408 for (size_t i = 0; i < batch_size(); i++) {
409 for (size_t oy = 0; oy < output_height(); oy++) {
410 for (size_t ox = 0; ox < output_width(); ox++) {
411 for (size_t c = 0; c < channels(); c++) {
412 uint8_t max_value = 0;
413 for (size_t py = 0; py < pooling_height(); py++) {
414 const size_t iy = oy * stride_height() + py * dilation_height() - padding_top();
415 for (size_t px = 0; px < pooling_width(); px++) {
416 const size_t ix = ox * stride_width() + px * dilation_width() - padding_left();
Marat Dukhane0df8312019-10-22 18:16:56 -0700417 if (ix < input_width() && iy < input_height()) {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700418 max_value = std::max(max_value,
419 input[((i * input_height() + iy) * input_width() + ix) * input_pixel_stride() + c]);
420 }
421 }
422 }
423 max_value = std::min(max_value, qmax());
424 max_value = std::max(max_value, qmin());
425 output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] = max_value;
426 }
427 }
428 }
429 }
430
431 // Create, setup, run, and destroy Max Pooling operator.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800432 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700433 xnn_operator_t max_pooling_op = nullptr;
434
435 ASSERT_EQ(xnn_status_success,
436 xnn_create_max_pooling2d_nhwc_u8(
437 padding_top(), padding_right(), padding_bottom(), padding_left(),
438 pooling_height(), pooling_width(),
439 stride_height(), stride_width(),
440 dilation_height(), dilation_width(),
441 channels(), input_pixel_stride(), output_pixel_stride(),
442 qmin(), qmax(),
443 0, &max_pooling_op));
444 ASSERT_NE(nullptr, max_pooling_op);
445
446 // Smart pointer to automatically delete max_pooling_op.
447 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_max_pooling_op(max_pooling_op, xnn_delete_operator);
448
449 ASSERT_EQ(xnn_status_success,
450 xnn_setup_max_pooling2d_nhwc_u8(
451 max_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(max_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_EQ(uint32_t(output_ref[((i * output_height() + y) * output_width() + x) * channels() + c]),
467 uint32_t(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c])) <<
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>(0.0f, 1.0f), 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() + XNN_EXTRA_BYTES / sizeof(float));
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(), 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 max_value = -std::numeric_limits<float>::infinity();
494 for (size_t py = 0; py < pooling_height(); py++) {
495 const size_t iy = oy * stride_height() + py * dilation_height() - padding_top();
496 for (size_t px = 0; px < pooling_width(); px++) {
497 const size_t ix = ox * stride_width() + px * dilation_width() - padding_left();
498 if (ix < input_width() && iy < input_height()) {
499 max_value = std::max(max_value,
500 input[((i * input_height() + iy) * input_width() + ix) * input_pixel_stride() + c]);
501 }
502 }
503 }
504 output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] = max_value;
505 }
506 }
507 }
508 }
509
510 // Compute clamping parameters.
511 const float accumulated_min = *std::min_element(output_ref.cbegin(), output_ref.cend());
512 const float accumulated_max = *std::max_element(output_ref.cbegin(), output_ref.cend());
513 const float accumulated_range = accumulated_max - accumulated_min;
514 const float output_min = accumulated_range == 0.0f ?
515 -std::numeric_limits<float>::infinity() :
516 accumulated_min + accumulated_range / 255.0f * float(qmin());
517 const float output_max = accumulated_range == 0.0f ?
518 +std::numeric_limits<float>::infinity() :
519 accumulated_max - accumulated_range / 255.0f * float(255 - qmax());
520
521 // Clamp reference results.
522 for (float& value : output_ref) {
523 value = std::max(std::min(value, output_max), output_min);
524 }
525
526 // Create, setup, run, and destroy Max Pooling operator.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800527 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700528 xnn_operator_t max_pooling_op = nullptr;
529
530 ASSERT_EQ(xnn_status_success,
531 xnn_create_max_pooling2d_nhwc_f32(
532 padding_top(), padding_right(), padding_bottom(), padding_left(),
533 pooling_height(), pooling_width(),
534 stride_height(), stride_width(),
535 dilation_height(), dilation_width(),
536 channels(), input_pixel_stride(), output_pixel_stride(),
537 output_min, output_max,
538 0, &max_pooling_op));
539 ASSERT_NE(nullptr, max_pooling_op);
540
541 // Smart pointer to automatically delete max_pooling_op.
542 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_max_pooling_op(max_pooling_op, xnn_delete_operator);
543
544 ASSERT_EQ(xnn_status_success,
545 xnn_setup_max_pooling2d_nhwc_f32(
546 max_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(max_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_EQ(output_ref[((i * output_height() + y) * output_width() + x) * channels() + c],
562 output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c]) <<
563 "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c
564 << ", min = " << output_min << ", max = " << output_max;
565 }
566 }
567 }
568 }
569 }
570 }
571
572 void TestSetupU8() 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(XNN_EXTRA_BYTES / sizeof(uint8_t) + 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 for (size_t i = 0; i < batch_size(); i++) {
591 for (size_t oy = 0; oy < output_height(); oy++) {
592 for (size_t ox = 0; ox < output_width(); ox++) {
593 for (size_t c = 0; c < channels(); c++) {
594 uint8_t max_value = 0;
595 for (size_t py = 0; py < pooling_height(); py++) {
596 const size_t iy = oy * stride_height() + py * dilation_height() - padding_top();
597 for (size_t px = 0; px < pooling_width(); px++) {
598 const size_t ix = ox * stride_width() + px * dilation_width() - padding_left();
599 if (ix < input_width() && iy < input_height()) {
600 max_value = std::max(max_value,
601 input[((i * input_height() + iy) * input_width() + ix) * input_pixel_stride() + c]);
602 }
603 }
604 }
605 max_value = std::min(max_value, qmax());
606 max_value = std::max(max_value, qmin());
607 output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] = max_value;
608 }
609 }
610 }
611 }
612
613 // Create, setup, and run Max Pooling operator once.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800614 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700615 xnn_operator_t max_pooling_op = nullptr;
616
617 ASSERT_EQ(xnn_status_success,
618 xnn_create_max_pooling2d_nhwc_u8(
619 padding_top(), padding_right(), padding_bottom(), padding_left(),
620 pooling_height(), pooling_width(),
621 stride_height(), stride_width(),
622 dilation_height(), dilation_width(),
623 channels(), input_pixel_stride(), output_pixel_stride(),
624 qmin(), qmax(),
625 0, &max_pooling_op));
626 ASSERT_NE(nullptr, max_pooling_op);
627
628 // Smart pointer to automatically delete max_pooling_op.
629 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_max_pooling_op(max_pooling_op, xnn_delete_operator);
630
631 ASSERT_EQ(xnn_status_success,
632 xnn_setup_max_pooling2d_nhwc_u8(
633 max_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(max_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_EQ(uint32_t(output_ref[((i * output_height() + y) * output_width() + x) * channels() + c]),
649 uint32_t(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c])) <<
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 uint8_t max_value = 0;
666 for (size_t py = 0; py < pooling_height(); py++) {
667 const size_t iy = oy * stride_height() + py * dilation_height() - padding_top();
668 for (size_t px = 0; px < pooling_width(); px++) {
669 const size_t ix = ox * stride_width() + px * dilation_width() - padding_left();
670 if (ix < next_input_width() && iy < next_input_height()) {
671 max_value = std::max(max_value,
672 input[((i * next_input_height() + iy) * next_input_width() + ix) * input_pixel_stride() + c]);
673 }
674 }
675 }
676 max_value = std::min(max_value, qmax());
677 max_value = std::max(max_value, qmin());
678 next_output_ref[((i * next_output_height() + oy) * next_output_width() + ox) * channels() + c] = max_value;
679 }
680 }
681 }
682 }
683
684 // Setup and run Max Pooling operator the second time, and destroy the operator.
685 ASSERT_EQ(xnn_status_success,
686 xnn_setup_max_pooling2d_nhwc_u8(
687 max_pooling_op,
688 next_batch_size(), next_input_height(), next_input_width(),
689 input.data(), output.data(),
690 nullptr /* thread pool */));
691
692 ASSERT_EQ(xnn_status_success,
693 xnn_run_operator(max_pooling_op, nullptr /* thread pool */));
694
695 // Verify results of the second run.
696 for (size_t i = 0; i < next_batch_size(); i++) {
697 for (size_t y = 0; y < next_output_height(); y++) {
698 for (size_t x = 0; x < next_output_width(); x++) {
699 for (size_t c = 0; c < channels(); c++) {
700 ASSERT_LE(uint32_t(output[((i * next_output_height() + y) * next_output_width() + x) * output_pixel_stride() + c]), uint32_t(qmax()));
701 ASSERT_GE(uint32_t(output[((i * next_output_height() + y) * next_output_width() + x) * output_pixel_stride() + c]), uint32_t(qmin()));
702 ASSERT_EQ(uint32_t(next_output_ref[((i * next_output_height() + y) * next_output_width() + x) * channels() + c]),
703 uint32_t(output[((i * next_output_height() + y) * next_output_width() + x) * output_pixel_stride() + c])) <<
704 "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
705 }
706 }
707 }
708 }
709 }
710 }
711
712 void TestSetupF32() const {
713 std::random_device random_device;
714 auto rng = std::mt19937(random_device());
715 auto f32rng = std::bind(std::uniform_real_distribution<float>(0.0f, 1.0f), rng);
716
717 std::vector<float> input(XNN_EXTRA_BYTES / sizeof(float) + std::max(
718 (batch_size() * input_height() * input_width() - 1) * input_pixel_stride() + channels(),
719 (next_batch_size() * next_input_height() * next_input_width() - 1) * input_pixel_stride() + channels()));
720 std::vector<float> output(XNN_EXTRA_BYTES / sizeof(float) + std::max(
721 (batch_size() * output_height() * output_width() - 1) * output_pixel_stride() + channels(),
722 (next_batch_size() * next_output_height() * next_output_width() - 1) * output_pixel_stride() + channels()));
723 std::vector<float> output_ref(batch_size() * output_height() * output_width() * channels());
724 std::vector<float> next_output_ref(next_batch_size() * next_output_height() * next_output_width() * channels());
725 for (size_t iteration = 0; iteration < iterations(); iteration++) {
726 std::generate(input.begin(), input.end(), std::ref(f32rng));
727 std::fill(output.begin(), output.end(), nanf(""));
728
729 // Compute reference results, without clamping.
730 for (size_t i = 0; i < batch_size(); i++) {
731 for (size_t oy = 0; oy < output_height(); oy++) {
732 for (size_t ox = 0; ox < output_width(); ox++) {
733 for (size_t c = 0; c < channels(); c++) {
734 float max_value = -std::numeric_limits<float>::infinity();
735 for (size_t py = 0; py < pooling_height(); py++) {
736 const size_t iy = oy * stride_height() + py * dilation_height() - padding_top();
737 for (size_t px = 0; px < pooling_width(); px++) {
738 const size_t ix = ox * stride_width() + px * dilation_width() - padding_left();
739 if (ix < input_width() && iy < input_height()) {
740 max_value = std::max(max_value,
741 input[((i * input_height() + iy) * input_width() + ix) * input_pixel_stride() + c]);
742 }
743 }
744 }
745 output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] = max_value;
746 }
747 }
748 }
749 }
750
751 // Compute clamping parameters.
752 const float accumulated_min = *std::min_element(output_ref.cbegin(), output_ref.cend());
753 const float accumulated_max = *std::max_element(output_ref.cbegin(), output_ref.cend());
754 const float accumulated_range = accumulated_max - accumulated_min;
755 const float output_min = accumulated_range == 0.0f ?
756 -std::numeric_limits<float>::infinity() :
757 accumulated_min + accumulated_range / 255.0f * float(qmin());
758 const float output_max = accumulated_range == 0.0f ?
759 +std::numeric_limits<float>::infinity() :
760 accumulated_max - accumulated_range / 255.0f * float(255 - qmax());
761
762 // Clamp reference results.
763 for (float& value : output_ref) {
764 value = std::max(std::min(value, output_max), output_min);
765 }
766
767 // Create, setup, and run Max Pooling operator once.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800768 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700769 xnn_operator_t max_pooling_op = nullptr;
770
771 ASSERT_EQ(xnn_status_success,
772 xnn_create_max_pooling2d_nhwc_f32(
773 padding_top(), padding_right(), padding_bottom(), padding_left(),
774 pooling_height(), pooling_width(),
775 stride_height(), stride_width(),
776 dilation_height(), dilation_width(),
777 channels(), input_pixel_stride(), output_pixel_stride(),
778 output_min, output_max,
779 0, &max_pooling_op));
780 ASSERT_NE(nullptr, max_pooling_op);
781
782 // Smart pointer to automatically delete max_pooling_op.
783 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_max_pooling_op(max_pooling_op, xnn_delete_operator);
784
785 ASSERT_EQ(xnn_status_success,
786 xnn_setup_max_pooling2d_nhwc_f32(
787 max_pooling_op,
788 batch_size(), input_height(), input_width(),
789 input.data(), output.data(),
790 nullptr /* thread pool */));
791
792 ASSERT_EQ(xnn_status_success,
793 xnn_run_operator(max_pooling_op, nullptr /* thread pool */));
794
795 // Verify results of the first run.
796 for (size_t i = 0; i < batch_size(); i++) {
797 for (size_t y = 0; y < output_height(); y++) {
798 for (size_t x = 0; x < output_width(); x++) {
799 for (size_t c = 0; c < channels(); c++) {
800 ASSERT_LE(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c], output_max);
801 ASSERT_GE(output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c], output_min);
802 ASSERT_EQ(output_ref[((i * output_height() + y) * output_width() + x) * channels() + c],
803 output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c]) <<
804 "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
805 }
806 }
807 }
808 }
809
810 // Re-generate data for the second run.
811 std::generate(input.begin(), input.end(), std::ref(f32rng));
812 std::fill(output.begin(), output.end(), 0xA5);
813
814 // Compute reference results for the second run, including clamping.
815 for (size_t i = 0; i < next_batch_size(); i++) {
816 for (size_t oy = 0; oy < next_output_height(); oy++) {
817 for (size_t ox = 0; ox < next_output_width(); ox++) {
818 for (size_t c = 0; c < channels(); c++) {
819 float max_value = -std::numeric_limits<float>::infinity();
820 for (size_t py = 0; py < pooling_height(); py++) {
821 const size_t iy = oy * stride_height() + py * dilation_height() - padding_top();
822 for (size_t px = 0; px < pooling_width(); px++) {
823 const size_t ix = ox * stride_width() + px * dilation_width() - padding_left();
824 if (ix < next_input_width() && iy < next_input_height()) {
825 max_value = std::max(max_value,
826 input[((i * next_input_height() + iy) * next_input_width() + ix) * input_pixel_stride() + c]);
827 }
828 }
829 }
830 max_value = std::min(max_value, output_max);
831 max_value = std::max(max_value, output_min);
832 next_output_ref[((i * next_output_height() + oy) * next_output_width() + ox) * channels() + c] = max_value;
833 }
834 }
835 }
836 }
837
838 // Setup and run Max Pooling operator the second time, and destroy the operator.
839 ASSERT_EQ(xnn_status_success,
840 xnn_setup_max_pooling2d_nhwc_f32(
841 max_pooling_op,
842 next_batch_size(), next_input_height(), next_input_width(),
843 input.data(), output.data(),
844 nullptr /* thread pool */));
845
846 ASSERT_EQ(xnn_status_success,
847 xnn_run_operator(max_pooling_op, nullptr /* thread pool */));
848
849 // Verify results of the second run.
850 for (size_t i = 0; i < next_batch_size(); i++) {
851 for (size_t y = 0; y < next_output_height(); y++) {
852 for (size_t x = 0; x < next_output_width(); x++) {
853 for (size_t c = 0; c < channels(); c++) {
854 ASSERT_LE(output[((i * next_output_height() + y) * next_output_width() + x) * output_pixel_stride() + c], output_max);
855 ASSERT_GE(output[((i * next_output_height() + y) * next_output_width() + x) * output_pixel_stride() + c], output_min);
856 ASSERT_EQ(next_output_ref[((i * next_output_height() + y) * next_output_width() + x) * channels() + c],
857 output[((i * next_output_height() + y) * next_output_width() + x) * output_pixel_stride() + c]) <<
858 "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
859 }
860 }
861 }
862 }
863 }
864 }
865
866 private:
867 uint32_t padding_top_{0};
868 uint32_t padding_right_{0};
869 uint32_t padding_bottom_{0};
870 uint32_t padding_left_{0};
871 size_t input_height_{1};
872 size_t input_width_{1};
873 size_t channels_{1};
874 size_t batch_size_{1};
875 size_t input_pixel_stride_{0};
876 size_t output_pixel_stride_{0};
877 uint32_t pooling_height_{1};
878 uint32_t pooling_width_{1};
879 uint32_t stride_height_{1};
880 uint32_t stride_width_{1};
881 uint32_t dilation_height_{1};
882 uint32_t dilation_width_{1};
883 size_t next_input_height_{0};
884 size_t next_input_width_{0};
885 size_t next_batch_size_{0};
886 uint8_t qmin_{0};
887 uint8_t qmax_{255};
888 size_t iterations_{1};
889};