• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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>
19 #include <limits>
20 #include <random>
21 #include <vector>
22 
23 #include <xnnpack.h>
24 
25 
26 class SigmoidOperatorTester {
27  public:
channels(size_t channels)28   inline SigmoidOperatorTester& channels(size_t channels) {
29     assert(channels != 0);
30     this->channels_ = channels;
31     return *this;
32   }
33 
channels()34   inline size_t channels() const {
35     return this->channels_;
36   }
37 
input_stride(size_t input_stride)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 
input_stride()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 
output_stride(size_t output_stride)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 
output_stride()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 
batch_size(size_t batch_size)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 
batch_size()74   inline size_t batch_size() const {
75     return this->batch_size_;
76   }
77 
input_scale(float input_scale)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 
input_scale()85   inline float input_scale() const {
86     return this->input_scale_;
87   }
88 
input_zero_point(uint8_t input_zero_point)89   inline SigmoidOperatorTester& input_zero_point(uint8_t input_zero_point) {
90     this->input_zero_point_ = input_zero_point;
91     return *this;
92   }
93 
input_zero_point()94   inline uint8_t input_zero_point() const {
95     return this->input_zero_point_;
96   }
97 
output_scale()98   inline float output_scale() const {
99     return 1.0f / 256.0f;
100   }
101 
output_zero_point()102   inline uint8_t output_zero_point() const {
103     return 0;
104   }
105 
qmin(uint8_t qmin)106   inline SigmoidOperatorTester& qmin(uint8_t qmin) {
107     this->qmin_ = qmin;
108     return *this;
109   }
110 
qmin()111   inline uint8_t qmin() const {
112     return this->qmin_;
113   }
114 
qmax(uint8_t qmax)115   inline SigmoidOperatorTester& qmax(uint8_t qmax) {
116     this->qmax_ = qmax;
117     return *this;
118   }
119 
qmax()120   inline uint8_t qmax() const {
121     return this->qmax_;
122   }
123 
iterations(size_t iterations)124   inline SigmoidOperatorTester& iterations(size_t iterations) {
125     this->iterations_ = iterations;
126     return *this;
127   }
128 
iterations()129   inline size_t iterations() const {
130     return this->iterations_;
131   }
132 
TestQS8()133   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 
TestQU8()196   void TestQU8() const {
197     std::random_device random_device;
198     auto rng = std::mt19937(random_device());
199     auto u8rng = std::bind(std::uniform_int_distribution<uint32_t>(0, std::numeric_limits<uint8_t>::max()), rng);
200 
201     std::vector<uint8_t> input((batch_size() - 1) * input_stride() + channels() + XNN_EXTRA_BYTES / sizeof(uint8_t));
202     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.
223       ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
224       xnn_operator_t sigmoid_op = nullptr;
225 
226       ASSERT_EQ(xnn_status_success,
227         xnn_create_sigmoid_nc_qu8(
228           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.
236       std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_sigmoid_op(sigmoid_op, xnn_delete_operator);
237 
238       ASSERT_EQ(xnn_status_success,
239         xnn_setup_sigmoid_nc_qu8(
240           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 
TestF32()257   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 
262     std::vector<float> input((batch_size() - 1) * input_stride() + channels() + XNN_EXTRA_BYTES / sizeof(float));
263     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.
280       ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
281       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);
286       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],
308             5.0e-6);
309         }
310       }
311     }
312   }
313 
314  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 };
325