• 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 LeakyReLUOperatorTester {
27  public:
channels(size_t channels)28   inline LeakyReLUOperatorTester& 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 LeakyReLUOperatorTester& 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 LeakyReLUOperatorTester& 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 LeakyReLUOperatorTester& 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 
negative_slope(float negative_slope)78   inline LeakyReLUOperatorTester& negative_slope(float negative_slope) {
79     assert(negative_slope > 0.0f);
80     assert(negative_slope < 1.0f);
81     this->negative_slope_ = negative_slope;
82     return *this;
83   }
84 
negative_slope()85   inline float negative_slope() const {
86     return this->negative_slope_;
87   }
88 
input_scale(float input_scale)89   inline LeakyReLUOperatorTester& 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 
input_scale()96   inline float input_scale() const {
97     return this->input_scale_;
98   }
99 
input_zero_point(uint8_t input_zero_point)100   inline LeakyReLUOperatorTester& input_zero_point(uint8_t input_zero_point) {
101     this->input_zero_point_ = input_zero_point;
102     return *this;
103   }
104 
input_zero_point()105   inline uint8_t input_zero_point() const {
106     return this->input_zero_point_;
107   }
108 
output_scale(float output_scale)109   inline LeakyReLUOperatorTester& 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 
output_scale()116   inline float output_scale() const {
117     return this->output_scale_;
118   }
119 
output_zero_point(uint8_t output_zero_point)120   inline LeakyReLUOperatorTester& output_zero_point(uint8_t output_zero_point) {
121     this->output_zero_point_ = output_zero_point;
122     return *this;
123   }
124 
output_zero_point()125   inline uint8_t output_zero_point() const {
126     return this->output_zero_point_;
127   }
128 
qmin(uint8_t qmin)129   inline LeakyReLUOperatorTester& qmin(uint8_t qmin) {
130     this->qmin_ = qmin;
131     return *this;
132   }
133 
qmin()134   inline uint8_t qmin() const {
135     return this->qmin_;
136   }
137 
qmax(uint8_t qmax)138   inline LeakyReLUOperatorTester& qmax(uint8_t qmax) {
139     this->qmax_ = qmax;
140     return *this;
141   }
142 
qmax()143   inline uint8_t qmax() const {
144     return this->qmax_;
145   }
146 
iterations(size_t iterations)147   inline LeakyReLUOperatorTester& iterations(size_t iterations) {
148     this->iterations_ = iterations;
149     return *this;
150   }
151 
iterations()152   inline size_t iterations() const {
153     return this->iterations_;
154   }
155 
TestF32()156   void TestF32() const {
157     std::random_device random_device;
158     auto rng = std::mt19937(random_device());
159     auto f32rng = std::bind(std::uniform_real_distribution<float>(-1.0f, 1.0f), std::ref(rng));
160 
161     std::vector<float> input(XNN_EXTRA_BYTES / sizeof(float) + (batch_size() - 1) * input_stride() + channels());
162     std::vector<float> output((batch_size() - 1) * output_stride() + channels());
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(f32rng));
166       std::fill(output.begin(), output.end(), std::nanf(""));
167 
168       // Compute reference results.
169       for (size_t i = 0; i < batch_size(); i++) {
170         for (size_t c = 0; c < channels(); c++) {
171           const float x = input[i * input_stride() + c];
172           const float y = std::signbit(x) ? x * negative_slope() : x;
173           output_ref[i * channels() + c] = y;
174         }
175       }
176 
177       // Create, setup, run, and destroy Leaky ReLU operator.
178       ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
179       xnn_operator_t leaky_relu_op = nullptr;
180 
181       ASSERT_EQ(xnn_status_success,
182         xnn_create_leaky_relu_nc_f32(
183           channels(), input_stride(), output_stride(),
184           negative_slope(),
185           0, &leaky_relu_op));
186       ASSERT_NE(nullptr, leaky_relu_op);
187 
188       // Smart pointer to automatically delete leaky_relu_op.
189       std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_leaky_relu_op(leaky_relu_op, xnn_delete_operator);
190 
191       ASSERT_EQ(xnn_status_success,
192         xnn_setup_leaky_relu_nc_f32(
193           leaky_relu_op,
194           batch_size(),
195           input.data(), output.data(),
196           nullptr /* thread pool */));
197 
198       ASSERT_EQ(xnn_status_success,
199         xnn_run_operator(leaky_relu_op, nullptr /* thread pool */));
200 
201       // Verify results.
202       for (size_t i = 0; i < batch_size(); i++) {
203         for (size_t c = 0; c < channels(); c++) {
204           ASSERT_EQ(output[i * output_stride() + c], output_ref[i * channels() + c])
205             << "at batch " << i << " / " << batch_size() << ", channel " << c << " / " << channels()
206             << ", input " << input[i * input_stride() + c] << ", negative slope " << negative_slope();
207         }
208       }
209     }
210   }
211 
TestQU8()212   void TestQU8() const {
213     std::random_device random_device;
214     auto rng = std::mt19937(random_device());
215     auto u8rng = std::bind(std::uniform_int_distribution<uint32_t>(0, std::numeric_limits<uint8_t>::max()), rng);
216 
217     std::vector<uint8_t> input(XNN_EXTRA_BYTES / sizeof(uint8_t) + (batch_size() - 1) * input_stride() + channels());
218     std::vector<uint8_t> output((batch_size() - 1) * output_stride() + channels());
219     std::vector<float> output_ref(batch_size() * channels());
220     for (size_t iteration = 0; iteration < iterations(); iteration++) {
221       std::generate(input.begin(), input.end(), std::ref(u8rng));
222       std::fill(output.begin(), output.end(), 0xA5);
223 
224       // Compute reference results.
225       for (size_t i = 0; i < batch_size(); i++) {
226         for (size_t c = 0; c < channels(); c++) {
227           const float x = input_scale() * (int32_t(input[i * input_stride() + c]) - int32_t(input_zero_point()));
228           float y = (x < 0.0f ? x * negative_slope() : x) / output_scale();
229           y = std::min<float>(y, int32_t(qmax()) - int32_t(output_zero_point()));
230           y = std::max<float>(y, int32_t(qmin()) - int32_t(output_zero_point()));
231           output_ref[i * channels() + c] = y + float(int32_t(output_zero_point()));
232         }
233       }
234 
235       // Create, setup, run, and destroy Leaky ReLU operator.
236       ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
237       xnn_operator_t leaky_relu_op = nullptr;
238 
239       ASSERT_EQ(xnn_status_success,
240         xnn_create_leaky_relu_nc_qu8(
241           channels(), input_stride(), output_stride(),
242           negative_slope(),
243           input_zero_point(), input_scale(),
244           output_zero_point(), output_scale(),
245           qmin(), qmax(),
246           0, &leaky_relu_op));
247       ASSERT_NE(nullptr, leaky_relu_op);
248 
249       // Smart pointer to automatically delete leaky_relu_op.
250       std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_leaky_relu_op(leaky_relu_op, xnn_delete_operator);
251 
252       ASSERT_EQ(xnn_status_success,
253         xnn_setup_leaky_relu_nc_qu8(
254           leaky_relu_op,
255           batch_size(),
256           input.data(), output.data(),
257           nullptr /* thread pool */));
258 
259       ASSERT_EQ(xnn_status_success,
260         xnn_run_operator(leaky_relu_op, nullptr /* thread pool */));
261 
262       // Verify results.
263       for (size_t i = 0; i < batch_size(); i++) {
264         for (size_t c = 0; c < channels(); c++) {
265           ASSERT_NEAR(float(int32_t(output[i * output_stride() + c])), output_ref[i * channels() + c], 0.6f);
266         }
267       }
268     }
269   }
270 
271  private:
272   size_t batch_size_{1};
273   size_t channels_{1};
274   size_t input_stride_{0};
275   size_t output_stride_{0};
276   float negative_slope_{0.5f};
277   float output_scale_{0.75f};
278   uint8_t output_zero_point_{133};
279   float input_scale_{1.25f};
280   uint8_t input_zero_point_{121};
281   uint8_t qmin_{0};
282   uint8_t qmax_{255};
283   size_t iterations_{15};
284 };
285