diff options
author | Jeremy Johnson <jeremy.johnson@arm.com> | 2023-10-26 13:53:14 +0100 |
---|---|---|
committer | Eric Kunze <eric.kunze@arm.com> | 2023-11-02 23:22:09 +0000 |
commit | a4d907e8686791dd84ed987d0d79325c4d908b73 (patch) | |
tree | 9748ef39183b7548a9ff50d457920eace3a6fdec /reference_model/src | |
parent | d1a08ce27ef8d0f6cf77e1b864610aade06edc5c (diff) | |
download | reference_model-a4d907e8686791dd84ed987d0d79325c4d908b73.tar.gz |
Main compliance testing support for MUL
Update verify ULP mode to allow fractions (e.g. 0.5).
Update pseudo generator to accept ranges.
Fix up pseudo random distribution based on ranges.
Change-Id: I9168c5f7d37722678c0f1f9e906953c8cec367b1
Signed-off-by: Jeremy Johnson <jeremy.johnson@arm.com>
Diffstat (limited to 'reference_model/src')
-rw-r--r-- | reference_model/src/generate/generate_pseudo_random.cc | 62 | ||||
-rw-r--r-- | reference_model/src/generate/generate_utils.cc | 7 | ||||
-rw-r--r-- | reference_model/src/generate/generate_utils.h | 2 | ||||
-rw-r--r-- | reference_model/src/verify/verifiers.h | 2 | ||||
-rw-r--r-- | reference_model/src/verify/verify_dot_product.cc | 1 | ||||
-rw-r--r-- | reference_model/src/verify/verify_entry.cc | 2 | ||||
-rw-r--r-- | reference_model/src/verify/verify_ulp.cc | 47 | ||||
-rw-r--r-- | reference_model/src/verify/verify_utils.cc | 20 | ||||
-rw-r--r-- | reference_model/src/verify/verify_utils.h | 10 |
9 files changed, 107 insertions, 46 deletions
diff --git a/reference_model/src/generate/generate_pseudo_random.cc b/reference_model/src/generate/generate_pseudo_random.cc index 858a4b2..f234796 100644 --- a/reference_model/src/generate/generate_pseudo_random.cc +++ b/reference_model/src/generate/generate_pseudo_random.cc @@ -40,40 +40,76 @@ public: constexpr auto min = std::numeric_limits<FP>::lowest() / 2; constexpr auto max = std::numeric_limits<FP>::max() / 2; static_assert(max <= std::numeric_limits<FP>::max() + min); - _unidis = std::uniform_real_distribution<FP>(min, max); - // Piecewise Constant distribution - const std::array<double, 7> intervals{ min, min + 1000, -1000.0, 0.0, 1000.0, max - 1000, max }; - const std::array<double, 7> weights{ 1.0, 0.1, 1.0, 2.0, 1.0, 0.1, 1.0 }; - _pwcdis = std::piecewise_constant_distribution<FP>(intervals.begin(), intervals.end(), weights.begin()); + setDistribution(min, max); } - FP getRandomUniformFloat() + PseudoRandomGeneratorFloat(uint64_t seed, FP min, FP max) + : _gen(seed) { - return _unidis(_gen); + setDistribution(min, max); } - FP getRandomPWCFloat() + FP getRandomFloat() { - return _pwcdis(_gen); + if (_useUniform) + return _unidis(_gen); + else + return _pwcdis(_gen); } private: + void setDistribution(FP min, FP max) + { + _unidis = std::uniform_real_distribution<FP>(min, max); + + // Piecewise Constant distribution for larger ranges + double range = std::abs(max - min); + double mid; + if (max == -min) + mid = 0.f; + else + mid = (range / 2) + min; + double segment = std::min<double>(1000.0, range / 5); + + const std::array<double, 7> intervals{ + min, min + segment, mid - segment, mid, mid + segment, max - segment, max + }; + const std::array<double, 7> weights{ 1.0, 0.1, 1.0, 2.0, 1.0, 0.1, 1.0 }; + _pwcdis = std::piecewise_constant_distribution<FP>(intervals.begin(), intervals.end(), weights.begin()); + + // Uniform distribution works well on smaller ranges + _useUniform = (range < 2000.0); + } + std::mt19937 _gen; std::uniform_real_distribution<FP> _unidis; std::piecewise_constant_distribution<FP> _pwcdis; + bool _useUniform; }; bool generateFP32(const TosaReference::GenerateConfig& cfg, void* data, size_t size) { const TosaReference::PseudoRandomInfo& prinfo = cfg.pseudoRandomInfo; - PseudoRandomGeneratorFloat<float> generator(prinfo.rngSeed); + + PseudoRandomGeneratorFloat<float>* generator; + + if (prinfo.range.size() == 2) + { + const float min = std::stof(prinfo.range[0]); + const float max = std::stof(prinfo.range[1]); + generator = new PseudoRandomGeneratorFloat<float>(prinfo.rngSeed, min, max); + } + else + { + generator = new PseudoRandomGeneratorFloat<float>(prinfo.rngSeed); + } float* a = reinterpret_cast<float*>(data); const auto T = TosaReference::numElementsFromShape(cfg.shape); for (auto t = 0; t < T; ++t) { - a[t] = generator.getRandomPWCFloat(); + a[t] = generator->getRandomFloat(); } return true; } @@ -90,6 +126,10 @@ bool generatePseudoRandom(const GenerateConfig& cfg, void* data, size_t size) WARNING("[Generator][PR] Unknown operator."); return false; } + if (cfg.pseudoRandomInfo.range.size() != 0 || cfg.pseudoRandomInfo.range.size() != 2) + { + WARNING("[Generator][PR] Invalid range."); + } switch (cfg.dataType) { diff --git a/reference_model/src/generate/generate_utils.cc b/reference_model/src/generate/generate_utils.cc index d3bb076..ae6dfcb 100644 --- a/reference_model/src/generate/generate_utils.cc +++ b/reference_model/src/generate/generate_utils.cc @@ -38,10 +38,11 @@ NLOHMANN_JSON_SERIALIZE_ENUM(DType, NLOHMANN_JSON_SERIALIZE_ENUM(Op, { { Op::Op_UNKNOWN, "UNKNOWN" }, + { Op::Op_CONV2D, "CONV2D" }, { Op::Op_MATMUL, "MATMUL" }, { Op::Op_MAX_POOL2D, "MAX_POOL2D" }, + { Op::Op_MUL, "MUL" }, { Op::Op_PAD, "PAD" }, - { Op::Op_CONV2D, "CONV2D" }, }) } // namespace tosa @@ -84,6 +85,10 @@ void from_json(const nlohmann::json& j, DotProductInfo& dotProductInfo) void from_json(const nlohmann::json& j, PseudoRandomInfo& pseudoRandomInfo) { j.at("rng_seed").get_to(pseudoRandomInfo.rngSeed); + if (j.contains("range")) + { + j.at("range").get_to(pseudoRandomInfo.range); + } } void from_json(const nlohmann::json& j, GenerateConfig& cfg) diff --git a/reference_model/src/generate/generate_utils.h b/reference_model/src/generate/generate_utils.h index 7c55f1d..8d0f654 100644 --- a/reference_model/src/generate/generate_utils.h +++ b/reference_model/src/generate/generate_utils.h @@ -61,7 +61,7 @@ struct PseudoRandomInfo PseudoRandomInfo() = default; int64_t rngSeed; - // TODO: Add range support + std::vector<std::string> range; }; /// \brief Generator configuration diff --git a/reference_model/src/verify/verifiers.h b/reference_model/src/verify/verifiers.h index dd97122..fcfb3b3 100644 --- a/reference_model/src/verify/verifiers.h +++ b/reference_model/src/verify/verifiers.h @@ -58,7 +58,7 @@ bool verifyReduceProduct(const CTensor* referenceTensor, const CTensor* implemen /// \param ulp The ULP tolerence for the comparison of the two tensors /// /// \return True if compliant else false -bool verifyULP(const CTensor* referenceTensor, const CTensor* implementationTensor, uint64_t ulp); +bool verifyULP(const CTensor* referenceTensor, const CTensor* implementationTensor, const UlpInfo& ulpInfo); }; // namespace TosaReference diff --git a/reference_model/src/verify/verify_dot_product.cc b/reference_model/src/verify/verify_dot_product.cc index 233c072..863640f 100644 --- a/reference_model/src/verify/verify_dot_product.cc +++ b/reference_model/src/verify/verify_dot_product.cc @@ -14,7 +14,6 @@ #include "func_debug.h" #include "verifiers.h" -#include "verify_utils.h" #include <cmath> #include <numeric> diff --git a/reference_model/src/verify/verify_entry.cc b/reference_model/src/verify/verify_entry.cc index 67eb7df..4da3bde 100644 --- a/reference_model/src/verify/verify_entry.cc +++ b/reference_model/src/verify/verify_entry.cc @@ -38,7 +38,7 @@ bool verify(const CTensor* ref, const CTensor* refBnd, const CTensor* imp, const return verifyReduceProduct(ref, imp, cfg.reduceProductInfo.m, cfg.reduceProductInfo.n); } case VerifyMode::Ulp: { - return verifyULP(ref, imp, cfg.ulpInfo.ulp); + return verifyULP(ref, imp, cfg.ulpInfo); } default: { WARNING("[Verifier] Unsupported verification mode."); diff --git a/reference_model/src/verify/verify_ulp.cc b/reference_model/src/verify/verify_ulp.cc index 486c0ff..8c27191 100644 --- a/reference_model/src/verify/verify_ulp.cc +++ b/reference_model/src/verify/verify_ulp.cc @@ -31,7 +31,7 @@ static_assert(std::numeric_limits<double>::is_iec559, "TOSA Reference Model has not been built with standard IEE574 64-bit float support; ULP based " "verifcation is invalid"); -bool tosaCheckULP(float testValue, double referenceValue, int64_t ulpCount) +bool tosaCheckULP(double referenceValue, float testValue, double ulpNum) { // Start by sanitizing the input. @@ -71,57 +71,55 @@ bool tosaCheckULP(float testValue, double referenceValue, int64_t ulpCount) else { // Find the exponent of the reference value. - int referenceExponent; - std::frexp(referenceValue, &referenceExponent); + int32_t referenceExponent = ilog2(referenceValue); // Work out the values magnitude - by raising 2 to the power of the // exponent and taking the normalized minimum for denormal values - const double referencePower2 = - std::max(std::ldexp(1.0, referenceExponent), static_cast<double>(std::numeric_limits<float>::min())); + const double referencePower2 = std::max(exp2(referenceExponent), AccPrecision<float>::normal_min); // Get the value of changing the last bit - by shifting the least significant bit to this magnitude // i.e. the ULP. - double ulpValue = referencePower2 * std::ldexp(1.0, -23); - - // It is possible that within one ULP we cross a boundary where we need to change the exponent, - // if this happens we will take the ULP for the larger exponent. - if (referenceValue + ulpValue > 2 * referencePower2) - { - ulpValue = 2 * ulpValue; - } + double ulpValue = referencePower2 * exp2(-AccPrecision<float>::normal_frac); // Scale by the number of ULPs requested by the user. - referenceMax = referenceValue + ulpValue * ulpCount; - referenceMin = referenceValue - ulpValue * ulpCount; + referenceMax = referenceValue + ulpValue * ulpNum; + referenceMin = referenceValue - ulpValue * ulpNum; // Handle the overflow cases. - if (referenceMax > std::numeric_limits<float>::max()) + if (referenceMax > AccPrecision<float>::normal_max) { referenceMax = std::numeric_limits<float>::infinity(); } - if (referenceMin > std::numeric_limits<float>::max()) + if (referenceMin > AccPrecision<float>::normal_max) { referenceMin = std::numeric_limits<float>::infinity(); } // And the underflow cases. - if (referenceMax < std::numeric_limits<float>::min()) + if (referenceMax < AccPrecision<float>::normal_min) { - referenceMax = std::numeric_limits<float>::min(); + referenceMax = AccPrecision<float>::normal_min; } - if (referenceMin < std::numeric_limits<float>::min()) + if (referenceMin < AccPrecision<float>::normal_min) { - referenceMin = 0; + referenceMin = 0.0; } } // And finally... Do the comparison. - return static_cast<double>(testValue) >= referenceMin && static_cast<double>(testValue) <= referenceMax; + double testValue64 = static_cast<double>(testValue); + bool withinUlp = testValue64 >= referenceMin && testValue64 <= referenceMax; + if (!withinUlp) + { + WARNING("[Verfier][ULP] value (%10f) is not in ULP %g range (%10f <= ref (%10f) <= %10f).", testValue64, ulpNum, + referenceMin, referenceValue, referenceMax); + } + return withinUlp; } } // namespace -bool verifyULP(const CTensor* referenceTensor, const CTensor* implementationTensor, uint64_t ulp) +bool verifyULP(const CTensor* referenceTensor, const CTensor* implementationTensor, const UlpInfo& ulpInfo) { // Validate that tensors are provided TOSA_REF_REQUIRE(referenceTensor != nullptr, "[ULP] Reference tensor is missing"); @@ -132,10 +130,11 @@ bool verifyULP(const CTensor* referenceTensor, const CTensor* implementationTens numElements(std::vector<int32_t>(referenceTensor->shape, referenceTensor->shape + referenceTensor->num_dims)); TOSA_REF_REQUIRE(elementCount > 0, "[ULP] Invalid shape for reference tensor"); + const double ulp = ulpInfo.ulp; switch (implementationTensor->data_type) { case tosa_datatype_fp32_t: { - const auto* refData = reinterpret_cast<const float*>(referenceTensor->data); + const auto* refData = reinterpret_cast<const double*>(referenceTensor->data); TOSA_REF_REQUIRE(refData != nullptr, "[ULP] Missing data for reference"); const auto* impData = reinterpret_cast<const float*>(implementationTensor->data); TOSA_REF_REQUIRE(impData != nullptr, "[ULP] Missing data for implementation"); diff --git a/reference_model/src/verify/verify_utils.cc b/reference_model/src/verify/verify_utils.cc index 43ecbe7..99cb0c1 100644 --- a/reference_model/src/verify/verify_utils.cc +++ b/reference_model/src/verify/verify_utils.cc @@ -50,7 +50,6 @@ NLOHMANN_JSON_SERIALIZE_ENUM(VerifyMode, { VerifyMode::DotProduct, "DOT_PRODUCT" }, { VerifyMode::ReduceProduct, "REDUCE_PRODUCT" }, { VerifyMode::FpSpecial, "FP_SPECIAL" }, - { VerifyMode::Round, "ROUND" }, }) void from_json(const nlohmann::json& j, UlpInfo& ulpInfo) @@ -144,7 +143,24 @@ DType mapToDType(tosa_datatype_t dataType) // Like const_exp2 but for use during runtime double exp2(int32_t n) { - TOSA_REF_REQUIRE(-1022 <= n && n <= 1023, " Invalid exponent value (%d)", n); + TOSA_REF_REQUIRE(-1022 <= n && n <= 1023, " Invalid exponent value (%d) in exp2", n); return const_exp2(n); } + +int32_t ilog2(double v) +{ + TOSA_REF_REQUIRE(0.0 < v && v < std::numeric_limits<double>::infinity(), " Value out of range (%g) in ilog2", v); + int32_t n = 0; + while (v >= 2.0) + { + v = v / 2.0; + n++; + } + while (v < 1.0) + { + v = v * 2.0; + n--; + } + return n; +} } // namespace TosaReference diff --git a/reference_model/src/verify/verify_utils.h b/reference_model/src/verify/verify_utils.h index 486ce19..15d7ba5 100644 --- a/reference_model/src/verify/verify_utils.h +++ b/reference_model/src/verify/verify_utils.h @@ -44,8 +44,7 @@ enum class VerifyMode Ulp, DotProduct, ReduceProduct, - FpSpecial, - Round + FpSpecial }; /// \brief ULP verification meta-data @@ -53,7 +52,7 @@ struct UlpInfo { UlpInfo() = default; - uint64_t ulp; + double ulp; }; /// \brief Dot-product verification meta-data @@ -95,7 +94,7 @@ int64_t numElements(const std::vector<int32_t>& shape); /// \brief Map API data-type to DType DType mapToDType(tosa_datatype_t dataType); -/// \brief Raise a value by the power of N or -N +/// \brief Return 2 to the power of N or -N // For use during compile time - as no range check constexpr double const_exp2(int32_t n) { @@ -116,6 +115,9 @@ constexpr double const_exp2(int32_t n) /// \brief Same as const_exp2 but with runtime range check of N double exp2(int32_t n); +/// \brief Return the base-2 exponent of V +int32_t ilog2(double v); + /// \brief Accuracy precision information template <typename T> struct AccPrecision; |