aboutsummaryrefslogtreecommitdiff
path: root/reference_model/src
diff options
context:
space:
mode:
authorJeremy Johnson <jeremy.johnson@arm.com>2023-10-26 13:53:14 +0100
committerEric Kunze <eric.kunze@arm.com>2023-11-02 23:22:09 +0000
commita4d907e8686791dd84ed987d0d79325c4d908b73 (patch)
tree9748ef39183b7548a9ff50d457920eace3a6fdec /reference_model/src
parentd1a08ce27ef8d0f6cf77e1b864610aade06edc5c (diff)
downloadreference_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.cc62
-rw-r--r--reference_model/src/generate/generate_utils.cc7
-rw-r--r--reference_model/src/generate/generate_utils.h2
-rw-r--r--reference_model/src/verify/verifiers.h2
-rw-r--r--reference_model/src/verify/verify_dot_product.cc1
-rw-r--r--reference_model/src/verify/verify_entry.cc2
-rw-r--r--reference_model/src/verify/verify_ulp.cc47
-rw-r--r--reference_model/src/verify/verify_utils.cc20
-rw-r--r--reference_model/src/verify/verify_utils.h10
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;