blob: f81fe8454d70991666b475e0d507f8da0d1c4c18 [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 <cstddef>
14#include <cstdlib>
15#include <algorithm>
16#include <cmath>
17#include <functional>
Marat Dukhan5ce30d92020-04-14 03:31:26 -070018#include <limits>
XNNPACK Teamb455b122019-09-27 18:10:33 -070019#include <random>
20#include <vector>
21
Frank Barchard7e2cbb02020-06-12 01:22:13 -070022#include <fp16.h>
23
XNNPACK Teamb455b122019-09-27 18:10:33 -070024#include <xnnpack.h>
25
26
27class GlobalAveragePoolingOperatorTester {
28 public:
29 inline GlobalAveragePoolingOperatorTester& channels(size_t channels) {
30 assert(channels != 0);
31 this->channels_ = channels;
32 return *this;
33 }
34
35 inline size_t channels() const {
36 return this->channels_;
37 }
38
39 inline GlobalAveragePoolingOperatorTester& width(size_t width) {
40 assert(width != 0);
41 this->width_ = width;
42 return *this;
43 }
44
45 inline size_t width() const {
46 return this->width_;
47 }
48
49 inline GlobalAveragePoolingOperatorTester& input_stride(size_t input_stride) {
50 assert(input_stride != 0);
51 this->input_stride_ = input_stride;
52 return *this;
53 }
54
55 inline size_t input_stride() const {
56 if (this->input_stride_ == 0) {
57 return channels();
58 } else {
59 assert(this->input_stride_ >= channels());
60 return this->input_stride_;
61 }
62 }
63
64 inline GlobalAveragePoolingOperatorTester& output_stride(size_t output_stride) {
65 assert(output_stride != 0);
66 this->output_stride_ = output_stride;
67 return *this;
68 }
69
70 inline size_t output_stride() const {
71 if (this->output_stride_ == 0) {
72 return channels();
73 } else {
74 assert(this->output_stride_ >= channels());
75 return this->output_stride_;
76 }
77 }
78
79 inline GlobalAveragePoolingOperatorTester& batch_size(size_t batch_size) {
80 assert(batch_size != 0);
81 this->batch_size_ = batch_size;
82 return *this;
83 }
84
85 inline size_t batch_size() const {
86 return this->batch_size_;
87 }
88
89 inline GlobalAveragePoolingOperatorTester& input_scale(float input_scale) {
90 assert(input_scale > 0.0f);
91 assert(std::isnormal(input_scale));
92 this->input_scale_ = input_scale;
93 return *this;
94 }
95
96 inline float input_scale() const {
97 return this->input_scale_;
98 }
99
100 inline GlobalAveragePoolingOperatorTester& input_zero_point(uint8_t input_zero_point) {
101 this->input_zero_point_ = input_zero_point;
102 return *this;
103 }
104
105 inline uint8_t input_zero_point() const {
106 return this->input_zero_point_;
107 }
108
109 inline GlobalAveragePoolingOperatorTester& output_scale(float output_scale) {
110 assert(output_scale > 0.0f);
111 assert(std::isnormal(output_scale));
112 this->output_scale_ = output_scale;
113 return *this;
114 }
115
116 inline float output_scale() const {
117 return this->output_scale_;
118 }
119
120 inline GlobalAveragePoolingOperatorTester& output_zero_point(uint8_t output_zero_point) {
121 this->output_zero_point_ = output_zero_point;
122 return *this;
123 }
124
125 inline uint8_t output_zero_point() const {
126 return this->output_zero_point_;
127 }
128
129 inline GlobalAveragePoolingOperatorTester& qmin(uint8_t qmin) {
130 this->qmin_ = qmin;
131 return *this;
132 }
133
134 inline uint8_t qmin() const {
135 return this->qmin_;
136 }
137
138 inline GlobalAveragePoolingOperatorTester& qmax(uint8_t qmax) {
139 this->qmax_ = qmax;
140 return *this;
141 }
142
143 inline uint8_t qmax() const {
144 return this->qmax_;
145 }
146
147 inline GlobalAveragePoolingOperatorTester& iterations(size_t iterations) {
148 this->iterations_ = iterations;
149 return *this;
150 }
151
152 inline size_t iterations() const {
153 return this->iterations_;
154 }
155
Marat Dukhanefc47b82019-11-18 09:25:38 -0800156 void TestNWCxQ8() const {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700157 std::random_device random_device;
158 auto rng = std::mt19937(random_device());
Marat Dukhan5ce30d92020-04-14 03:31:26 -0700159 auto u8rng = std::bind(std::uniform_int_distribution<uint32_t>(0, std::numeric_limits<uint8_t>::max()), rng);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700160
161 std::vector<uint8_t> input((batch_size() * width() - 1) * input_stride() + channels() + XNN_EXTRA_BYTES / sizeof(uint8_t));
162 std::vector<uint8_t> output(batch_size() * output_stride());
163 std::vector<float> output_ref(batch_size() * channels());
164 for (size_t iteration = 0; iteration < iterations(); iteration++) {
165 std::generate(input.begin(), input.end(), std::ref(u8rng));
166 std::fill(output.begin(), output.end(), 0xA5);
167
168 // Compute reference results.
169 const double scale = double(input_scale()) / (double(width()) * double(output_scale()));
170 for (size_t i = 0; i < batch_size(); i++) {
171 for (size_t j = 0; j < channels(); j++) {
172 double acc = 0.0f;
173 for (size_t k = 0; k < width(); k++) {
174 acc += double(int32_t(input[(i * width() + k) * input_stride() + j]) - int32_t(input_zero_point()));
175 }
176 output_ref[i * channels() + j] = float(acc * scale + double(output_zero_point()));
177 output_ref[i * channels() + j] = std::min<float>(output_ref[i * channels() + j], float(qmax()));
178 output_ref[i * channels() + j] = std::max<float>(output_ref[i * channels() + j], float(qmin()));
179 }
180 }
181
182 // Create, setup, run, and destroy Global Average Pooling operator.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800183 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700184 xnn_operator_t global_average_pooling_op = nullptr;
185
186 ASSERT_EQ(xnn_status_success,
187 xnn_create_global_average_pooling_nwc_q8(
188 channels(), input_stride(), output_stride(),
189 input_zero_point(), input_scale(),
190 output_zero_point(), output_scale(),
191 qmin(), qmax(),
192 0, &global_average_pooling_op));
193 ASSERT_NE(nullptr, global_average_pooling_op);
194
195 // Smart pointer to automatically delete global_average_pooling_op.
196 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_global_average_pooling_op(global_average_pooling_op, xnn_delete_operator);
197
198 ASSERT_EQ(xnn_status_success,
199 xnn_setup_global_average_pooling_nwc_q8(
200 global_average_pooling_op,
201 batch_size(), width(),
202 input.data(), output.data(),
203 nullptr /* thread pool */));
204
205 ASSERT_EQ(xnn_status_success,
206 xnn_run_operator(global_average_pooling_op, nullptr /* thread pool */));
207
208 // Verify results.
209 for (size_t i = 0; i < batch_size(); i++) {
210 for (size_t c = 0; c < channels(); c++) {
211 ASSERT_LE(uint32_t(output[i * output_stride() + c]), uint32_t(qmax()));
212 ASSERT_GE(uint32_t(output[i * output_stride() + c]), uint32_t(qmin()));
213 ASSERT_NEAR(float(int32_t(output[i * output_stride() + c])), output_ref[i * channels() + c], 0.80f) <<
214 "in batch index " << i << ", channel " << c;
215 }
216 }
217 }
218 }
219
Frank Barchard7e2cbb02020-06-12 01:22:13 -0700220 void TestNWCxF16() const {
221 std::random_device random_device;
222 auto rng = std::mt19937(random_device());
223 auto f32rng = std::bind(std::uniform_real_distribution<float>(0.1f, 1.0f), rng);
224 auto f16rng = std::bind(fp16_ieee_from_fp32_value, f32rng);
225
226 std::vector<uint16_t> input((batch_size() * width() - 1) * input_stride() + channels() + XNN_EXTRA_BYTES / sizeof(uint16_t));
227 std::vector<uint16_t> output(batch_size() * output_stride());
228 std::vector<float> output_ref(batch_size() * channels());
229 for (size_t iteration = 0; iteration < iterations(); iteration++) {
230 std::generate(input.begin(), input.end(), std::ref(f16rng));
231 std::fill(output.begin(), output.end(), UINT16_C(0x7E00) /* NaN */);
232
233 // Compute reference results, without clamping.
234 for (size_t i = 0; i < batch_size(); i++) {
235 for (size_t j = 0; j < channels(); j++) {
236 float acc = 0.0f;
237 for (size_t k = 0; k < width(); k++) {
238 acc += fp16_ieee_to_fp32_value(input[(i * width() + k) * input_stride() + j]);
239 }
240 output_ref[i * channels() + j] = acc / float(width());
241 }
242 }
243
244 // Compute clamping parameters.
245 const float accumulated_min = *std::min_element(output_ref.cbegin(), output_ref.cend());
246 const float accumulated_max = *std::max_element(output_ref.cbegin(), output_ref.cend());
247 const float accumulated_range = accumulated_max - accumulated_min;
Frank Barchard39133702020-06-22 13:25:10 -0700248 const float scaled_min = fp16_ieee_to_fp32_value(fp16_ieee_from_fp32_value(accumulated_min + accumulated_range / 255.0f * float(qmin())));
249 const float scaled_max = fp16_ieee_to_fp32_value(fp16_ieee_from_fp32_value(accumulated_max - accumulated_range / 255.0f * float(255 - qmax())));
250 const float output_min = scaled_min == scaled_max ? -std::numeric_limits<float>::infinity() : scaled_min;
251 const float output_max = scaled_min == scaled_max ? +std::numeric_limits<float>::infinity() : scaled_max;
Frank Barchard7e2cbb02020-06-12 01:22:13 -0700252
253 // Clamp reference results.
254 for (float& value : output_ref) {
255 value = std::max(std::min(value, output_max), output_min);
256 }
257
258 // Create, setup, run, and destroy Global Average Pooling operator.
259 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
260 xnn_operator_t global_average_pooling_op = nullptr;
261
Marat Dukhanef61d022020-06-19 13:54:49 -0700262 xnn_status status = xnn_create_global_average_pooling_nwc_f16(
Frank Barchard7e2cbb02020-06-12 01:22:13 -0700263 channels(), input_stride(), output_stride(),
264 output_min, output_max,
Marat Dukhanef61d022020-06-19 13:54:49 -0700265 0, &global_average_pooling_op);
266 if (status == xnn_status_unsupported_hardware) {
267 GTEST_SKIP();
268 }
269 ASSERT_EQ(xnn_status_success, status);
Frank Barchard7e2cbb02020-06-12 01:22:13 -0700270 ASSERT_NE(nullptr, global_average_pooling_op);
271
272 // Smart pointer to automatically delete global_average_pooling_op.
273 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_global_average_pooling_op(global_average_pooling_op, xnn_delete_operator);
274
275 ASSERT_EQ(xnn_status_success,
276 xnn_setup_global_average_pooling_nwc_f16(
277 global_average_pooling_op,
278 batch_size(), width(),
279 input.data(), output.data(),
280 nullptr /* thread pool */));
281
282 ASSERT_EQ(xnn_status_success,
283 xnn_run_operator(global_average_pooling_op, nullptr /* thread pool */));
284
285 // Verify results.
286 for (size_t i = 0; i < batch_size(); i++) {
287 for (size_t c = 0; c < channels(); c++) {
288 ASSERT_LE(fp16_ieee_to_fp32_value(output[i * output_stride() + c]), output_max);
289 ASSERT_GE(fp16_ieee_to_fp32_value(output[i * output_stride() + c]), output_min);
290 ASSERT_NEAR(fp16_ieee_to_fp32_value(output[i * output_stride() + c]), output_ref[i * channels() + c], std::abs(output_ref[i * channels() + c]) * 1.0e-2f) <<
291 "in batch index " << i << ", channel " << c;
292 }
293 }
294 }
295 }
296
Marat Dukhanefc47b82019-11-18 09:25:38 -0800297 void TestNWCxF32() const {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700298 std::random_device random_device;
299 auto rng = std::mt19937(random_device());
300 auto f32rng = std::bind(std::uniform_real_distribution<float>(), rng);
301
302 std::vector<float> input((batch_size() * width() - 1) * input_stride() + channels() + XNN_EXTRA_BYTES / sizeof(float));
303 std::vector<float> output(batch_size() * output_stride());
304 std::vector<float> output_ref(batch_size() * channels());
305 for (size_t iteration = 0; iteration < iterations(); iteration++) {
306 std::generate(input.begin(), input.end(), std::ref(f32rng));
307 std::fill(output.begin(), output.end(), std::nanf(""));
308
309 // Compute reference results, without clamping.
310 for (size_t i = 0; i < batch_size(); i++) {
311 for (size_t j = 0; j < channels(); j++) {
312 float acc = 0.0f;
313 for (size_t k = 0; k < width(); k++) {
314 acc += input[(i * width() + k) * input_stride() + j];
315 }
316 output_ref[i * channels() + j] = acc / float(width());
317 }
318 }
319
320 // Compute clamping parameters.
321 const float accumulated_min = *std::min_element(output_ref.cbegin(), output_ref.cend());
322 const float accumulated_max = *std::max_element(output_ref.cbegin(), output_ref.cend());
323 const float accumulated_range = accumulated_max - accumulated_min;
324 const float output_min = accumulated_range == 0.0f ?
325 -std::numeric_limits<float>::infinity() :
326 accumulated_min + accumulated_range / 255.0f * float(qmin());
327 const float output_max = accumulated_range == 0.0f ?
328 +std::numeric_limits<float>::infinity() :
329 accumulated_max - accumulated_range / 255.0f * float(255 - qmax());
330
331 // Clamp reference results.
332 for (float& value : output_ref) {
333 value = std::max(std::min(value, output_max), output_min);
334 }
335
336 // Create, setup, run, and destroy Global Average Pooling operator.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800337 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700338 xnn_operator_t global_average_pooling_op = nullptr;
339
340 ASSERT_EQ(xnn_status_success,
341 xnn_create_global_average_pooling_nwc_f32(
342 channels(), input_stride(), output_stride(),
343 output_min, output_max,
344 0, &global_average_pooling_op));
345 ASSERT_NE(nullptr, global_average_pooling_op);
346
347 // Smart pointer to automatically delete global_average_pooling_op.
348 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_global_average_pooling_op(global_average_pooling_op, xnn_delete_operator);
349
350 ASSERT_EQ(xnn_status_success,
351 xnn_setup_global_average_pooling_nwc_f32(
352 global_average_pooling_op,
353 batch_size(), width(),
354 input.data(), output.data(),
355 nullptr /* thread pool */));
356
357 ASSERT_EQ(xnn_status_success,
358 xnn_run_operator(global_average_pooling_op, nullptr /* thread pool */));
359
360 // Verify results.
361 for (size_t i = 0; i < batch_size(); i++) {
362 for (size_t c = 0; c < channels(); c++) {
363 ASSERT_LE(output[i * output_stride() + c], output_max);
364 ASSERT_GE(output[i * output_stride() + c], output_min);
365 ASSERT_NEAR(output[i * output_stride() + c], output_ref[i * channels() + c], std::abs(output_ref[i * channels() + c]) * 1.0e-6f) <<
366 "in batch index " << i << ", channel " << c;
367 }
368 }
369 }
370 }
371
Marat Dukhanefc47b82019-11-18 09:25:38 -0800372 void TestNCWxF32() const {
373 std::random_device random_device;
374 auto rng = std::mt19937(random_device());
375 auto f32rng = std::bind(std::uniform_real_distribution<float>(), rng);
376
377 std::vector<float> input(batch_size() * channels() * width() + XNN_EXTRA_BYTES / sizeof(float));
378 std::vector<float> output(batch_size() * channels());
379 std::vector<float> output_ref(batch_size() * channels());
380 for (size_t iteration = 0; iteration < iterations(); iteration++) {
381 std::generate(input.begin(), input.end(), std::ref(f32rng));
382 std::fill(output.begin(), output.end(), std::nanf(""));
383
384 // Compute reference results, without clamping.
385 for (size_t i = 0; i < batch_size(); i++) {
386 for (size_t j = 0; j < channels(); j++) {
387 float acc = 0.0f;
388 for (size_t k = 0; k < width(); k++) {
389 acc += input[(i * channels() + j) * width() + k];
390 }
391 output_ref[i * channels() + j] = acc / float(width());
392 }
393 }
394
395 // Compute clamping parameters.
396 const float accumulated_min = *std::min_element(output_ref.cbegin(), output_ref.cend());
397 const float accumulated_max = *std::max_element(output_ref.cbegin(), output_ref.cend());
398 const float accumulated_range = accumulated_max - accumulated_min;
399 const float output_min = accumulated_range == 0.0f ?
400 -std::numeric_limits<float>::infinity() :
401 accumulated_min + accumulated_range / 255.0f * float(qmin());
402 const float output_max = accumulated_range == 0.0f ?
403 +std::numeric_limits<float>::infinity() :
404 accumulated_max - accumulated_range / 255.0f * float(255 - qmax());
405
406 // Clamp reference results.
407 for (float& value : output_ref) {
408 value = std::max(std::min(value, output_max), output_min);
409 }
410
411 // Create, setup, run, and destroy Global Average Pooling operator.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800412 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
Marat Dukhanefc47b82019-11-18 09:25:38 -0800413 xnn_operator_t global_average_pooling_op = nullptr;
414
415 xnn_status status = xnn_create_global_average_pooling_ncw_f32(
416 channels(), output_min, output_max,
417 0, &global_average_pooling_op);
418 if (status == xnn_status_unsupported_parameter) {
419 GTEST_SKIP();
420 }
421 ASSERT_EQ(xnn_status_success, status);
422
423 // Smart pointer to automatically delete global_average_pooling_op.
424 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_global_average_pooling_op(global_average_pooling_op, xnn_delete_operator);
425
426 ASSERT_EQ(xnn_status_success,
427 xnn_setup_global_average_pooling_ncw_f32(
428 global_average_pooling_op,
429 batch_size(), width(),
430 input.data(), output.data(),
431 nullptr /* thread pool */));
432
433 ASSERT_EQ(xnn_status_success,
434 xnn_run_operator(global_average_pooling_op, nullptr /* thread pool */));
435
436 // Verify results.
437 for (size_t i = 0; i < batch_size(); i++) {
438 for (size_t c = 0; c < channels(); c++) {
439 ASSERT_LE(output[i * channels() + c], output_max);
440 ASSERT_GE(output[i * channels() + c], output_min);
441 ASSERT_NEAR(output[i * channels() + c], output_ref[i * channels() + c], std::abs(output_ref[i * channels() + c]) * 1.0e-5f) <<
442 "in batch index " << i << ", channel " << c;
443 }
444 }
445 }
446 }
447
XNNPACK Teamb455b122019-09-27 18:10:33 -0700448 private:
449 size_t batch_size_{1};
450 size_t width_{1};
451 size_t channels_{1};
452 size_t input_stride_{0};
453 size_t output_stride_{0};
454 float input_scale_{1.0f};
455 float output_scale_{1.0f};
456 uint8_t input_zero_point_{121};
457 uint8_t output_zero_point_{133};
458 uint8_t qmin_{0};
459 uint8_t qmax_{255};
460 size_t iterations_{1};
461};