aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Johnson <jeremy.johnson@arm.com>2023-11-07 16:27:35 +0000
committerEric Kunze <eric.kunze@arm.com>2023-11-16 21:24:23 +0000
commit9a758384d1066ade713311940f3d15c860f90866 (patch)
treec25c9624b7c1d589d0648d6b3b4e6333f87a7712
parent2d70ac4c02808609feb357488dcd080bd6fc5ba5 (diff)
downloadreference_model-9a758384d1066ade713311940f3d15c860f90866.tar.gz
Main Compliance testing support for EXP & POW
Added new ABS_ERROR mode to verify lib and ref model. Signed-off-by: Jeremy Johnson <jeremy.johnson@arm.com> Change-Id: Ifb78290675833d3df7df91a4d6cef336b02b64a4
-rw-r--r--reference_model/CMakeLists.txt2
-rw-r--r--reference_model/src/generate/generate_utils.cc2
-rw-r--r--reference_model/src/main.cpp22
-rw-r--r--reference_model/src/ops/ewise_binary.cc13
-rw-r--r--reference_model/src/ops/ewise_unary.cc10
-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
-rw-r--r--reference_model/test/verify_tests.cpp61
-rw-r--r--verif/conformance/tosa_main_profile_ops_info.json14
-rw-r--r--verif/generator/tosa_test_gen.py22
-rw-r--r--verif/generator/tosa_utils.py1
-rw-r--r--verif/runner/tosa_test_runner.py10
16 files changed, 321 insertions, 118 deletions
diff --git a/reference_model/CMakeLists.txt b/reference_model/CMakeLists.txt
index 24467c8..d36167a 100644
--- a/reference_model/CMakeLists.txt
+++ b/reference_model/CMakeLists.txt
@@ -76,6 +76,7 @@ set(CXX_SOURCE
src/generate/generate_pseudo_random.cc
src/generate/generate_entry.cc
src/generate/generate_utils.cc
+ src/verify/verify_abs_error.cc
src/verify/verify_dot_product.cc
src/verify/verify_entry.cc
src/verify/verify_exact.cc
@@ -148,6 +149,7 @@ set_target_properties(tosa_reference_model_lib PROPERTIES PUBLIC_HEADER "${PUBLI
# Build TOSA verification library
add_library(tosa_reference_verify_lib SHARED
+ src/verify/verify_abs_error.cc
src/verify/verify_dot_product.cc
src/verify/verify_entry.cc
src/verify/verify_exact.cc
diff --git a/reference_model/src/generate/generate_utils.cc b/reference_model/src/generate/generate_utils.cc
index fd42f21..3b00ba1 100644
--- a/reference_model/src/generate/generate_utils.cc
+++ b/reference_model/src/generate/generate_utils.cc
@@ -43,6 +43,7 @@ NLOHMANN_JSON_SERIALIZE_ENUM(Op,
{ Op::Op_ARGMAX, "ARGMAX" },
{ Op::Op_CEIL, "CEIL" },
{ Op::Op_CONV2D, "CONV2D" },
+ { Op::Op_EXP, "EXP" },
{ Op::Op_FLOOR, "FLOOR" },
{ Op::Op_FULLY_CONNECTED, "FULLY_CONNECTED" },
{ Op::Op_IDENTITY, "IDENTITY" },
@@ -53,6 +54,7 @@ NLOHMANN_JSON_SERIALIZE_ENUM(Op,
{ Op::Op_MUL, "MUL" },
{ Op::Op_NEGATE, "NEGATE" },
{ Op::Op_PAD, "PAD" },
+ { Op::Op_POW, "POW" },
{ Op::Op_RECIPROCAL, "RECIPROCAL" },
{ Op::Op_RSQRT, "RSQRT" },
{ Op::Op_REDUCE_MAX, "REDUCE_MAX" },
diff --git a/reference_model/src/main.cpp b/reference_model/src/main.cpp
index 6970bb1..80125ee 100644
--- a/reference_model/src/main.cpp
+++ b/reference_model/src/main.cpp
@@ -41,7 +41,7 @@ int writeVariableTensors(SubgraphTraverser& gt, json test_desc);
int loadGraph(TosaSerializationHandler& tsh, json& test_desc);
void parse_value(const std::string& text, tosa_level_t& value);
const std::string getResultFilenamePrefix();
-bool isComplianceModeDotProduct(json& test_desc);
+bool isComplianceAbsModeNeeded(json& test_desc);
int main(int argc, char** argv)
{
@@ -90,10 +90,10 @@ int main(int argc, char** argv)
GraphStatus status = GraphStatus::TOSA_VALID;
- if (isComplianceModeDotProduct(test_desc) && !g_func_config.precise_mode)
+ if (isComplianceAbsModeNeeded(test_desc) && !g_func_config.precise_mode)
{
- // Warn about precise mode for dot product compliance
- DEBUG_INFO(CONFIG, "DOT_PRODUCT compliance: NOTE - enable precise mode for compliance results")
+ // Warn about precise mode for dot product or abs error compliance
+ DEBUG_INFO(CONFIG, "DOT_PRODUCT/ABS_ERROR compliance: NOTE - enable precise mode for compliance results")
}
// max of 2 runs, second run only happens when precise_mode is set, to do an abs_mode run
@@ -220,10 +220,10 @@ int main(int argc, char** argv)
}
if (run == 0 && status == GraphStatus::TOSA_VALID && g_func_config.precise_mode && g_func_config.eval &&
- isComplianceModeDotProduct(test_desc))
+ isComplianceAbsModeNeeded(test_desc))
{
- // first run result is valid and precise mode and eval is true: turn on abs_mode for second run
- DEBUG_INFO(CONFIG, "DOT_PRODUCT compliance: Evaluating the graph again to produce bounds results")
+ // when first run result is valid and precise mode and eval is true: turn on abs_mode for second run
+ DEBUG_INFO(CONFIG, "DOT_PRODUCT/ABS_ERROR compliance: Evaluating the graph again to produce bounds results")
g_func_config.abs_mode = true;
continue;
}
@@ -361,17 +361,17 @@ const std::string getResultFilenamePrefix()
return g_func_config.abs_mode ? "bounds_" : "";
}
-// returns true iff test_desc contains a "meta" object containing a "compliance"
+// returns true if test_desc contains a "meta" object containing a "compliance"
// object which contains "tensors" and one of those has a "mode" whose value is
-// "DOT_PRODUCT"
-bool isComplianceModeDotProduct(json& test_desc)
+// "DOT_PRODUCT" or "ABS_ERROR"
+bool isComplianceAbsModeNeeded(json& test_desc)
{
if (test_desc.contains("meta") && test_desc["meta"].contains("compliance") &&
test_desc["meta"]["compliance"].contains("tensors"))
{
for (auto t : test_desc["meta"]["compliance"]["tensors"])
{
- if (t.contains("mode") && t["mode"] == "DOT_PRODUCT")
+ if (t.contains("mode") && (t["mode"] == "DOT_PRODUCT" || t["mode"] == "ABS_ERROR"))
{
return true;
}
diff --git a/reference_model/src/ops/ewise_binary.cc b/reference_model/src/ops/ewise_binary.cc
index be621cb..fab221d 100644
--- a/reference_model/src/ops/ewise_binary.cc
+++ b/reference_model/src/ops/ewise_binary.cc
@@ -514,7 +514,18 @@ int OpPow<Rank, Dtype>::register_fcn()
this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return fpTrunc<OutDtype>(powf(a, b)); };
break;
case TOSA_REF_TYPE_FP64:
- this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return pow(a, b); };
+ if (g_func_config.abs_mode)
+ {
+ // ABS_ERROR bounds return (1+abs(log(abs(a))*b))
+ this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType {
+ OutEigenType c = log(a > (InEigenType)0 ? a : (-a)) * b;
+ return 1.0 + (c > (OutEigenType)0 ? c : (-c));
+ };
+ }
+ else
+ {
+ this->fcn = [](InEigenType a, InEigenType b) -> OutEigenType { return pow(a, b); };
+ }
break;
default:
ERROR_IF(true, "unsupported TOSA_REF_TYPE %s", EnumNameTOSAREFTYPE(Dtype));
diff --git a/reference_model/src/ops/ewise_unary.cc b/reference_model/src/ops/ewise_unary.cc
index 05c1f4b..d92cde1 100644
--- a/reference_model/src/ops/ewise_unary.cc
+++ b/reference_model/src/ops/ewise_unary.cc
@@ -172,7 +172,15 @@ int OpExp<Rank, Dtype>::register_fcn()
this->fcn = [](InEigenType a) -> OutEigenType { return fpTrunc<Dtype>(expf(a)); };
break;
case TOSA_REF_TYPE_FP64:
- this->fcn = [](InEigenType a) -> OutEigenType { return exp(a); };
+ if (g_func_config.abs_mode)
+ {
+ // ABS_ERROR bounds return (1+abs(a))
+ this->fcn = [](InEigenType a) -> OutEigenType { return 1.0 + (a > (InEigenType)0 ? a : (-a)); };
+ }
+ else
+ {
+ this->fcn = [](InEigenType a) -> OutEigenType { return exp(a); };
+ }
break;
default:
ERROR_IF(true, "unsupported TOSA_REF_TYPE %s", EnumNameTOSAREFTYPE(Dtype));
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_
diff --git a/reference_model/test/verify_tests.cpp b/reference_model/test/verify_tests.cpp
index 31e27c0..f92792a 100644
--- a/reference_model/test/verify_tests.cpp
+++ b/reference_model/test/verify_tests.cpp
@@ -428,4 +428,65 @@ TEST_CASE("positive - ulp")
}
}
+TEST_CASE("positive - abs error")
+{
+ std::string jsonCfg = R"({
+ "tensors" : {
+ "out1" : {
+ "mode": "ABS_ERROR",
+ "data_type": "FP32"
+ }
+ }
+ })";
+
+ const auto shape = std::vector<int32_t>{ 4, 4, 4 };
+ const auto elementCount = std::accumulate(std::begin(shape), std::end(shape), 1, std::multiplies<>());
+
+ // Generate some random floats using the full range of fp32.
+ auto data_fp32 = generateRandomTensorData<float>(elementCount, true);
+ std::vector<double> data_fp64(data_fp32.begin(), data_fp32.end());
+
+ // Set up simple bounds of the input to 2.0
+ std::vector<double> bounds_fp64(elementCount);
+ std::for_each(std::begin(bounds_fp64), std::end(bounds_fp64), [](auto& value) { value = 2.0; });
+ constexpr float insideErrBound = 1.0e-7 * 2; // v.approx exp2(-23) * bounds[]
+ constexpr float outsideErrBound = 1.0e-7 * 3;
+
+ SUBCASE("inside")
+ {
+ // Generate some data that meets the ABS_ERROR requirements of the result.
+ auto otherData_fp32 = data_fp32;
+ std::for_each(std::begin(otherData_fp32), std::end(otherData_fp32), [insideErrBound](auto& value) {
+ if (std::abs(value) != 0.0 && !std::isinf(value) && !std::isnan(value))
+ value += value * insideErrBound;
+ });
+ const auto referenceTensor =
+ TosaTensor("out1", tosa_datatype_fp64_t, shape, reinterpret_cast<uint8_t*>(data_fp64.data()));
+ const auto boundsTensor =
+ TosaTensor("out1", tosa_datatype_fp64_t, shape, reinterpret_cast<uint8_t*>(bounds_fp64.data()));
+ const auto implementationTensor =
+ TosaTensor("out1", tosa_datatype_fp32_t, shape, reinterpret_cast<uint8_t*>(otherData_fp32.data()));
+ REQUIRE(tvf_verify_data(referenceTensor.cTensor(), boundsTensor.cTensor(), implementationTensor.cTensor(),
+ jsonCfg.c_str()));
+ }
+
+ SUBCASE("outside")
+ {
+ // Generate some data that exceeds a specified number of ULP for each value in the tensor.
+ auto otherData_fp32 = data_fp32;
+ std::for_each(std::begin(otherData_fp32), std::end(otherData_fp32), [outsideErrBound](auto& value) {
+ if (std::abs(value) != 0.0 && !std::isinf(value) && !std::isnan(value))
+ value += value * outsideErrBound;
+ });
+
+ const auto referenceTensor =
+ TosaTensor("out1", tosa_datatype_fp64_t, shape, reinterpret_cast<uint8_t*>(data_fp64.data()));
+ const auto boundsTensor =
+ TosaTensor("out1", tosa_datatype_fp64_t, shape, reinterpret_cast<uint8_t*>(bounds_fp64.data()));
+ const auto implementationTensor =
+ TosaTensor("out1", tosa_datatype_fp32_t, shape, reinterpret_cast<uint8_t*>(otherData_fp32.data()));
+ REQUIRE_FALSE(tvf_verify_data(referenceTensor.cTensor(), boundsTensor.cTensor(), implementationTensor.cTensor(),
+ jsonCfg.c_str()));
+ }
+}
TEST_SUITE_END(); // verify
diff --git a/verif/conformance/tosa_main_profile_ops_info.json b/verif/conformance/tosa_main_profile_ops_info.json
index 62505fd..6538c81 100644
--- a/verif/conformance/tosa_main_profile_ops_info.json
+++ b/verif/conformance/tosa_main_profile_ops_info.json
@@ -898,6 +898,7 @@
"profile": [
"tosa-mi"
],
+ "support_for": [ "lazy_data_gen" ],
"generation": {
"standard": {
"generator_args": [
@@ -909,7 +910,7 @@
"--target-dtype",
"bf16",
"--fp-values-range",
- "-2.0,2.0",
+ "-max,max",
"--tensor-dim-range",
"15,64",
"--target-rank",
@@ -923,7 +924,7 @@
"--target-dtype",
"fp16",
"--fp-values-range",
- "-2.0,2.0",
+ "-max,max",
"--tensor-dim-range",
"1,15",
"--target-rank",
@@ -935,7 +936,7 @@
"--target-dtype",
"fp32",
"--fp-values-range",
- "-2.0,2.0",
+ "-max,max",
"--target-shape",
"1,1,65535,4",
"--target-shape",
@@ -1705,6 +1706,7 @@
"profile": [
"tosa-mi"
],
+ "support_for": [ "lazy_data_gen" ],
"generation": {
"standard": {
"generator_args": [
@@ -1716,7 +1718,7 @@
"--target-dtype",
"bf16",
"--fp-values-range",
- "-2.0,2.0",
+ "-max,max",
"--tensor-dim-range",
"16,64",
"--target-rank",
@@ -1730,7 +1732,7 @@
"--target-dtype",
"fp16",
"--fp-values-range",
- "-2.0,2.0",
+ "-max,max",
"--tensor-dim-range",
"1,16",
"--target-rank",
@@ -1742,7 +1744,7 @@
"--target-dtype",
"bf16",
"--fp-values-range",
- "-2.0,2.0",
+ "-max,max",
"--target-shape",
"65534,3,1,1",
"--target-shape",
diff --git a/verif/generator/tosa_test_gen.py b/verif/generator/tosa_test_gen.py
index 35cd78f..3e5aee8 100644
--- a/verif/generator/tosa_test_gen.py
+++ b/verif/generator/tosa_test_gen.py
@@ -333,6 +333,8 @@ class TosaTestGen:
compliance_tens["ulp_info"] = {"ulp": op["compliance"]["ulp"]}
elif op["op"] == Op.REDUCE_PRODUCT:
mode = gtu.ComplianceMode.REDUCE_PRODUCT
+ elif op["op"] in (Op.EXP, Op.POW):
+ mode = gtu.ComplianceMode.ABS_ERROR
else:
mode = gtu.ComplianceMode.EXACT
compliance_tens["mode"] = gtu.ComplianceMode(mode).name
@@ -400,8 +402,8 @@ class TosaTestGen:
self.ser.addOperator(op["op"], input_list, output_list, attr)
- if op["op"] in (Op.EXP, Op.LOG):
- # TODO - add compliance support LOG and EXP
+ if op["op"] in (Op.LOG,):
+ # TODO - add compliance support LOG
compliance = None
else:
compliance = self.tensorComplianceMetaData(
@@ -445,13 +447,9 @@ class TosaTestGen:
self.ser.addOperator(op["op"], input_list, output_list)
- if op["op"] == Op.POW:
- # TODO - add compliance support
- compliance = None
- else:
- compliance = self.tensorComplianceMetaData(
- op, a.dtype, args_dict, result_tensor, error_name
- )
+ compliance = self.tensorComplianceMetaData(
+ op, a.dtype, args_dict, result_tensor, error_name
+ )
return TosaTestGen.BuildInfo(result_tensor, compliance)
@@ -3576,6 +3574,9 @@ class TosaTestGen:
TosaErrorValidator.evDimensionMismatch,
TosaErrorValidator.evBroadcastShapesMismatch,
),
+ "data_gen": {
+ "fp": (gtu.DataGenType.PSEUDO_RANDOM,),
+ },
},
"sub": {
"op": Op.SUB,
@@ -3713,6 +3714,9 @@ class TosaTestGen:
TosaErrorValidator.evWrongInputList,
TosaErrorValidator.evWrongOutputList,
),
+ "data_gen": {
+ "fp": (gtu.DataGenType.PSEUDO_RANDOM,),
+ },
},
"floor": {
"op": Op.FLOOR,
diff --git a/verif/generator/tosa_utils.py b/verif/generator/tosa_utils.py
index 3b487de..318f296 100644
--- a/verif/generator/tosa_utils.py
+++ b/verif/generator/tosa_utils.py
@@ -38,6 +38,7 @@ class ComplianceMode(IntEnum):
ULP = 2
FP_SPECIAL = 3
REDUCE_PRODUCT = 4
+ ABS_ERROR = 5
class DataGenType(IntEnum):
diff --git a/verif/runner/tosa_test_runner.py b/verif/runner/tosa_test_runner.py
index 984b2d9..876fbdd 100644
--- a/verif/runner/tosa_test_runner.py
+++ b/verif/runner/tosa_test_runner.py
@@ -16,15 +16,15 @@ from json2numpy import json2numpy
from runner.tosa_test_presets import TOSA_REFCOMPLIANCE_RUNNER
-def isComplianceModeDotProduct(testDesc):
- """Checks the test descriptor for DOT_PRODUCT compliance mode."""
+def isComplianceAbsModeNeeded(testDesc):
+ """Checks the test descriptor for DOT_PRODUCT/ABS_ERROR compliance mode."""
if (
"meta" in testDesc
and "compliance" in testDesc["meta"]
and "tensors" in testDesc["meta"]["compliance"]
):
for _, t in testDesc["meta"]["compliance"]["tensors"].items():
- if "mode" in t and t["mode"] == "DOT_PRODUCT":
+ if "mode" in t and t["mode"] in ("DOT_PRODUCT", "ABS_ERROR"):
return True
return False
@@ -195,7 +195,7 @@ class TosaTestRunner:
conformanceFilePath = getRunnerResultFilePath(
resultFilePath, TOSA_REFCOMPLIANCE_RUNNER
)
- if isComplianceModeDotProduct(self.testDesc):
+ if isComplianceAbsModeNeeded(self.testDesc):
conformanceBoundsPath = getBoundsResultFilePath(
resultFilePath, TOSA_REFCOMPLIANCE_RUNNER
)
@@ -255,7 +255,7 @@ class TosaTestRunner:
getRunnerResultFilePath(resultFilePath, sutModule)
)
if (
- isComplianceModeDotProduct(self.testDesc)
+ isComplianceAbsModeNeeded(self.testDesc)
and sutModule == TOSA_REFCOMPLIANCE_RUNNER
):
boundsFilePath = getBoundsResultFilePath(resultFilePath)