aboutsummaryrefslogtreecommitdiff
path: root/reference_model/src/verify
diff options
context:
space:
mode:
Diffstat (limited to 'reference_model/src/verify')
-rw-r--r--reference_model/src/verify/verifiers.h9
-rw-r--r--reference_model/src/verify/verify_abs_error.cc74
-rw-r--r--reference_model/src/verify/verify_entry.cc3
-rw-r--r--reference_model/src/verify/verify_ulp.cc94
-rw-r--r--reference_model/src/verify/verify_utils.cc96
-rw-r--r--reference_model/src/verify/verify_utils.h6
6 files changed, 197 insertions, 85 deletions
diff --git a/reference_model/src/verify/verifiers.h b/reference_model/src/verify/verifiers.h
index fcfb3b3..f2590cb 100644
--- a/reference_model/src/verify/verifiers.h
+++ b/reference_model/src/verify/verifiers.h
@@ -60,6 +60,15 @@ bool verifyReduceProduct(const CTensor* referenceTensor, const CTensor* implemen
/// \return True if compliant else false
bool verifyULP(const CTensor* referenceTensor, const CTensor* implementationTensor, const UlpInfo& ulpInfo);
+/// \brief Perform abs-error based verification
+///
+/// \param ref Reference tensor
+/// \param refBnd Reference bounds tensor (according to op)
+/// \param imp Implementation resulting tensor
+///
+/// \return True if compliant else false
+bool verifyAbsError(const CTensor* ref, const CTensor* refBnd, const CTensor* imp);
+
}; // namespace TosaReference
#endif // VERIFIERS_H_
diff --git a/reference_model/src/verify/verify_abs_error.cc b/reference_model/src/verify/verify_abs_error.cc
new file mode 100644
index 0000000..1afa7fd
--- /dev/null
+++ b/reference_model/src/verify/verify_abs_error.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2023, ARM Limited.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cmath>
+#include <limits>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+#include "verifiers.h"
+
+namespace TosaReference
+{
+
+namespace
+{
+bool validateData(const double* ref, const double* bnd, const float* imp, const std::vector<int32_t>& shape)
+{
+ const size_t T = static_cast<size_t>(numElements(shape));
+ TOSA_REF_REQUIRE(T > 0, "[AE] Invalid shape for reference tensor");
+
+ for (size_t i = 0; i < T; ++i)
+ {
+ double errBound = ref[i] * exp2(-AccPrecision<float>::normal_frac) * bnd[i];
+ bool valid = tosaCheckFloatBound(imp[i], ref[i], errBound);
+ if (!valid)
+ {
+ auto pos = indexToPosition(T, shape);
+ WARNING("[Verifier][AE] Location %s", positionToString(pos).c_str());
+ return false;
+ }
+ }
+ return true;
+}
+} // namespace
+bool verifyAbsError(const CTensor* ref, const CTensor* refBnd, const CTensor* imp)
+{
+ // Validate that tensors are provided
+ TOSA_REF_REQUIRE(ref != nullptr, "[AE] Reference tensor is missing");
+ TOSA_REF_REQUIRE(refBnd != nullptr, "[AE] Reference bounds tensor is missing");
+ TOSA_REF_REQUIRE(imp != nullptr, "[AE] Implementation tensor is missing");
+
+ const std::vector<int32_t> refShape(ref->shape, ref->shape + ref->num_dims);
+
+ const double* refData = reinterpret_cast<const double*>(ref->data);
+ const double* refBndData = reinterpret_cast<const double*>(refBnd->data);
+ TOSA_REF_REQUIRE(refData != nullptr && refBndData != nullptr, "[AE] Missing data for reference or bounds tensors");
+
+ switch (imp->data_type)
+ {
+ case tosa_datatype_fp32_t: {
+ const float* impData = reinterpret_cast<const float*>(imp->data);
+ TOSA_REF_REQUIRE(impData != nullptr, "[AE] Missing data for implementation");
+ return validateData(refData, refBndData, impData, refShape);
+ }
+ default:
+ WARNING("[Verifier][AE] Data-type not supported.");
+ break;
+ }
+
+ return false;
+}
+} // namespace TosaReference
diff --git a/reference_model/src/verify/verify_entry.cc b/reference_model/src/verify/verify_entry.cc
index 4da3bde..a37dff7 100644
--- a/reference_model/src/verify/verify_entry.cc
+++ b/reference_model/src/verify/verify_entry.cc
@@ -40,6 +40,9 @@ bool verify(const CTensor* ref, const CTensor* refBnd, const CTensor* imp, const
case VerifyMode::Ulp: {
return verifyULP(ref, imp, cfg.ulpInfo);
}
+ case VerifyMode::AbsError: {
+ return verifyAbsError(ref, refBnd, imp);
+ }
default: {
WARNING("[Verifier] Unsupported verification mode.");
break;
diff --git a/reference_model/src/verify/verify_ulp.cc b/reference_model/src/verify/verify_ulp.cc
index 2af0012..b333810 100644
--- a/reference_model/src/verify/verify_ulp.cc
+++ b/reference_model/src/verify/verify_ulp.cc
@@ -25,57 +25,18 @@ namespace TosaReference
namespace
{
-static_assert(std::numeric_limits<float>::is_iec559,
- "TOSA Reference Model has not been built with standard IEE574 32-bit float support; ULP based "
- "verifcation is invalid");
-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(double referenceValue, float testValue, double ulpNum)
+bool tosaCheckULP(float testValue, double referenceValue, double ulpNum)
{
-
- // Start by sanitizing the input.
-
- // Both must be NaNs to be correct
- if (std::isnan(referenceValue) || std::isnan(testValue))
+ double errorBound = 0.0;
+ if (std::isfinite(referenceValue) && std::abs(referenceValue) != 0.0)
{
- if (std::isnan(referenceValue) && std::isnan(testValue))
+ // Make the sign of the reference value positive
+ // and adjust the test value appropriately.
+ if (referenceValue < 0)
{
- return true;
+ referenceValue = -referenceValue;
+ testValue = -testValue;
}
- WARNING("[Verfier][ULP] Non-matching NaN values - ref (%10f) versus test (%10f).", referenceValue, testValue);
- return false;
- }
-
- // Make the sign of the reference value positive
- // and adjust the test value appropriately.
- if (referenceValue < 0)
- {
- referenceValue = -referenceValue;
- testValue = -testValue;
- }
-
- // At this point we are ready to calculate the ULP bounds for the reference value.
- double referenceMin, referenceMax;
-
- // If the reference is infinity e.g. the result of an overflow the test value must
- // be infinity of an appropriate sign.
- if (std::isinf(referenceValue))
- {
- // We already canonicalized the input such that the reference value is positive
- // so no need to check again here.
- referenceMin = std::numeric_limits<float>::infinity();
- referenceMax = std::numeric_limits<float>::infinity();
- }
- else if (referenceValue == 0)
- {
- // For zero we require that the results match exactly with the correct sign.
- referenceMin = 0;
- referenceMax = 0;
- }
- else
- {
// Find the exponent of the reference value.
int32_t referenceExponent = ilog2(referenceValue);
@@ -86,42 +47,9 @@ bool tosaCheckULP(double referenceValue, float testValue, double ulpNum)
// i.e. the ULP.
double ulpValue = referencePower2 * exp2(-AccPrecision<float>::normal_frac);
- // Scale by the number of ULPs requested by the user.
- referenceMax = referenceValue + ulpValue * ulpNum;
- referenceMin = referenceValue - ulpValue * ulpNum;
-
- // Handle the overflow cases.
- if (referenceMax > AccPrecision<float>::normal_max)
- {
- referenceMax = std::numeric_limits<float>::infinity();
- }
-
- if (referenceMin > AccPrecision<float>::normal_max)
- {
- referenceMin = std::numeric_limits<float>::infinity();
- }
-
- // And the underflow cases.
- if (referenceMax < AccPrecision<float>::normal_min)
- {
- referenceMax = AccPrecision<float>::normal_min;
- }
-
- if (referenceMin < AccPrecision<float>::normal_min)
- {
- referenceMin = 0.0;
- }
- }
-
- // And finally... Do the comparison.
- double testValue64 = static_cast<double>(testValue);
- bool withinUlp = testValue64 >= referenceMin && testValue64 <= referenceMax;
- if (!withinUlp)
- {
- WARNING("[Verfier][ULP] value (%10.10f) is not in ULP %g range (%10.10f <= ref (%10.10f) <= %10.10f).",
- testValue64, ulpNum, referenceMin, referenceValue, referenceMax);
+ errorBound = ulpValue * ulpNum;
}
- return withinUlp;
+ return tosaCheckFloatBound(testValue, referenceValue, errorBound);
}
} // namespace
@@ -148,7 +76,7 @@ bool verifyULP(const CTensor* referenceTensor, const CTensor* implementationTens
// Use mismatch to get the location of the first unequal value
auto pair = std::mismatch(refData, refDataEnd, impData, std::next(impData, elementCount),
[ulp](const auto& referenceValue, const auto& implementationValue) {
- return tosaCheckULP(referenceValue, implementationValue, ulp);
+ return tosaCheckULP(implementationValue, referenceValue, ulp);
});
if (std::get<0>(pair) == refDataEnd)
{
diff --git a/reference_model/src/verify/verify_utils.cc b/reference_model/src/verify/verify_utils.cc
index 9b20fb2..414f7d7 100644
--- a/reference_model/src/verify/verify_utils.cc
+++ b/reference_model/src/verify/verify_utils.cc
@@ -48,8 +48,9 @@ NLOHMANN_JSON_SERIALIZE_ENUM(VerifyMode,
{ VerifyMode::Exact, "EXACT" },
{ VerifyMode::Ulp, "ULP" },
{ VerifyMode::DotProduct, "DOT_PRODUCT" },
- { VerifyMode::ReduceProduct, "REDUCE_PRODUCT" },
{ VerifyMode::FpSpecial, "FP_SPECIAL" },
+ { VerifyMode::ReduceProduct, "REDUCE_PRODUCT" },
+ { VerifyMode::AbsError, "ABS_ERROR" },
})
void from_json(const nlohmann::json& j, UlpInfo& ulpInfo)
@@ -189,4 +190,97 @@ int32_t ilog2(double v)
}
return n;
}
+
+static_assert(std::numeric_limits<float>::is_iec559,
+ "TOSA Reference Model has not been built with standard IEEE 754 32-bit float support; Bounds based "
+ "verification is invalid");
+static_assert(std::numeric_limits<double>::is_iec559,
+ "TOSA Reference Model has not been built with standard IEEE 754 64-bit float support; Bounds based "
+ "verification is invalid");
+
+bool tosaCheckFloatBound(float testValue, double referenceValue, double errorBound)
+{
+ // Both must be NaNs to be correct
+ if (std::isnan(referenceValue) || std::isnan(testValue))
+ {
+ if (std::isnan(referenceValue) && std::isnan(testValue))
+ {
+ return true;
+ }
+ WARNING("[Verifier][Bound] Non-matching NaN values - ref (%10f) versus test (%10f).", referenceValue,
+ testValue);
+ return false;
+ }
+
+ // Make the sign of the reference value positive
+ // and adjust the test value appropriately.
+ if (referenceValue < 0)
+ {
+ referenceValue = -referenceValue;
+ testValue = -testValue;
+ }
+ if (errorBound < 0)
+ {
+ errorBound = -errorBound;
+ }
+
+ // At this point we are ready to calculate the ULP bounds for the reference value.
+ double referenceMin, referenceMax;
+
+ // If the reference is infinity e.g. the result of an overflow the test value must
+ // be infinity of an appropriate sign.
+ if (std::isinf(referenceValue))
+ {
+ // We already canonicalized the input such that the reference value is positive
+ // so no need to check again here.
+ referenceMin = std::numeric_limits<float>::infinity();
+ referenceMax = std::numeric_limits<float>::infinity();
+ }
+ else if (referenceValue == 0)
+ {
+ // For zero we require that the results match exactly with the correct sign.
+ referenceMin = 0;
+ referenceMax = 0;
+ }
+ else
+ {
+
+ // Scale by the number of ULPs requested by the user.
+ referenceMax = referenceValue + errorBound;
+ referenceMin = referenceValue - errorBound;
+
+ // Handle the overflow cases.
+ if (referenceMax > AccPrecision<float>::normal_max)
+ {
+ referenceMax = std::numeric_limits<float>::infinity();
+ }
+
+ if (referenceMin > AccPrecision<float>::normal_max)
+ {
+ referenceMin = std::numeric_limits<float>::infinity();
+ }
+
+ // And the underflow cases.
+ if (referenceMax < AccPrecision<float>::normal_min)
+ {
+ referenceMax = AccPrecision<float>::normal_min;
+ }
+
+ if (referenceMin < AccPrecision<float>::normal_min)
+ {
+ referenceMin = 0.0;
+ }
+ }
+
+ // And finally... Do the comparison.
+ double testValue64 = static_cast<double>(testValue);
+ bool withinBound = testValue64 >= referenceMin && testValue64 <= referenceMax;
+ if (!withinBound)
+ {
+ WARNING(
+ "[Verifier][Bound] value (%10.10f) is not in error bound %g range (%10.10f <= ref (%10.10f) <= %10.10f).",
+ testValue64, errorBound, referenceMin, referenceValue, referenceMax);
+ }
+ return withinBound;
+}
} // namespace TosaReference
diff --git a/reference_model/src/verify/verify_utils.h b/reference_model/src/verify/verify_utils.h
index 24d65b0..a58950e 100644
--- a/reference_model/src/verify/verify_utils.h
+++ b/reference_model/src/verify/verify_utils.h
@@ -43,8 +43,9 @@ enum class VerifyMode
Exact,
Ulp,
DotProduct,
+ FpSpecial,
ReduceProduct,
- FpSpecial
+ AbsError
};
/// \brief ULP verification meta-data
@@ -135,6 +136,9 @@ struct AccPrecision<float>
static constexpr int32_t normal_frac = 23;
};
+/// \brief Error bounds check for ULP and ABS_ERROR modes
+bool tosaCheckFloatBound(float testValue, double referenceValue, double errorBound);
+
}; // namespace TosaReference
#endif // VERIFY_UTILS_H_