blob: 5f4dce702e337fa3fd5c868b798a17bacb7c0bb0 [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>
19#include <random>
20#include <vector>
21
22#include <xnnpack.h>
23
24
25class LeakyReLUOperatorTester {
26 public:
27 inline LeakyReLUOperatorTester& channels(size_t channels) {
28 assert(channels != 0);
29 this->channels_ = channels;
30 return *this;
31 }
32
33 inline size_t channels() const {
34 return this->channels_;
35 }
36
37 inline LeakyReLUOperatorTester& input_stride(size_t input_stride) {
38 assert(input_stride != 0);
39 this->input_stride_ = input_stride;
40 return *this;
41 }
42
43 inline size_t input_stride() const {
44 if (this->input_stride_ == 0) {
45 return this->channels_;
46 } else {
47 assert(this->input_stride_ >= this->channels_);
48 return this->input_stride_;
49 }
50 }
51
52 inline LeakyReLUOperatorTester& output_stride(size_t output_stride) {
53 assert(output_stride != 0);
54 this->output_stride_ = output_stride;
55 return *this;
56 }
57
58 inline size_t output_stride() const {
59 if (this->output_stride_ == 0) {
60 return this->channels_;
61 } else {
62 assert(this->output_stride_ >= this->channels_);
63 return this->output_stride_;
64 }
65 }
66
67 inline LeakyReLUOperatorTester& batch_size(size_t batch_size) {
68 assert(batch_size != 0);
69 this->batch_size_ = batch_size;
70 return *this;
71 }
72
73 inline size_t batch_size() const {
74 return this->batch_size_;
75 }
76
77 inline LeakyReLUOperatorTester& negative_slope(float negative_slope) {
78 assert(negative_slope > 0.0f);
79 assert(negative_slope < 1.0f);
80 this->negative_slope_ = negative_slope;
81 return *this;
82 }
83
84 inline float negative_slope() const {
85 return this->negative_slope_;
86 }
87
88 inline LeakyReLUOperatorTester& input_scale(float input_scale) {
89 assert(input_scale > 0.0f);
90 assert(std::isnormal(input_scale));
91 this->input_scale_ = input_scale;
92 return *this;
93 }
94
95 inline float input_scale() const {
96 return this->input_scale_;
97 }
98
99 inline LeakyReLUOperatorTester& input_zero_point(uint8_t input_zero_point) {
100 this->input_zero_point_ = input_zero_point;
101 return *this;
102 }
103
104 inline uint8_t input_zero_point() const {
105 return this->input_zero_point_;
106 }
107
108 inline LeakyReLUOperatorTester& output_scale(float output_scale) {
109 assert(output_scale > 0.0f);
110 assert(std::isnormal(output_scale));
111 this->output_scale_ = output_scale;
112 return *this;
113 }
114
115 inline float output_scale() const {
116 return this->output_scale_;
117 }
118
119 inline LeakyReLUOperatorTester& output_zero_point(uint8_t output_zero_point) {
120 this->output_zero_point_ = output_zero_point;
121 return *this;
122 }
123
124 inline uint8_t output_zero_point() const {
125 return this->output_zero_point_;
126 }
127
128 inline LeakyReLUOperatorTester& qmin(uint8_t qmin) {
129 this->qmin_ = qmin;
130 return *this;
131 }
132
133 inline uint8_t qmin() const {
134 return this->qmin_;
135 }
136
137 inline LeakyReLUOperatorTester& qmax(uint8_t qmax) {
138 this->qmax_ = qmax;
139 return *this;
140 }
141
142 inline uint8_t qmax() const {
143 return this->qmax_;
144 }
145
146 inline LeakyReLUOperatorTester& iterations(size_t iterations) {
147 this->iterations_ = iterations;
148 return *this;
149 }
150
151 inline size_t iterations() const {
152 return this->iterations_;
153 }
154
155 void TestQ8() const {
156 std::random_device random_device;
157 auto rng = std::mt19937(random_device());
158 auto u8rng = std::bind(std::uniform_int_distribution<uint8_t>(), rng);
159
160 std::vector<uint8_t> input((batch_size() - 1) * input_stride() + channels());
161 std::vector<uint8_t> output((batch_size() - 1) * output_stride() + channels());
162 std::vector<float> output_ref(batch_size() * channels());
163 for (size_t iteration = 0; iteration < iterations(); iteration++) {
164 std::generate(input.begin(), input.end(), std::ref(u8rng));
165 std::fill(output.begin(), output.end(), 0xA5);
166
167 // Compute reference results.
168 for (size_t i = 0; i < batch_size(); i++) {
169 for (size_t c = 0; c < channels(); c++) {
170 const float x = input_scale() * (int32_t(input[i * input_stride() + c]) - int32_t(input_zero_point()));
171 float y = (x < 0.0f ? x * negative_slope() : x) / output_scale();
172 y = std::min<float>(y, int32_t(qmax()) - int32_t(output_zero_point()));
173 y = std::max<float>(y, int32_t(qmin()) - int32_t(output_zero_point()));
174 output_ref[i * channels() + c] = y + float(int32_t(output_zero_point()));
175 }
176 }
177
178 // Create, setup, run, and destroy LeakyReLU operator.
Marat Dukhan04f03be2019-11-19 12:36:47 -0800179 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
XNNPACK Teamb455b122019-09-27 18:10:33 -0700180 xnn_operator_t leaky_relu_op = nullptr;
181
182 ASSERT_EQ(xnn_status_success,
183 xnn_create_leaky_relu_nc_q8(
184 channels(), input_stride(), output_stride(),
185 negative_slope(),
186 input_zero_point(), input_scale(),
187 output_zero_point(), output_scale(),
188 qmin(), qmax(),
189 0, &leaky_relu_op));
190 ASSERT_NE(nullptr, leaky_relu_op);
191
192 // Smart pointer to automatically delete leaky_relu_op.
193 std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_leaky_relu_op(leaky_relu_op, xnn_delete_operator);
194
195 ASSERT_EQ(xnn_status_success,
196 xnn_setup_leaky_relu_nc_q8(
197 leaky_relu_op,
198 batch_size(),
199 input.data(), output.data(),
200 nullptr /* thread pool */));
201
202 ASSERT_EQ(xnn_status_success,
203 xnn_run_operator(leaky_relu_op, nullptr /* thread pool */));
204
205 // Verify results.
206 for (size_t i = 0; i < batch_size(); i++) {
207 for (size_t c = 0; c < channels(); c++) {
208 ASSERT_NEAR(float(int32_t(output[i * output_stride() + c])), output_ref[i * channels() + c], 0.6f);
209 }
210 }
211 }
212 }
213
214 private:
215 size_t batch_size_{1};
216 size_t channels_{1};
217 size_t input_stride_{0};
218 size_t output_stride_{0};
219 float negative_slope_{0.5f};
220 float output_scale_{0.75f};
221 uint8_t output_zero_point_{133};
222 float input_scale_{1.25f};
223 uint8_t input_zero_point_{121};
224 uint8_t qmin_{0};
225 uint8_t qmax_{255};
226 size_t iterations_{15};
227};