blob: 56d727e8fb242362d7cabaeccef7efe52844f534 [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 <cmath>
16#include <cstddef>
17#include <cstdlib>
18#include <functional>
Marat Dukhan5ce30d92020-04-14 03:31:26 -070019#include <limits>
XNNPACK Teamb455b122019-09-27 18:10:33 -070020#include <random>
21#include <vector>
22
23#include <xnnpack.h>
24
25
26class SigmoidOperatorTester {
27 public:
28 inline SigmoidOperatorTester& channels(size_t channels) {
29 assert(channels != 0);
30 this->channels_ = channels;
31 return *this;
32 }
33
34 inline size_t channels() const {
35 return this->channels_;
36 }
37
38 inline SigmoidOperatorTester& input_stride(size_t input_stride) {
39 assert(input_stride != 0);
40 this->input_stride_ = input_stride;
41 return *this;
42 }
43
44 inline size_t input_stride() const {
45 if (this->input_stride_ == 0) {
46 return this->channels_;
47 } else {
48 assert(this->input_stride_ >= this->channels_);
49 return this->input_stride_;
50 }
51 }
52
53 inline SigmoidOperatorTester& output_stride(size_t output_stride) {
54 assert(output_stride != 0);
55 this->output_stride_ = output_stride;
56 return *this;
57 }
58
59 inline size_t output_stride() const {
60 if (this->output_stride_ == 0) {
61 return this->channels_;
62 } else {
63 assert(this->output_stride_ >= this->channels_);
64 return this->output_stride_;
65 }
66 }
67
68 inline SigmoidOperatorTester& batch_size(size_t batch_size) {
69 assert(batch_size != 0);
70 this->batch_size_ = batch_size;
71 return *this;
72 }
73
74 inline size_t batch_size() const {
75 return this->batch_size_;
76 }
77
78 inline SigmoidOperatorTester& input_scale(float input_scale) {
79 assert(input_scale > 0.0f);
80 assert(std::isnormal(input_scale));
81 this->input_scale_ = input_scale;
82 return *this;
83 }
84
85 inline float input_scale() const {
86 return this->input_scale_;
87 }
88
89 inline SigmoidOperatorTester& input_zero_point(uint8_t input_zero_point) {
90 this->input_zero_point_ = input_zero_point;
91 return *this;
92 }
93
94 inline uint8_t input_zero_point() const {
95 return this->input_zero_point_;
96 }
97
98 inline float output_scale() const {
99 return 1.0f / 256.0f;
100 }
101
102 inline uint8_t output_zero_point() const {
103 return 0;
104 }
105
106 inline SigmoidOperatorTester& qmin(uint8_t qmin) {
107 this->qmin_ = qmin;
108 return *this;
109 }
110
111 inline uint8_t qmin() const {
112 return this->qmin_;
113 }
114
115 inline SigmoidOperatorTester& qmax(uint8_t qmax) {
116 this->qmax_ = qmax;
117 return *this;
118 }
119
120 inline uint8_t qmax() const {
121 return this->qmax_;
122 }
123
124 inline SigmoidOperatorTester& iterations(size_t iterations) {
125 this->iterations_ = iterations;
126 return *this;
127 }
128
129 inline size_t iterations() const {
130 return this->iterations_;
131 }
132
Marat Dukhan71a9bb12021-09-09 08:54:18 -0700133 void TestQS8() const {
134 std::random_device random_device;
135 auto rng = std::mt19937(random_device());
136 auto i8rng = std::bind(
137 std::uniform_int_distribution<int32_t>(std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max()),
138 std::ref(rng));
139
140 std::vector<int8_t> input((batch_size() - 1) * input_stride() + channels() + XNN_EXTRA_BYTES / sizeof(int8_t));
141 std::vector<int8_t> output((batch_size() - 1) * output_stride() + channels());
142 std::vector<float> output_ref(batch_size() * channels());
143 for (size_t iteration = 0; iteration < iterations(); iteration++) {
144 std::generate(input.begin(), input.end(), std::ref(i8rng));
145 std::fill(output.begin(), output.end(), 0xA5);
146
147 // Compute reference results.
148 for (size_t i = 0; i < batch_size(); i++) {
149 for (size_t c = 0; c < channels(); c++) {
150 const float x = input_scale() *
151 (int32_t(input[i * input_stride() + c]) - int32_t(input_zero_point() - 0x80));
152 const float sigmoid_x = 1.0f / (1.0f + std::exp(-x));
153 const float scaled_sigmoid_x = sigmoid_x / output_scale();
154 float y = scaled_sigmoid_x;
155 y = std::min<float>(y, int32_t(qmax() - 0x80) - int32_t(output_zero_point() - 0x80));
156 y = std::max<float>(y, int32_t(qmin() - 0x80) - int32_t(output_zero_point() - 0x80));
157 output_ref[i * channels() + c] = y + int32_t(output_zero_point() - 0x80);
158 }
159 }
160
161 // Create, setup, run, and destroy Sigmoid operator.
162 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
163 xnn_operator_t sigmoid_op = nullptr;
164
165 ASSERT_EQ(xnn_status_success,
166 xnn_create_sigmoid_nc_qs8(
167 channels(), input_stride(), output_stride(),
168 int8_t(input_zero_point() - 0x80), input_scale(),
169 int8_t(output_zero_point() - 0x80), output_scale(),
170 int8_t(qmin() - 0x80), int8_t(qmax() - 0x80),
171 0, &sigmoid_op));
172 ASSERT_NE(nullptr, sigmoid_op);
173
174 // Smart pointer to automatically delete sigmoid_op.
175 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_sigmoid_op(sigmoid_op, xnn_delete_operator);
176
177 ASSERT_EQ(xnn_status_success,
178 xnn_setup_sigmoid_nc_qs8(
179 sigmoid_op,
180 batch_size(),
181 input.data(), output.data(),
182 nullptr /* thread pool */));
183
184 ASSERT_EQ(xnn_status_success,
185 xnn_run_operator(sigmoid_op, nullptr /* thread pool */));
186
187 // Verify results.
188 for (size_t i = 0; i < batch_size(); i++) {
189 for (size_t c = 0; c < channels(); c++) {
190 ASSERT_NEAR(float(int32_t(output[i * output_stride() + c])), output_ref[i * channels() + c], 0.6f);
191 }
192 }
193 }
194 }
195
Marat Dukhan08b7a972020-07-14 18:17:29 -0700196 void TestQU8() const {
XNNPACK Teamb455b122019-09-27 18:10:33 -0700197 std::random_device random_device;
198 auto rng = std::mt19937(random_device());
Marat Dukhan5ce30d92020-04-14 03:31:26 -0700199 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 -0700200
Marat Dukhan7bee7512019-11-18 15:15:48 -0800201 std::vector<uint8_t> input((batch_size() - 1) * input_stride() + channels() + XNN_EXTRA_BYTES / sizeof(uint8_t));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700202 std::vector<uint8_t> output((batch_size() - 1) * output_stride() + channels());
203 std::vector<float> output_ref(batch_size() * channels());
204 for (size_t iteration = 0; iteration < iterations(); iteration++) {
205 std::generate(input.begin(), input.end(), std::ref(u8rng));
206 std::fill(output.begin(), output.end(), 0xA5);
207
208 // Compute reference results.
209 for (size_t i = 0; i < batch_size(); i++) {
210 for (size_t c = 0; c < channels(); c++) {
211 const float x = input_scale() *
212 (int32_t(input[i * input_stride() + c]) - int32_t(input_zero_point()));
213 const float sigmoid_x = 1.0f / (1.0f + std::exp(-x));
214 const float scaled_sigmoid_x = sigmoid_x / output_scale();
215 float y = scaled_sigmoid_x;
216 y = std::min<float>(y, int32_t(qmax()) - int32_t(output_zero_point()));
217 y = std::max<float>(y, int32_t(qmin()) - int32_t(output_zero_point()));
218 output_ref[i * channels() + c] = y + int32_t(output_zero_point());
219 }
220 }
221
222 // Create, setup, run, and destroy Sigmoid operator.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800223 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700224 xnn_operator_t sigmoid_op = nullptr;
225
226 ASSERT_EQ(xnn_status_success,
Marat Dukhan08b7a972020-07-14 18:17:29 -0700227 xnn_create_sigmoid_nc_qu8(
XNNPACK Teamb455b122019-09-27 18:10:33 -0700228 channels(), input_stride(), output_stride(),
229 input_zero_point(), input_scale(),
230 output_zero_point(), output_scale(),
231 qmin(), qmax(),
232 0, &sigmoid_op));
233 ASSERT_NE(nullptr, sigmoid_op);
234
235 // Smart pointer to automatically delete sigmoid_op.
Marat Dukhan346a9e52019-11-15 09:06:30 -0800236 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_sigmoid_op(sigmoid_op, xnn_delete_operator);
XNNPACK Teamb455b122019-09-27 18:10:33 -0700237
238 ASSERT_EQ(xnn_status_success,
Marat Dukhan08b7a972020-07-14 18:17:29 -0700239 xnn_setup_sigmoid_nc_qu8(
XNNPACK Teamb455b122019-09-27 18:10:33 -0700240 sigmoid_op,
241 batch_size(),
242 input.data(), output.data(),
243 nullptr /* thread pool */));
244
245 ASSERT_EQ(xnn_status_success,
246 xnn_run_operator(sigmoid_op, nullptr /* thread pool */));
247
248 // Verify results.
249 for (size_t i = 0; i < batch_size(); i++) {
250 for (size_t c = 0; c < channels(); c++) {
251 ASSERT_NEAR(float(int32_t(output[i * output_stride() + c])), output_ref[i * channels() + c], 0.6f);
252 }
253 }
254 }
255 }
256
Marat Dukhan346a9e52019-11-15 09:06:30 -0800257 void TestF32() const {
258 std::random_device random_device;
259 auto rng = std::mt19937(random_device());
260 auto f32rng = std::bind(std::uniform_real_distribution<float>(-25.0f, 25.0f), rng);
261
Marat Dukhan7bee7512019-11-18 15:15:48 -0800262 std::vector<float> input((batch_size() - 1) * input_stride() + channels() + XNN_EXTRA_BYTES / sizeof(float));
Marat Dukhan346a9e52019-11-15 09:06:30 -0800263 std::vector<float> output((batch_size() - 1) * output_stride() + channels());
264 std::vector<double> output_ref(batch_size() * channels());
265 for (size_t iteration = 0; iteration < iterations(); iteration++) {
266 std::generate(input.begin(), input.end(), std::ref(f32rng));
267 std::fill(output.begin(), output.end(), 0xA5);
268
269 // Compute reference results.
270 for (size_t i = 0; i < batch_size(); i++) {
271 for (size_t c = 0; c < channels(); c++) {
272 const double x = input[i * input_stride() + c];
273 const double exp_x = std::exp(x);
274 const double sigmoid_x = exp_x / (1.0 + exp_x);
275 output_ref[i * channels() + c] = sigmoid_x;
276 }
277 }
278
279 // Create, setup, run, and destroy Sigmoid operator.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800280 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
Marat Dukhan346a9e52019-11-15 09:06:30 -0800281 xnn_operator_t sigmoid_op = nullptr;
282
283 xnn_status status = xnn_create_sigmoid_nc_f32(
284 channels(), input_stride(), output_stride(),
285 0, &sigmoid_op);
Marat Dukhan346a9e52019-11-15 09:06:30 -0800286 ASSERT_EQ(xnn_status_success, status);
287 ASSERT_NE(nullptr, sigmoid_op);
288
289 // Smart pointer to automatically delete sigmoid_op.
290 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_sigmoid_op(sigmoid_op, xnn_delete_operator);
291
292 ASSERT_EQ(xnn_status_success,
293 xnn_setup_sigmoid_nc_f32(
294 sigmoid_op,
295 batch_size(),
296 input.data(), output.data(),
297 nullptr /* thread pool */));
298
299 ASSERT_EQ(xnn_status_success,
300 xnn_run_operator(sigmoid_op, nullptr /* thread pool */));
301
302 // Verify results.
303 for (size_t i = 0; i < batch_size(); i++) {
304 for (size_t c = 0; c < channels(); c++) {
305 ASSERT_NEAR(
306 output[i * output_stride() + c],
307 output_ref[i * channels() + c],
Erich Elsen8fd7b5f2019-11-18 10:50:41 -0800308 5.0e-6);
Marat Dukhan346a9e52019-11-15 09:06:30 -0800309 }
310 }
311 }
312 }
313
XNNPACK Teamb455b122019-09-27 18:10:33 -0700314 private:
315 size_t batch_size_{1};
316 size_t channels_{1};
317 size_t input_stride_{0};
318 size_t output_stride_{0};
319 float input_scale_{0.75f};
320 uint8_t input_zero_point_{121};
321 uint8_t qmin_{0};
322 uint8_t qmax_{255};
323 size_t iterations_{15};
324};