1*4bdc9457SAndroid Build Coastguard Worker // Copyright (c) Facebook, Inc. and its affiliates. 2*4bdc9457SAndroid Build Coastguard Worker // All rights reserved. 3*4bdc9457SAndroid Build Coastguard Worker // 4*4bdc9457SAndroid Build Coastguard Worker // Copyright 2019 Google LLC 5*4bdc9457SAndroid Build Coastguard Worker // 6*4bdc9457SAndroid Build Coastguard Worker // This source code is licensed under the BSD-style license found in the 7*4bdc9457SAndroid Build Coastguard Worker // LICENSE file in the root directory of this source tree. 8*4bdc9457SAndroid Build Coastguard Worker 9*4bdc9457SAndroid Build Coastguard Worker #pragma once 10*4bdc9457SAndroid Build Coastguard Worker 11*4bdc9457SAndroid Build Coastguard Worker #include <gtest/gtest.h> 12*4bdc9457SAndroid Build Coastguard Worker 13*4bdc9457SAndroid Build Coastguard Worker #include <algorithm> 14*4bdc9457SAndroid Build Coastguard Worker #include <cfloat> 15*4bdc9457SAndroid Build Coastguard Worker #include <cmath> 16*4bdc9457SAndroid Build Coastguard Worker #include <cstddef> 17*4bdc9457SAndroid Build Coastguard Worker #include <cstdlib> 18*4bdc9457SAndroid Build Coastguard Worker #include <functional> 19*4bdc9457SAndroid Build Coastguard Worker #include <limits> 20*4bdc9457SAndroid Build Coastguard Worker #include <random> 21*4bdc9457SAndroid Build Coastguard Worker #include <vector> 22*4bdc9457SAndroid Build Coastguard Worker 23*4bdc9457SAndroid Build Coastguard Worker #include <xnnpack/requantization-stubs.h> 24*4bdc9457SAndroid Build Coastguard Worker #include <xnnpack/requantization.h> 25*4bdc9457SAndroid Build Coastguard Worker 26*4bdc9457SAndroid Build Coastguard Worker 27*4bdc9457SAndroid Build Coastguard Worker class RequantizationTester { 28*4bdc9457SAndroid Build Coastguard Worker public: s(uint32_t s)29*4bdc9457SAndroid Build Coastguard Worker inline RequantizationTester& s(uint32_t s) { 30*4bdc9457SAndroid Build Coastguard Worker this->s_ = s; 31*4bdc9457SAndroid Build Coastguard Worker return *this; 32*4bdc9457SAndroid Build Coastguard Worker } 33*4bdc9457SAndroid Build Coastguard Worker s()34*4bdc9457SAndroid Build Coastguard Worker inline uint32_t s() const { 35*4bdc9457SAndroid Build Coastguard Worker return this->s_; 36*4bdc9457SAndroid Build Coastguard Worker } 37*4bdc9457SAndroid Build Coastguard Worker scale()38*4bdc9457SAndroid Build Coastguard Worker inline float scale() const { 39*4bdc9457SAndroid Build Coastguard Worker return ldexpf(1.0f, -s()); 40*4bdc9457SAndroid Build Coastguard Worker } 41*4bdc9457SAndroid Build Coastguard Worker zero_point(int32_t zero_point)42*4bdc9457SAndroid Build Coastguard Worker inline RequantizationTester& zero_point(int32_t zero_point) { 43*4bdc9457SAndroid Build Coastguard Worker this->zero_point_ = zero_point; 44*4bdc9457SAndroid Build Coastguard Worker return *this; 45*4bdc9457SAndroid Build Coastguard Worker } 46*4bdc9457SAndroid Build Coastguard Worker zero_point()47*4bdc9457SAndroid Build Coastguard Worker inline int32_t zero_point() const { 48*4bdc9457SAndroid Build Coastguard Worker return this->zero_point_; 49*4bdc9457SAndroid Build Coastguard Worker } 50*4bdc9457SAndroid Build Coastguard Worker qmin(int16_t qmin)51*4bdc9457SAndroid Build Coastguard Worker inline RequantizationTester& qmin(int16_t qmin) { 52*4bdc9457SAndroid Build Coastguard Worker this->qmin_ = qmin; 53*4bdc9457SAndroid Build Coastguard Worker return *this; 54*4bdc9457SAndroid Build Coastguard Worker } 55*4bdc9457SAndroid Build Coastguard Worker qmin()56*4bdc9457SAndroid Build Coastguard Worker inline int16_t qmin() const { 57*4bdc9457SAndroid Build Coastguard Worker return this->qmin_; 58*4bdc9457SAndroid Build Coastguard Worker } 59*4bdc9457SAndroid Build Coastguard Worker qmax(int16_t qmax)60*4bdc9457SAndroid Build Coastguard Worker inline RequantizationTester& qmax(int16_t qmax) { 61*4bdc9457SAndroid Build Coastguard Worker this->qmax_ = qmax; 62*4bdc9457SAndroid Build Coastguard Worker return *this; 63*4bdc9457SAndroid Build Coastguard Worker } 64*4bdc9457SAndroid Build Coastguard Worker qmax()65*4bdc9457SAndroid Build Coastguard Worker inline int16_t qmax() const { 66*4bdc9457SAndroid Build Coastguard Worker return this->qmax_; 67*4bdc9457SAndroid Build Coastguard Worker } 68*4bdc9457SAndroid Build Coastguard Worker iterations(size_t iterations)69*4bdc9457SAndroid Build Coastguard Worker inline RequantizationTester& iterations(size_t iterations) { 70*4bdc9457SAndroid Build Coastguard Worker this->iterations_ = iterations; 71*4bdc9457SAndroid Build Coastguard Worker return *this; 72*4bdc9457SAndroid Build Coastguard Worker } 73*4bdc9457SAndroid Build Coastguard Worker iterations()74*4bdc9457SAndroid Build Coastguard Worker inline size_t iterations() const { 75*4bdc9457SAndroid Build Coastguard Worker return this->iterations_; 76*4bdc9457SAndroid Build Coastguard Worker } 77*4bdc9457SAndroid Build Coastguard Worker 78*4bdc9457SAndroid Build Coastguard Worker /* 79*4bdc9457SAndroid Build Coastguard Worker * Test that requantization of numbers ((i - zero point) * 2**s) with 80*4bdc9457SAndroid Build Coastguard Worker * - scale = exp2(-s) 81*4bdc9457SAndroid Build Coastguard Worker * - zero point in [0, 255] 82*4bdc9457SAndroid Build Coastguard Worker * - no output clamping 83*4bdc9457SAndroid Build Coastguard Worker * produces exactly i, provided that ((i - zero point) * 2**s) does not overflow. 84*4bdc9457SAndroid Build Coastguard Worker */ TestExactDivideByPO2(xnn_qu8_requantization_function requantize)85*4bdc9457SAndroid Build Coastguard Worker void TestExactDivideByPO2(xnn_qu8_requantization_function requantize) const { 86*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(zero_point(), std::numeric_limits<uint8_t>::min()); 87*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(zero_point(), std::numeric_limits<uint8_t>::max()); 88*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<uint8_t>::min()); 89*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<uint8_t>::max()); 90*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<uint8_t>::min()); 91*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<uint8_t>::max()); 92*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 93*4bdc9457SAndroid Build Coastguard Worker 94*4bdc9457SAndroid Build Coastguard Worker /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */ 95*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(s(), 1); 96*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(s(), 32); 97*4bdc9457SAndroid Build Coastguard Worker 98*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(256); 99*4bdc9457SAndroid Build Coastguard Worker std::vector<uint8_t> outputs(inputs.size()); 100*4bdc9457SAndroid Build Coastguard Worker const int32_t max_i = (uint32_t(std::numeric_limits<int32_t>::max()) >> s()) + zero_point(); 101*4bdc9457SAndroid Build Coastguard Worker const int32_t min_i = -(-uint32_t(std::numeric_limits<int32_t>::min()) >> s()) + zero_point(); 102*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) { 103*4bdc9457SAndroid Build Coastguard Worker const int32_t clamped_i = std::max(min_i, std::min(max_i, i)); 104*4bdc9457SAndroid Build Coastguard Worker inputs[i] = int32_t(uint32_t(clamped_i - zero_point()) << s()); 105*4bdc9457SAndroid Build Coastguard Worker } 106*4bdc9457SAndroid Build Coastguard Worker requantize(inputs.size(), inputs.data(), 107*4bdc9457SAndroid Build Coastguard Worker scale(), zero_point(), qmin(), qmax(), 108*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 109*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) { 110*4bdc9457SAndroid Build Coastguard Worker const int32_t clamped_i = std::max(min_i, std::min(max_i, i)); 111*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(uint32_t(clamped_i), uint32_t(outputs[i])) 112*4bdc9457SAndroid Build Coastguard Worker << "i = " << i << ", clamped i = " << clamped_i << ", input = " << inputs[i] 113*4bdc9457SAndroid Build Coastguard Worker << ", min i = " << min_i << ", max i = " << max_i 114*4bdc9457SAndroid Build Coastguard Worker << ", s = " << s() << ", zero point = " << zero_point(); 115*4bdc9457SAndroid Build Coastguard Worker } 116*4bdc9457SAndroid Build Coastguard Worker } 117*4bdc9457SAndroid Build Coastguard Worker 118*4bdc9457SAndroid Build Coastguard Worker /* 119*4bdc9457SAndroid Build Coastguard Worker * Test that requantization of numbers ((i - zero point) * 2**s) with 120*4bdc9457SAndroid Build Coastguard Worker * - scale = exp2(-s) 121*4bdc9457SAndroid Build Coastguard Worker * - zero point in [-128, 127] 122*4bdc9457SAndroid Build Coastguard Worker * - no output clamping 123*4bdc9457SAndroid Build Coastguard Worker * produces exactly i, provided that ((i - zero point) * 2**s) does not overflow. 124*4bdc9457SAndroid Build Coastguard Worker */ TestExactDivideByPO2(xnn_qs8_requantization_function requantize)125*4bdc9457SAndroid Build Coastguard Worker void TestExactDivideByPO2(xnn_qs8_requantization_function requantize) const { 126*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min()); 127*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max()); 128*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min()); 129*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max()); 130*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min()); 131*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max()); 132*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 133*4bdc9457SAndroid Build Coastguard Worker 134*4bdc9457SAndroid Build Coastguard Worker /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */ 135*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(s(), 1); 136*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(s(), 32); 137*4bdc9457SAndroid Build Coastguard Worker 138*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(256); 139*4bdc9457SAndroid Build Coastguard Worker std::vector<int8_t> outputs(inputs.size()); 140*4bdc9457SAndroid Build Coastguard Worker const int32_t max_i = (uint32_t(std::numeric_limits<int32_t>::max()) >> s()) + zero_point(); 141*4bdc9457SAndroid Build Coastguard Worker const int32_t min_i = -(-uint32_t(std::numeric_limits<int32_t>::min()) >> s()) + zero_point(); 142*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) { 143*4bdc9457SAndroid Build Coastguard Worker const int32_t clamped_i = std::max(min_i, std::min(max_i, i)); 144*4bdc9457SAndroid Build Coastguard Worker inputs[i - std::numeric_limits<int8_t>::min()] = int32_t(uint32_t(clamped_i - zero_point()) << s()); 145*4bdc9457SAndroid Build Coastguard Worker } 146*4bdc9457SAndroid Build Coastguard Worker requantize(inputs.size(), inputs.data(), 147*4bdc9457SAndroid Build Coastguard Worker scale(), zero_point(), qmin(), qmax(), 148*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 149*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) { 150*4bdc9457SAndroid Build Coastguard Worker const int32_t clamped_i = std::max(min_i, std::min(max_i, i)); 151*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(clamped_i, int32_t(outputs[i - std::numeric_limits<int8_t>::min()])) 152*4bdc9457SAndroid Build Coastguard Worker << "i = " << i << ", clamped i = " << clamped_i 153*4bdc9457SAndroid Build Coastguard Worker << ", input = " << inputs[i - std::numeric_limits<int8_t>::min()] 154*4bdc9457SAndroid Build Coastguard Worker << ", min i = " << min_i << ", max i = " << max_i 155*4bdc9457SAndroid Build Coastguard Worker << ", s = " << s() << ", zero point = " << zero_point(); 156*4bdc9457SAndroid Build Coastguard Worker } 157*4bdc9457SAndroid Build Coastguard Worker } 158*4bdc9457SAndroid Build Coastguard Worker 159*4bdc9457SAndroid Build Coastguard Worker /* 160*4bdc9457SAndroid Build Coastguard Worker * Test that requantization of numbers ((i - zero point) * 2**s - 2**(s-1) + 1) with 161*4bdc9457SAndroid Build Coastguard Worker * - scale = exp2(-s) 162*4bdc9457SAndroid Build Coastguard Worker * - zero point in [1, 255] 163*4bdc9457SAndroid Build Coastguard Worker * - no output clamping 164*4bdc9457SAndroid Build Coastguard Worker * produces exactly i, provided that ((i - zero point) * 2**s) does not overflow. 165*4bdc9457SAndroid Build Coastguard Worker */ TestDivideByPO2WithRoundingUp(xnn_qu8_requantization_function requantize)166*4bdc9457SAndroid Build Coastguard Worker void TestDivideByPO2WithRoundingUp(xnn_qu8_requantization_function requantize) { 167*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(zero_point(), std::numeric_limits<uint8_t>::min()); 168*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(zero_point(), std::numeric_limits<uint8_t>::max()); 169*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<uint8_t>::min()); 170*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<uint8_t>::max()); 171*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<uint8_t>::min()); 172*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<uint8_t>::max()); 173*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 174*4bdc9457SAndroid Build Coastguard Worker 175*4bdc9457SAndroid Build Coastguard Worker /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */ 176*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(s(), 1); 177*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(s(), 32); 178*4bdc9457SAndroid Build Coastguard Worker 179*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(256); 180*4bdc9457SAndroid Build Coastguard Worker std::vector<uint8_t> outputs(inputs.size()); 181*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) { 182*4bdc9457SAndroid Build Coastguard Worker const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) - 183*4bdc9457SAndroid Build Coastguard Worker (INT64_C(1) << (s() - 1)) + 1; 184*4bdc9457SAndroid Build Coastguard Worker inputs[i] = int32_t(input); 185*4bdc9457SAndroid Build Coastguard Worker } 186*4bdc9457SAndroid Build Coastguard Worker requantize(inputs.size(), inputs.data(), 187*4bdc9457SAndroid Build Coastguard Worker scale(), zero_point(), qmin(), qmax(), 188*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 189*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) { 190*4bdc9457SAndroid Build Coastguard Worker const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) - 191*4bdc9457SAndroid Build Coastguard Worker (INT64_C(1) << (s() - 1)) + 1; 192*4bdc9457SAndroid Build Coastguard Worker if (int32_t(input) == input) { 193*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(i, int32_t(outputs[i])) 194*4bdc9457SAndroid Build Coastguard Worker << "i = " << i << ", input = " << input 195*4bdc9457SAndroid Build Coastguard Worker << ", s = " << s() << ", zero point = " << zero_point(); 196*4bdc9457SAndroid Build Coastguard Worker } 197*4bdc9457SAndroid Build Coastguard Worker } 198*4bdc9457SAndroid Build Coastguard Worker } 199*4bdc9457SAndroid Build Coastguard Worker 200*4bdc9457SAndroid Build Coastguard Worker /* 201*4bdc9457SAndroid Build Coastguard Worker * Test that requantization of numbers ((i - zero point) * 2**s - 2**(s-1) + 1) with 202*4bdc9457SAndroid Build Coastguard Worker * - scale = exp2(-s) 203*4bdc9457SAndroid Build Coastguard Worker * - zero point in [-128, 127] 204*4bdc9457SAndroid Build Coastguard Worker * - no output clamping 205*4bdc9457SAndroid Build Coastguard Worker * produces exactly i, provided that ((i - zero point) * 2**s) does not overflow. 206*4bdc9457SAndroid Build Coastguard Worker */ TestDivideByPO2WithRoundingUp(xnn_qs8_requantization_function requantize)207*4bdc9457SAndroid Build Coastguard Worker void TestDivideByPO2WithRoundingUp(xnn_qs8_requantization_function requantize) { 208*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min()); 209*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max()); 210*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min()); 211*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max()); 212*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min()); 213*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max()); 214*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 215*4bdc9457SAndroid Build Coastguard Worker 216*4bdc9457SAndroid Build Coastguard Worker /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */ 217*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(s(), 1); 218*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(s(), 32); 219*4bdc9457SAndroid Build Coastguard Worker 220*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(256); 221*4bdc9457SAndroid Build Coastguard Worker std::vector<int8_t> outputs(inputs.size()); 222*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) { 223*4bdc9457SAndroid Build Coastguard Worker const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) - 224*4bdc9457SAndroid Build Coastguard Worker (INT64_C(1) << (s() - 1)) + 1; 225*4bdc9457SAndroid Build Coastguard Worker inputs[i - std::numeric_limits<int8_t>::min()] = int32_t(input); 226*4bdc9457SAndroid Build Coastguard Worker } 227*4bdc9457SAndroid Build Coastguard Worker requantize(inputs.size(), inputs.data(), 228*4bdc9457SAndroid Build Coastguard Worker scale(), zero_point(), qmin(), qmax(), 229*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 230*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) { 231*4bdc9457SAndroid Build Coastguard Worker const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) - 232*4bdc9457SAndroid Build Coastguard Worker (INT64_C(1) << (s() - 1)) + 1; 233*4bdc9457SAndroid Build Coastguard Worker if (int32_t(input) == input) { 234*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(i, int32_t(outputs[i - std::numeric_limits<int8_t>::min()])) 235*4bdc9457SAndroid Build Coastguard Worker << "i = " << i << ", input = " << input 236*4bdc9457SAndroid Build Coastguard Worker << ", s = " << s() << ", zero point = " << zero_point(); 237*4bdc9457SAndroid Build Coastguard Worker } 238*4bdc9457SAndroid Build Coastguard Worker } 239*4bdc9457SAndroid Build Coastguard Worker } 240*4bdc9457SAndroid Build Coastguard Worker 241*4bdc9457SAndroid Build Coastguard Worker /* 242*4bdc9457SAndroid Build Coastguard Worker * Test that requantization of numbers ((i - zero point) * 2**s + 2**(s-1) - 1) with 243*4bdc9457SAndroid Build Coastguard Worker * - scale = exp2(-s) 244*4bdc9457SAndroid Build Coastguard Worker * - zero point in [1, 255] 245*4bdc9457SAndroid Build Coastguard Worker * - no output clamping 246*4bdc9457SAndroid Build Coastguard Worker * produces exactly i, provided that ((i - zero point) * 2**s) does not overflow. 247*4bdc9457SAndroid Build Coastguard Worker */ TestDivideByPO2WithRoundingDown(xnn_qu8_requantization_function requantize)248*4bdc9457SAndroid Build Coastguard Worker void TestDivideByPO2WithRoundingDown(xnn_qu8_requantization_function requantize) { 249*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(zero_point(), std::numeric_limits<uint8_t>::min()); 250*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(zero_point(), std::numeric_limits<uint8_t>::max()); 251*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<uint8_t>::min()); 252*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<uint8_t>::max()); 253*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<uint8_t>::min()); 254*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<uint8_t>::max()); 255*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 256*4bdc9457SAndroid Build Coastguard Worker 257*4bdc9457SAndroid Build Coastguard Worker /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */ 258*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(s(), 1); 259*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(s(), 32); 260*4bdc9457SAndroid Build Coastguard Worker 261*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(256); 262*4bdc9457SAndroid Build Coastguard Worker std::vector<uint8_t> outputs(inputs.size()); 263*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) { 264*4bdc9457SAndroid Build Coastguard Worker const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) + 265*4bdc9457SAndroid Build Coastguard Worker (INT64_C(1) << (s() - 1)) - 1; 266*4bdc9457SAndroid Build Coastguard Worker inputs[i] = int32_t(input); 267*4bdc9457SAndroid Build Coastguard Worker } 268*4bdc9457SAndroid Build Coastguard Worker requantize(inputs.size(), inputs.data(), 269*4bdc9457SAndroid Build Coastguard Worker scale(), zero_point(), qmin(), qmax(), 270*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 271*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) { 272*4bdc9457SAndroid Build Coastguard Worker const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) + 273*4bdc9457SAndroid Build Coastguard Worker (INT64_C(1) << (s() - 1)) - 1; 274*4bdc9457SAndroid Build Coastguard Worker if (int32_t(input) == input) { 275*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(i, int32_t(outputs[i])) 276*4bdc9457SAndroid Build Coastguard Worker << "i = " << i << ", input = " << input 277*4bdc9457SAndroid Build Coastguard Worker << ", s = " << s() << ", zero point = " << zero_point(); 278*4bdc9457SAndroid Build Coastguard Worker } 279*4bdc9457SAndroid Build Coastguard Worker } 280*4bdc9457SAndroid Build Coastguard Worker } 281*4bdc9457SAndroid Build Coastguard Worker 282*4bdc9457SAndroid Build Coastguard Worker /* 283*4bdc9457SAndroid Build Coastguard Worker * Test that requantization of numbers ((i - zero point) * 2**s + 2**(s-1) - 1) with 284*4bdc9457SAndroid Build Coastguard Worker * - scale = exp2(-s) 285*4bdc9457SAndroid Build Coastguard Worker * - zero point in [-128, 127] 286*4bdc9457SAndroid Build Coastguard Worker * - no output clamping 287*4bdc9457SAndroid Build Coastguard Worker * produces exactly i, provided that ((i - zero point) * 2**s) does not overflow. 288*4bdc9457SAndroid Build Coastguard Worker */ TestDivideByPO2WithRoundingDown(xnn_qs8_requantization_function requantize)289*4bdc9457SAndroid Build Coastguard Worker void TestDivideByPO2WithRoundingDown(xnn_qs8_requantization_function requantize) { 290*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min()); 291*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max()); 292*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min()); 293*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max()); 294*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min()); 295*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max()); 296*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 297*4bdc9457SAndroid Build Coastguard Worker 298*4bdc9457SAndroid Build Coastguard Worker /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */ 299*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(s(), 1); 300*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(s(), 32); 301*4bdc9457SAndroid Build Coastguard Worker 302*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(256); 303*4bdc9457SAndroid Build Coastguard Worker std::vector<int8_t> outputs(inputs.size()); 304*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) { 305*4bdc9457SAndroid Build Coastguard Worker const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) + 306*4bdc9457SAndroid Build Coastguard Worker (INT64_C(1) << (s() - 1)) - 1; 307*4bdc9457SAndroid Build Coastguard Worker inputs[i - std::numeric_limits<int8_t>::min()] = int32_t(input); 308*4bdc9457SAndroid Build Coastguard Worker } 309*4bdc9457SAndroid Build Coastguard Worker requantize(inputs.size(), inputs.data(), 310*4bdc9457SAndroid Build Coastguard Worker scale(), zero_point(), qmin(), qmax(), 311*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 312*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) { 313*4bdc9457SAndroid Build Coastguard Worker const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) + 314*4bdc9457SAndroid Build Coastguard Worker (INT64_C(1) << (s() - 1)) - 1; 315*4bdc9457SAndroid Build Coastguard Worker if (int32_t(input) == input) { 316*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(i, int32_t(outputs[i - std::numeric_limits<int8_t>::min()])) 317*4bdc9457SAndroid Build Coastguard Worker << "i = " << i << ", input = " << input 318*4bdc9457SAndroid Build Coastguard Worker << ", s = " << s() << ", zero point = " << zero_point(); 319*4bdc9457SAndroid Build Coastguard Worker } 320*4bdc9457SAndroid Build Coastguard Worker } 321*4bdc9457SAndroid Build Coastguard Worker } 322*4bdc9457SAndroid Build Coastguard Worker TestDivideByPO2WithRoundingTiesAway(xnn_qu8_requantization_function requantize)323*4bdc9457SAndroid Build Coastguard Worker void TestDivideByPO2WithRoundingTiesAway(xnn_qu8_requantization_function requantize) { 324*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(zero_point(), std::numeric_limits<uint8_t>::min()); 325*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(zero_point(), std::numeric_limits<uint8_t>::max()); 326*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<uint8_t>::min()); 327*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<uint8_t>::max()); 328*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<uint8_t>::min()); 329*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<uint8_t>::max()); 330*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 331*4bdc9457SAndroid Build Coastguard Worker 332*4bdc9457SAndroid Build Coastguard Worker /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */ 333*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(s(), 1); 334*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(s(), 32); 335*4bdc9457SAndroid Build Coastguard Worker 336*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(256); 337*4bdc9457SAndroid Build Coastguard Worker std::vector<uint8_t> outputs(inputs.size()); 338*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) { 339*4bdc9457SAndroid Build Coastguard Worker int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()); 340*4bdc9457SAndroid Build Coastguard Worker if (input > 0) { 341*4bdc9457SAndroid Build Coastguard Worker input -= INT64_C(1) << (s() - 1); 342*4bdc9457SAndroid Build Coastguard Worker } else if (input < 0) { 343*4bdc9457SAndroid Build Coastguard Worker input += INT64_C(1) << (s() - 1); 344*4bdc9457SAndroid Build Coastguard Worker } 345*4bdc9457SAndroid Build Coastguard Worker inputs[i] = int32_t(input); 346*4bdc9457SAndroid Build Coastguard Worker } 347*4bdc9457SAndroid Build Coastguard Worker requantize(inputs.size(), inputs.data(), 348*4bdc9457SAndroid Build Coastguard Worker scale(), zero_point(), qmin(), qmax(), 349*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 350*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) { 351*4bdc9457SAndroid Build Coastguard Worker int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()); 352*4bdc9457SAndroid Build Coastguard Worker if (input > 0) { 353*4bdc9457SAndroid Build Coastguard Worker input -= INT64_C(1) << (s() - 1); 354*4bdc9457SAndroid Build Coastguard Worker } else if (input < 0) { 355*4bdc9457SAndroid Build Coastguard Worker input += INT64_C(1) << (s() - 1); 356*4bdc9457SAndroid Build Coastguard Worker } 357*4bdc9457SAndroid Build Coastguard Worker if (int32_t(input) == input) { 358*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(i, int32_t(outputs[i])) 359*4bdc9457SAndroid Build Coastguard Worker << "i = " << i << ", input = " << input 360*4bdc9457SAndroid Build Coastguard Worker << ", s = " << s() << ", zero point = " << zero_point(); 361*4bdc9457SAndroid Build Coastguard Worker } 362*4bdc9457SAndroid Build Coastguard Worker } 363*4bdc9457SAndroid Build Coastguard Worker } 364*4bdc9457SAndroid Build Coastguard Worker TestDivideByPO2WithRoundingTiesAway(xnn_qs8_requantization_function requantize)365*4bdc9457SAndroid Build Coastguard Worker void TestDivideByPO2WithRoundingTiesAway(xnn_qs8_requantization_function requantize) { 366*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min()); 367*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max()); 368*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min()); 369*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max()); 370*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min()); 371*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max()); 372*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 373*4bdc9457SAndroid Build Coastguard Worker 374*4bdc9457SAndroid Build Coastguard Worker /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */ 375*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(s(), 1); 376*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(s(), 32); 377*4bdc9457SAndroid Build Coastguard Worker 378*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(256); 379*4bdc9457SAndroid Build Coastguard Worker std::vector<int8_t> outputs(inputs.size()); 380*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) { 381*4bdc9457SAndroid Build Coastguard Worker int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()); 382*4bdc9457SAndroid Build Coastguard Worker if (input > 0) { 383*4bdc9457SAndroid Build Coastguard Worker input -= INT64_C(1) << (s() - 1); 384*4bdc9457SAndroid Build Coastguard Worker } else if (input < 0) { 385*4bdc9457SAndroid Build Coastguard Worker input += INT64_C(1) << (s() - 1); 386*4bdc9457SAndroid Build Coastguard Worker } 387*4bdc9457SAndroid Build Coastguard Worker inputs[i - std::numeric_limits<int8_t>::min()] = int32_t(input); 388*4bdc9457SAndroid Build Coastguard Worker } 389*4bdc9457SAndroid Build Coastguard Worker requantize(inputs.size(), inputs.data(), 390*4bdc9457SAndroid Build Coastguard Worker scale(), zero_point(), qmin(), qmax(), 391*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 392*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) { 393*4bdc9457SAndroid Build Coastguard Worker int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()); 394*4bdc9457SAndroid Build Coastguard Worker if (input > 0) { 395*4bdc9457SAndroid Build Coastguard Worker input -= INT64_C(1) << (s() - 1); 396*4bdc9457SAndroid Build Coastguard Worker } else if (input < 0) { 397*4bdc9457SAndroid Build Coastguard Worker input += INT64_C(1) << (s() - 1); 398*4bdc9457SAndroid Build Coastguard Worker } 399*4bdc9457SAndroid Build Coastguard Worker if (int32_t(input) == input) { 400*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(i, int32_t(outputs[i - std::numeric_limits<int8_t>::min()])) 401*4bdc9457SAndroid Build Coastguard Worker << "i = " << i << ", input = " << input 402*4bdc9457SAndroid Build Coastguard Worker << ", s = " << s() << ", zero point = " << zero_point(); 403*4bdc9457SAndroid Build Coastguard Worker } 404*4bdc9457SAndroid Build Coastguard Worker } 405*4bdc9457SAndroid Build Coastguard Worker } 406*4bdc9457SAndroid Build Coastguard Worker TestDivideByPO2WithRoundingTiesUp(xnn_qs8_requantization_function requantize)407*4bdc9457SAndroid Build Coastguard Worker void TestDivideByPO2WithRoundingTiesUp(xnn_qs8_requantization_function requantize) { 408*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min()); 409*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max()); 410*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min()); 411*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max()); 412*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min()); 413*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max()); 414*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 415*4bdc9457SAndroid Build Coastguard Worker 416*4bdc9457SAndroid Build Coastguard Worker /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */ 417*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(s(), 1); 418*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(s(), 32); 419*4bdc9457SAndroid Build Coastguard Worker 420*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(256); 421*4bdc9457SAndroid Build Coastguard Worker std::vector<int8_t> outputs(inputs.size()); 422*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) { 423*4bdc9457SAndroid Build Coastguard Worker int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()); 424*4bdc9457SAndroid Build Coastguard Worker input -= INT64_C(1) << (s() - 1); 425*4bdc9457SAndroid Build Coastguard Worker inputs[i - std::numeric_limits<int8_t>::min()] = int32_t(input); 426*4bdc9457SAndroid Build Coastguard Worker } 427*4bdc9457SAndroid Build Coastguard Worker requantize(inputs.size(), inputs.data(), 428*4bdc9457SAndroid Build Coastguard Worker scale(), zero_point(), qmin(), qmax(), 429*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 430*4bdc9457SAndroid Build Coastguard Worker for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) { 431*4bdc9457SAndroid Build Coastguard Worker int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()); 432*4bdc9457SAndroid Build Coastguard Worker input -= INT64_C(1) << (s() - 1); 433*4bdc9457SAndroid Build Coastguard Worker if (int32_t(input) == input) { 434*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(i, int32_t(outputs[i - std::numeric_limits<int8_t>::min()])) 435*4bdc9457SAndroid Build Coastguard Worker << "i = " << i << ", input = " << input 436*4bdc9457SAndroid Build Coastguard Worker << ", s = " << s() << ", zero point = " << zero_point(); 437*4bdc9457SAndroid Build Coastguard Worker } 438*4bdc9457SAndroid Build Coastguard Worker } 439*4bdc9457SAndroid Build Coastguard Worker } 440*4bdc9457SAndroid Build Coastguard Worker TestSpecialCases(xnn_qu8_requantization_function requantize)441*4bdc9457SAndroid Build Coastguard Worker void TestSpecialCases(xnn_qu8_requantization_function requantize) { 442*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<uint8_t>::min()); 443*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<uint8_t>::max()); 444*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<uint8_t>::min()); 445*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<uint8_t>::max()); 446*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 447*4bdc9457SAndroid Build Coastguard Worker 448*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(256); 449*4bdc9457SAndroid Build Coastguard Worker std::vector<uint8_t> outputs(inputs.size()); 450*4bdc9457SAndroid Build Coastguard Worker 451*4bdc9457SAndroid Build Coastguard Worker std::fill(inputs.begin(), inputs.end(), std::numeric_limits<int32_t>::min()); 452*4bdc9457SAndroid Build Coastguard Worker for (int32_t zero_point = 0; zero_point <= std::numeric_limits<uint8_t>::max(); zero_point++) { 453*4bdc9457SAndroid Build Coastguard Worker requantize( 454*4bdc9457SAndroid Build Coastguard Worker inputs.size(), 455*4bdc9457SAndroid Build Coastguard Worker inputs.data(), 456*4bdc9457SAndroid Build Coastguard Worker ldexpf(1.0f, -32) /* scale */, 457*4bdc9457SAndroid Build Coastguard Worker zero_point /* zero point */, 458*4bdc9457SAndroid Build Coastguard Worker std::numeric_limits<uint8_t>::min(), 459*4bdc9457SAndroid Build Coastguard Worker std::numeric_limits<uint8_t>::max(), 460*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 461*4bdc9457SAndroid Build Coastguard Worker for (size_t i = 0; i < outputs.size(); i++) { 462*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(std::max(int32_t(int32_t(std::numeric_limits<uint8_t>::min())), zero_point - 1), int32_t(outputs[i])); 463*4bdc9457SAndroid Build Coastguard Worker } 464*4bdc9457SAndroid Build Coastguard Worker } 465*4bdc9457SAndroid Build Coastguard Worker 466*4bdc9457SAndroid Build Coastguard Worker std::fill(inputs.begin(), inputs.end(), std::numeric_limits<int32_t>::max()); 467*4bdc9457SAndroid Build Coastguard Worker requantize( 468*4bdc9457SAndroid Build Coastguard Worker inputs.size(), 469*4bdc9457SAndroid Build Coastguard Worker inputs.data(), 470*4bdc9457SAndroid Build Coastguard Worker 0x1.FFFFFEp-1f /* scale */, 471*4bdc9457SAndroid Build Coastguard Worker std::numeric_limits<uint8_t>::max() /* zero point */, 472*4bdc9457SAndroid Build Coastguard Worker std::numeric_limits<uint8_t>::min(), 473*4bdc9457SAndroid Build Coastguard Worker std::numeric_limits<uint8_t>::max(), 474*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 475*4bdc9457SAndroid Build Coastguard Worker for (size_t i = 0; i < outputs.size(); i++) { 476*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(std::numeric_limits<uint8_t>::max(), int32_t(outputs[i])); 477*4bdc9457SAndroid Build Coastguard Worker } 478*4bdc9457SAndroid Build Coastguard Worker } 479*4bdc9457SAndroid Build Coastguard Worker TestSpecialCases(xnn_qs8_requantization_function requantize)480*4bdc9457SAndroid Build Coastguard Worker void TestSpecialCases(xnn_qs8_requantization_function requantize) { 481*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min()); 482*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max()); 483*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min()); 484*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max()); 485*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 486*4bdc9457SAndroid Build Coastguard Worker 487*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(256); 488*4bdc9457SAndroid Build Coastguard Worker std::vector<int8_t> outputs(inputs.size()); 489*4bdc9457SAndroid Build Coastguard Worker 490*4bdc9457SAndroid Build Coastguard Worker std::fill(inputs.begin(), inputs.end(), std::numeric_limits<int32_t>::min()); 491*4bdc9457SAndroid Build Coastguard Worker for (int32_t zero_point = std::numeric_limits<int8_t>::min(); 492*4bdc9457SAndroid Build Coastguard Worker zero_point <= std::numeric_limits<int8_t>::max(); 493*4bdc9457SAndroid Build Coastguard Worker zero_point++) 494*4bdc9457SAndroid Build Coastguard Worker { 495*4bdc9457SAndroid Build Coastguard Worker requantize( 496*4bdc9457SAndroid Build Coastguard Worker inputs.size(), 497*4bdc9457SAndroid Build Coastguard Worker inputs.data(), 498*4bdc9457SAndroid Build Coastguard Worker ldexpf(1.0f, -32) /* scale */, 499*4bdc9457SAndroid Build Coastguard Worker zero_point, 500*4bdc9457SAndroid Build Coastguard Worker std::numeric_limits<int8_t>::min(), 501*4bdc9457SAndroid Build Coastguard Worker std::numeric_limits<int8_t>::max(), 502*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 503*4bdc9457SAndroid Build Coastguard Worker for (size_t i = 0; i < outputs.size(); i++) { 504*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(std::max(int32_t(std::numeric_limits<int8_t>::min()), zero_point - 1), int32_t(outputs[i])); 505*4bdc9457SAndroid Build Coastguard Worker } 506*4bdc9457SAndroid Build Coastguard Worker } 507*4bdc9457SAndroid Build Coastguard Worker 508*4bdc9457SAndroid Build Coastguard Worker std::fill(inputs.begin(), inputs.end(), std::numeric_limits<int32_t>::max()); 509*4bdc9457SAndroid Build Coastguard Worker requantize( 510*4bdc9457SAndroid Build Coastguard Worker inputs.size(), 511*4bdc9457SAndroid Build Coastguard Worker inputs.data(), 512*4bdc9457SAndroid Build Coastguard Worker 0x1.FFFFFEp-1f /* scale */, 513*4bdc9457SAndroid Build Coastguard Worker std::numeric_limits<int8_t>::max() /* zero point */, 514*4bdc9457SAndroid Build Coastguard Worker std::numeric_limits<int8_t>::min(), 515*4bdc9457SAndroid Build Coastguard Worker std::numeric_limits<int8_t>::max(), 516*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 517*4bdc9457SAndroid Build Coastguard Worker for (size_t i = 0; i < outputs.size(); i++) { 518*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(std::numeric_limits<int8_t>::max(), int32_t(outputs[i])); 519*4bdc9457SAndroid Build Coastguard Worker } 520*4bdc9457SAndroid Build Coastguard Worker } 521*4bdc9457SAndroid Build Coastguard Worker TestRandomCasesRoundToNearestTiesAway(xnn_qu8_requantization_function requantize)522*4bdc9457SAndroid Build Coastguard Worker void TestRandomCasesRoundToNearestTiesAway(xnn_qu8_requantization_function requantize) { 523*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(zero_point(), std::numeric_limits<uint8_t>::min()); 524*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(zero_point(), std::numeric_limits<uint8_t>::max()); 525*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<uint8_t>::min()); 526*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<uint8_t>::max()); 527*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<uint8_t>::min()); 528*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<uint8_t>::max()); 529*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 530*4bdc9457SAndroid Build Coastguard Worker 531*4bdc9457SAndroid Build Coastguard Worker std::random_device random_device; 532*4bdc9457SAndroid Build Coastguard Worker std::mt19937 rng(random_device()); 533*4bdc9457SAndroid Build Coastguard Worker for (size_t iteration = 0; iteration < iterations(); iteration++) { 534*4bdc9457SAndroid Build Coastguard Worker auto u8rng = 535*4bdc9457SAndroid Build Coastguard Worker std::bind(std::uniform_int_distribution<uint32_t>(0, std::numeric_limits<uint8_t>::max()), std::ref(rng)); 536*4bdc9457SAndroid Build Coastguard Worker 537*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(4096); 538*4bdc9457SAndroid Build Coastguard Worker std::vector<uint8_t> outputs(inputs.size()); 539*4bdc9457SAndroid Build Coastguard Worker 540*4bdc9457SAndroid Build Coastguard Worker std::uniform_real_distribution<float> scale_distribution(0x1.000000p-23f, 0x1.FFFFFEp-1f); 541*4bdc9457SAndroid Build Coastguard Worker const float scale = scale_distribution(rng); 542*4bdc9457SAndroid Build Coastguard Worker for (size_t i = 0; i < inputs.size(); i++) { 543*4bdc9457SAndroid Build Coastguard Worker const uint8_t approximate_output = std::min(std::max(uint8_t(u8rng()), uint8_t(qmin())), uint8_t(qmax())); 544*4bdc9457SAndroid Build Coastguard Worker const int32_t input = int32_t(double(approximate_output) / double(scale)); 545*4bdc9457SAndroid Build Coastguard Worker inputs[i] = input; 546*4bdc9457SAndroid Build Coastguard Worker } 547*4bdc9457SAndroid Build Coastguard Worker 548*4bdc9457SAndroid Build Coastguard Worker requantize( 549*4bdc9457SAndroid Build Coastguard Worker inputs.size(), inputs.data(), scale, zero_point(), qmin(), qmax(), 550*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 551*4bdc9457SAndroid Build Coastguard Worker 552*4bdc9457SAndroid Build Coastguard Worker /* Ensure that outputs are not all identical, as in this case the test doesn't validate much */ 553*4bdc9457SAndroid Build Coastguard Worker ASSERT_NE( 554*4bdc9457SAndroid Build Coastguard Worker *std::max_element(outputs.cbegin(), outputs.cend()), 555*4bdc9457SAndroid Build Coastguard Worker *std::min_element(outputs.cbegin(), outputs.cend())); 556*4bdc9457SAndroid Build Coastguard Worker 557*4bdc9457SAndroid Build Coastguard Worker for (size_t i = 0; i < inputs.size(); i++) { 558*4bdc9457SAndroid Build Coastguard Worker const uint8_t reference_output = xnn_qu8_requantize_rndna( 559*4bdc9457SAndroid Build Coastguard Worker inputs[i], scale, zero_point(), qmin(), qmax()); 560*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(uint32_t(reference_output), uint32_t(outputs[i])); 561*4bdc9457SAndroid Build Coastguard Worker } 562*4bdc9457SAndroid Build Coastguard Worker } 563*4bdc9457SAndroid Build Coastguard Worker } 564*4bdc9457SAndroid Build Coastguard Worker TestRandomCasesRoundToNearestTiesAway(xnn_qs8_requantization_function requantize)565*4bdc9457SAndroid Build Coastguard Worker void TestRandomCasesRoundToNearestTiesAway(xnn_qs8_requantization_function requantize) { 566*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min()); 567*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max()); 568*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min()); 569*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max()); 570*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min()); 571*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max()); 572*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 573*4bdc9457SAndroid Build Coastguard Worker 574*4bdc9457SAndroid Build Coastguard Worker std::random_device random_device; 575*4bdc9457SAndroid Build Coastguard Worker std::mt19937 rng(random_device()); 576*4bdc9457SAndroid Build Coastguard Worker for (size_t iteration = 0; iteration < iterations(); iteration++) { 577*4bdc9457SAndroid Build Coastguard Worker auto i8rng = std::bind( 578*4bdc9457SAndroid Build Coastguard Worker std::uniform_int_distribution<int32_t>(std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max()), std::ref(rng)); 579*4bdc9457SAndroid Build Coastguard Worker 580*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(4096); 581*4bdc9457SAndroid Build Coastguard Worker std::vector<int8_t> outputs(inputs.size()); 582*4bdc9457SAndroid Build Coastguard Worker 583*4bdc9457SAndroid Build Coastguard Worker std::uniform_real_distribution<float> scale_distribution(0x1.000000p-23f, 0x1.FFFFFEp-1f); 584*4bdc9457SAndroid Build Coastguard Worker const float scale = scale_distribution(rng); 585*4bdc9457SAndroid Build Coastguard Worker for (size_t i = 0; i < inputs.size(); i++) { 586*4bdc9457SAndroid Build Coastguard Worker const int8_t approximate_output = std::min(std::max(int8_t(i8rng()), int8_t(qmin())), int8_t(qmax())); 587*4bdc9457SAndroid Build Coastguard Worker const int32_t input = int32_t(double(approximate_output) / double(scale)); 588*4bdc9457SAndroid Build Coastguard Worker inputs[i] = input; 589*4bdc9457SAndroid Build Coastguard Worker } 590*4bdc9457SAndroid Build Coastguard Worker 591*4bdc9457SAndroid Build Coastguard Worker requantize( 592*4bdc9457SAndroid Build Coastguard Worker inputs.size(), inputs.data(), scale, zero_point(), qmin(), qmax(), 593*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 594*4bdc9457SAndroid Build Coastguard Worker 595*4bdc9457SAndroid Build Coastguard Worker /* Ensure that outputs are not all identical, as in this case the test doesn't validate much */ 596*4bdc9457SAndroid Build Coastguard Worker ASSERT_NE( 597*4bdc9457SAndroid Build Coastguard Worker *std::max_element(outputs.cbegin(), outputs.cend()), 598*4bdc9457SAndroid Build Coastguard Worker *std::min_element(outputs.cbegin(), outputs.cend())); 599*4bdc9457SAndroid Build Coastguard Worker 600*4bdc9457SAndroid Build Coastguard Worker for (size_t i = 0; i < inputs.size(); i++) { 601*4bdc9457SAndroid Build Coastguard Worker const int8_t reference_output = xnn_qs8_requantize_rndna( 602*4bdc9457SAndroid Build Coastguard Worker inputs[i], scale, zero_point(), qmin(), qmax()); 603*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(int32_t(reference_output), int32_t(outputs[i])); 604*4bdc9457SAndroid Build Coastguard Worker } 605*4bdc9457SAndroid Build Coastguard Worker } 606*4bdc9457SAndroid Build Coastguard Worker } 607*4bdc9457SAndroid Build Coastguard Worker TestRandomCasesRoundToNearestTiesUp(xnn_qs8_requantization_function requantize)608*4bdc9457SAndroid Build Coastguard Worker void TestRandomCasesRoundToNearestTiesUp(xnn_qs8_requantization_function requantize) { 609*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min()); 610*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max()); 611*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min()); 612*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max()); 613*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min()); 614*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max()); 615*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 616*4bdc9457SAndroid Build Coastguard Worker 617*4bdc9457SAndroid Build Coastguard Worker std::random_device random_device; 618*4bdc9457SAndroid Build Coastguard Worker std::mt19937 rng(random_device()); 619*4bdc9457SAndroid Build Coastguard Worker for (size_t iteration = 0; iteration < iterations(); iteration++) { 620*4bdc9457SAndroid Build Coastguard Worker auto i8rng = std::bind( 621*4bdc9457SAndroid Build Coastguard Worker std::uniform_int_distribution<int32_t>(std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max()), std::ref(rng)); 622*4bdc9457SAndroid Build Coastguard Worker 623*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(4096); 624*4bdc9457SAndroid Build Coastguard Worker std::vector<int8_t> outputs(inputs.size()); 625*4bdc9457SAndroid Build Coastguard Worker 626*4bdc9457SAndroid Build Coastguard Worker std::uniform_real_distribution<float> scale_distribution(0x1.000000p-23f, 0x1.FFFFFEp-1f); 627*4bdc9457SAndroid Build Coastguard Worker const float scale = scale_distribution(rng); 628*4bdc9457SAndroid Build Coastguard Worker for (size_t i = 0; i < inputs.size(); i++) { 629*4bdc9457SAndroid Build Coastguard Worker const int8_t approximate_output = std::min(std::max(int8_t(i8rng()), int8_t(qmin())), int8_t(qmax())); 630*4bdc9457SAndroid Build Coastguard Worker const int32_t input = int32_t(double(approximate_output) / double(scale)); 631*4bdc9457SAndroid Build Coastguard Worker inputs[i] = input; 632*4bdc9457SAndroid Build Coastguard Worker } 633*4bdc9457SAndroid Build Coastguard Worker 634*4bdc9457SAndroid Build Coastguard Worker requantize( 635*4bdc9457SAndroid Build Coastguard Worker inputs.size(), inputs.data(), scale, zero_point(), qmin(), qmax(), 636*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 637*4bdc9457SAndroid Build Coastguard Worker 638*4bdc9457SAndroid Build Coastguard Worker /* Ensure that outputs are not all identical, as in this case the test doesn't validate much */ 639*4bdc9457SAndroid Build Coastguard Worker ASSERT_NE( 640*4bdc9457SAndroid Build Coastguard Worker *std::max_element(outputs.cbegin(), outputs.cend()), 641*4bdc9457SAndroid Build Coastguard Worker *std::min_element(outputs.cbegin(), outputs.cend())); 642*4bdc9457SAndroid Build Coastguard Worker 643*4bdc9457SAndroid Build Coastguard Worker for (size_t i = 0; i < inputs.size(); i++) { 644*4bdc9457SAndroid Build Coastguard Worker const int8_t reference_output = xnn_qs8_requantize_rndnu( 645*4bdc9457SAndroid Build Coastguard Worker inputs[i], scale, zero_point(), qmin(), qmax()); 646*4bdc9457SAndroid Build Coastguard Worker ASSERT_EQ(int32_t(reference_output), int32_t(outputs[i])); 647*4bdc9457SAndroid Build Coastguard Worker } 648*4bdc9457SAndroid Build Coastguard Worker } 649*4bdc9457SAndroid Build Coastguard Worker } 650*4bdc9457SAndroid Build Coastguard Worker TestRandomCasesApproximate(xnn_qu8_requantization_function requantize)651*4bdc9457SAndroid Build Coastguard Worker void TestRandomCasesApproximate(xnn_qu8_requantization_function requantize) { 652*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(zero_point(), std::numeric_limits<uint8_t>::min()); 653*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(zero_point(), std::numeric_limits<uint8_t>::max()); 654*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<uint8_t>::min()); 655*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<uint8_t>::max()); 656*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<uint8_t>::min()); 657*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<uint8_t>::max()); 658*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 659*4bdc9457SAndroid Build Coastguard Worker 660*4bdc9457SAndroid Build Coastguard Worker std::random_device random_device; 661*4bdc9457SAndroid Build Coastguard Worker std::mt19937 rng(random_device()); 662*4bdc9457SAndroid Build Coastguard Worker for (size_t iteration = 0; iteration < iterations(); iteration++) { 663*4bdc9457SAndroid Build Coastguard Worker auto u8rng = 664*4bdc9457SAndroid Build Coastguard Worker std::bind(std::uniform_int_distribution<uint32_t>(0, std::numeric_limits<uint8_t>::max()), std::ref(rng)); 665*4bdc9457SAndroid Build Coastguard Worker 666*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(4096); 667*4bdc9457SAndroid Build Coastguard Worker std::vector<uint8_t> outputs(inputs.size()); 668*4bdc9457SAndroid Build Coastguard Worker 669*4bdc9457SAndroid Build Coastguard Worker std::uniform_real_distribution<float> scale_distribution(0x1.000000p-23f, 0x1.FFFFFEp-1f); 670*4bdc9457SAndroid Build Coastguard Worker const float scale = scale_distribution(rng); 671*4bdc9457SAndroid Build Coastguard Worker for (size_t i = 0; i < inputs.size(); i++) { 672*4bdc9457SAndroid Build Coastguard Worker const uint8_t approximate_output = std::min(std::max(uint8_t(u8rng()), uint8_t(qmin())), uint8_t(qmax())); 673*4bdc9457SAndroid Build Coastguard Worker const int32_t input = int32_t(double(approximate_output) / double(scale)); 674*4bdc9457SAndroid Build Coastguard Worker inputs[i] = input; 675*4bdc9457SAndroid Build Coastguard Worker } 676*4bdc9457SAndroid Build Coastguard Worker 677*4bdc9457SAndroid Build Coastguard Worker requantize( 678*4bdc9457SAndroid Build Coastguard Worker inputs.size(), inputs.data(), scale, zero_point(), qmin(), qmax(), 679*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 680*4bdc9457SAndroid Build Coastguard Worker 681*4bdc9457SAndroid Build Coastguard Worker /* Ensure that outputs are not all identical, as in this case Test doesn't validate much */ 682*4bdc9457SAndroid Build Coastguard Worker ASSERT_NE( 683*4bdc9457SAndroid Build Coastguard Worker *std::max_element(outputs.cbegin(), outputs.cend()), 684*4bdc9457SAndroid Build Coastguard Worker *std::min_element(outputs.cbegin(), outputs.cend())); 685*4bdc9457SAndroid Build Coastguard Worker 686*4bdc9457SAndroid Build Coastguard Worker for (size_t i = 0; i < inputs.size(); i++) { 687*4bdc9457SAndroid Build Coastguard Worker const double reference_output = RequantizationTester::RequantizeApproximate( 688*4bdc9457SAndroid Build Coastguard Worker inputs[i], scale, uint8_t(zero_point()), uint8_t(qmin()), uint8_t(qmax())); 689*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(std::abs(reference_output - double(outputs[i])), 0.55) 690*4bdc9457SAndroid Build Coastguard Worker << "input = " << inputs[i] << ", output = " << int32_t(outputs[i]) 691*4bdc9457SAndroid Build Coastguard Worker << ", reference output = " << reference_output; 692*4bdc9457SAndroid Build Coastguard Worker } 693*4bdc9457SAndroid Build Coastguard Worker } 694*4bdc9457SAndroid Build Coastguard Worker } 695*4bdc9457SAndroid Build Coastguard Worker TestRandomCasesApproximate(xnn_qs8_requantization_function requantize)696*4bdc9457SAndroid Build Coastguard Worker void TestRandomCasesApproximate(xnn_qs8_requantization_function requantize) { 697*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min()); 698*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max()); 699*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min()); 700*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max()); 701*4bdc9457SAndroid Build Coastguard Worker ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min()); 702*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max()); 703*4bdc9457SAndroid Build Coastguard Worker ASSERT_LT(qmin(), qmax()); 704*4bdc9457SAndroid Build Coastguard Worker 705*4bdc9457SAndroid Build Coastguard Worker std::random_device random_device; 706*4bdc9457SAndroid Build Coastguard Worker std::mt19937 rng(random_device()); 707*4bdc9457SAndroid Build Coastguard Worker for (size_t iteration = 0; iteration < iterations(); iteration++) { 708*4bdc9457SAndroid Build Coastguard Worker auto i8rng = std::bind( 709*4bdc9457SAndroid Build Coastguard Worker std::uniform_int_distribution<int32_t>(std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max()), std::ref(rng)); 710*4bdc9457SAndroid Build Coastguard Worker 711*4bdc9457SAndroid Build Coastguard Worker std::vector<int32_t> inputs(4096); 712*4bdc9457SAndroid Build Coastguard Worker std::vector<int8_t> outputs(inputs.size()); 713*4bdc9457SAndroid Build Coastguard Worker 714*4bdc9457SAndroid Build Coastguard Worker std::uniform_real_distribution<float> scale_distribution(0x1.000000p-23f, 0x1.FFFFFEp-1f); 715*4bdc9457SAndroid Build Coastguard Worker const float scale = scale_distribution(rng); 716*4bdc9457SAndroid Build Coastguard Worker for (size_t i = 0; i < inputs.size(); i++) { 717*4bdc9457SAndroid Build Coastguard Worker const int8_t approximate_output = std::min(std::max(int8_t(i8rng()), int8_t(qmin())), int8_t(qmax())); 718*4bdc9457SAndroid Build Coastguard Worker const int32_t input = int32_t(double(approximate_output) / double(scale)); 719*4bdc9457SAndroid Build Coastguard Worker inputs[i] = input; 720*4bdc9457SAndroid Build Coastguard Worker } 721*4bdc9457SAndroid Build Coastguard Worker 722*4bdc9457SAndroid Build Coastguard Worker requantize( 723*4bdc9457SAndroid Build Coastguard Worker inputs.size(), inputs.data(), scale, zero_point(), qmin(), qmax(), 724*4bdc9457SAndroid Build Coastguard Worker outputs.data()); 725*4bdc9457SAndroid Build Coastguard Worker 726*4bdc9457SAndroid Build Coastguard Worker /* Ensure that outputs are not all identical, as in this case Test doesn't validate much */ 727*4bdc9457SAndroid Build Coastguard Worker ASSERT_NE( 728*4bdc9457SAndroid Build Coastguard Worker *std::max_element(outputs.cbegin(), outputs.cend()), 729*4bdc9457SAndroid Build Coastguard Worker *std::min_element(outputs.cbegin(), outputs.cend())); 730*4bdc9457SAndroid Build Coastguard Worker 731*4bdc9457SAndroid Build Coastguard Worker for (size_t i = 0; i < inputs.size(); i++) { 732*4bdc9457SAndroid Build Coastguard Worker const double reference_output = RequantizationTester::RequantizeApproximate( 733*4bdc9457SAndroid Build Coastguard Worker inputs[i], scale, int8_t(zero_point()), int8_t(qmin()), int8_t(qmax())); 734*4bdc9457SAndroid Build Coastguard Worker ASSERT_LE(std::abs(reference_output - double(outputs[i])), 0.55) 735*4bdc9457SAndroid Build Coastguard Worker << "input = " << inputs[i] << ", output = " << int32_t(outputs[i]) 736*4bdc9457SAndroid Build Coastguard Worker << ", reference output = " << reference_output; 737*4bdc9457SAndroid Build Coastguard Worker } 738*4bdc9457SAndroid Build Coastguard Worker } 739*4bdc9457SAndroid Build Coastguard Worker } 740*4bdc9457SAndroid Build Coastguard Worker ShiftLeft(int64_t w,uint32_t n)741*4bdc9457SAndroid Build Coastguard Worker static inline int64_t ShiftLeft(int64_t w, uint32_t n) { 742*4bdc9457SAndroid Build Coastguard Worker return (int64_t) ((uint64_t) w << n); 743*4bdc9457SAndroid Build Coastguard Worker } 744*4bdc9457SAndroid Build Coastguard Worker RequantizeApproximate(int32_t value,float scale,uint8_t zero_point,uint8_t qmin,uint8_t qmax)745*4bdc9457SAndroid Build Coastguard Worker static inline double RequantizeApproximate( 746*4bdc9457SAndroid Build Coastguard Worker int32_t value, 747*4bdc9457SAndroid Build Coastguard Worker float scale, 748*4bdc9457SAndroid Build Coastguard Worker uint8_t zero_point, 749*4bdc9457SAndroid Build Coastguard Worker uint8_t qmin, 750*4bdc9457SAndroid Build Coastguard Worker uint8_t qmax) 751*4bdc9457SAndroid Build Coastguard Worker { 752*4bdc9457SAndroid Build Coastguard Worker assert(scale < 1.0f); 753*4bdc9457SAndroid Build Coastguard Worker assert(scale >= 0x1.0p-32f); 754*4bdc9457SAndroid Build Coastguard Worker 755*4bdc9457SAndroid Build Coastguard Worker return std::min(std::max(double(value) * double(scale) + double(zero_point), double(qmin)), double(qmax)); 756*4bdc9457SAndroid Build Coastguard Worker } 757*4bdc9457SAndroid Build Coastguard Worker RequantizeApproximate(int32_t value,float scale,int8_t zero_point,int8_t qmin,int8_t qmax)758*4bdc9457SAndroid Build Coastguard Worker static inline double RequantizeApproximate( 759*4bdc9457SAndroid Build Coastguard Worker int32_t value, 760*4bdc9457SAndroid Build Coastguard Worker float scale, 761*4bdc9457SAndroid Build Coastguard Worker int8_t zero_point, 762*4bdc9457SAndroid Build Coastguard Worker int8_t qmin, 763*4bdc9457SAndroid Build Coastguard Worker int8_t qmax) 764*4bdc9457SAndroid Build Coastguard Worker { 765*4bdc9457SAndroid Build Coastguard Worker assert(scale < 1.0f); 766*4bdc9457SAndroid Build Coastguard Worker assert(scale >= 0x1.0p-32f); 767*4bdc9457SAndroid Build Coastguard Worker 768*4bdc9457SAndroid Build Coastguard Worker return std::min(std::max(double(value) * double(scale) + double(zero_point), double(qmin)), double(qmax)); 769*4bdc9457SAndroid Build Coastguard Worker } 770*4bdc9457SAndroid Build Coastguard Worker 771*4bdc9457SAndroid Build Coastguard Worker private: 772*4bdc9457SAndroid Build Coastguard Worker uint32_t s_{1}; 773*4bdc9457SAndroid Build Coastguard Worker int32_t zero_point_{0}; 774*4bdc9457SAndroid Build Coastguard Worker int16_t qmin_{std::numeric_limits<int16_t>::min()}; 775*4bdc9457SAndroid Build Coastguard Worker int16_t qmax_{std::numeric_limits<int16_t>::max()}; 776*4bdc9457SAndroid Build Coastguard Worker size_t iterations_{1}; 777*4bdc9457SAndroid Build Coastguard Worker }; 778