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 <random> 20 #include <vector> 21 22 #include <xnnpack.h> 23 24 25 class LeakyReLUOperatorTester { 26 public: channels(size_t channels)27 inline LeakyReLUOperatorTester& channels(size_t channels) { 28 assert(channels != 0); 29 this->channels_ = channels; 30 return *this; 31 } 32 channels()33 inline size_t channels() const { 34 return this->channels_; 35 } 36 input_stride(size_t input_stride)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 input_stride()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 output_stride(size_t output_stride)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 output_stride()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 batch_size(size_t batch_size)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 batch_size()73 inline size_t batch_size() const { 74 return this->batch_size_; 75 } 76 negative_slope(float negative_slope)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 negative_slope()84 inline float negative_slope() const { 85 return this->negative_slope_; 86 } 87 input_scale(float input_scale)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 input_scale()95 inline float input_scale() const { 96 return this->input_scale_; 97 } 98 input_zero_point(uint8_t input_zero_point)99 inline LeakyReLUOperatorTester& input_zero_point(uint8_t input_zero_point) { 100 this->input_zero_point_ = input_zero_point; 101 return *this; 102 } 103 input_zero_point()104 inline uint8_t input_zero_point() const { 105 return this->input_zero_point_; 106 } 107 output_scale(float output_scale)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 output_scale()115 inline float output_scale() const { 116 return this->output_scale_; 117 } 118 output_zero_point(uint8_t output_zero_point)119 inline LeakyReLUOperatorTester& output_zero_point(uint8_t output_zero_point) { 120 this->output_zero_point_ = output_zero_point; 121 return *this; 122 } 123 output_zero_point()124 inline uint8_t output_zero_point() const { 125 return this->output_zero_point_; 126 } 127 qmin(uint8_t qmin)128 inline LeakyReLUOperatorTester& qmin(uint8_t qmin) { 129 this->qmin_ = qmin; 130 return *this; 131 } 132 qmin()133 inline uint8_t qmin() const { 134 return this->qmin_; 135 } 136 qmax(uint8_t qmax)137 inline LeakyReLUOperatorTester& qmax(uint8_t qmax) { 138 this->qmax_ = qmax; 139 return *this; 140 } 141 qmax()142 inline uint8_t qmax() const { 143 return this->qmax_; 144 } 145 iterations(size_t iterations)146 inline LeakyReLUOperatorTester& iterations(size_t iterations) { 147 this->iterations_ = iterations; 148 return *this; 149 } 150 iterations()151 inline size_t iterations() const { 152 return this->iterations_; 153 } 154 TestQ8()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. 179 ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */)); 180 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 }; 228