blob: 6e78b960505ff9ba6f17536b956c36c6605f332b [file] [log] [blame]
Jack Frankland62737b12023-09-13 15:47:48 +01001// Copyright (c) 2023, ARM Limited.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include <cmath>
16#include <limits>
17#include <memory>
18#include <type_traits>
Jeremy Johnson2d70ac42023-11-06 17:46:02 +000019#include <utility>
Jack Frankland62737b12023-09-13 15:47:48 +010020
21#include "verifiers.h"
22
23namespace TosaReference
24{
25
26namespace
27{
Jeremy Johnson9a758382023-11-07 16:27:35 +000028bool tosaCheckULP(float testValue, double referenceValue, double ulpNum)
Jack Frankland62737b12023-09-13 15:47:48 +010029{
Jeremy Johnson9a758382023-11-07 16:27:35 +000030 double errorBound = 0.0;
31 if (std::isfinite(referenceValue) && std::abs(referenceValue) != 0.0)
Jack Frankland62737b12023-09-13 15:47:48 +010032 {
Jack Frankland62737b12023-09-13 15:47:48 +010033 // Find the exponent of the reference value.
Jeremy Johnson0bbd8bc2023-11-09 16:56:07 +000034 int32_t referenceExponent = ilog2(std::abs(referenceValue));
Jack Frankland62737b12023-09-13 15:47:48 +010035
36 // Work out the values magnitude - by raising 2 to the power of the
37 // exponent and taking the normalized minimum for denormal values
Jeremy Johnsona4d907e2023-10-26 13:53:14 +010038 const double referencePower2 = std::max(exp2(referenceExponent), AccPrecision<float>::normal_min);
Jack Frankland62737b12023-09-13 15:47:48 +010039 // Get the value of changing the last bit - by shifting the least significant bit to this magnitude
40 // i.e. the ULP.
Jeremy Johnsona4d907e2023-10-26 13:53:14 +010041 double ulpValue = referencePower2 * exp2(-AccPrecision<float>::normal_frac);
Jack Frankland62737b12023-09-13 15:47:48 +010042
Jeremy Johnson9a758382023-11-07 16:27:35 +000043 errorBound = ulpValue * ulpNum;
Jack Frankland62737b12023-09-13 15:47:48 +010044 }
Jeremy Johnson9a758382023-11-07 16:27:35 +000045 return tosaCheckFloatBound(testValue, referenceValue, errorBound);
Jack Frankland62737b12023-09-13 15:47:48 +010046}
47} // namespace
48
Jeremy Johnsona4d907e2023-10-26 13:53:14 +010049bool verifyULP(const CTensor* referenceTensor, const CTensor* implementationTensor, const UlpInfo& ulpInfo)
Jack Frankland62737b12023-09-13 15:47:48 +010050{
51 // Validate that tensors are provided
Jeremy Johnsonfc5e34e2023-10-24 14:45:12 +010052 TOSA_REF_REQUIRE(referenceTensor != nullptr, "[ULP] Reference tensor is missing");
53 TOSA_REF_REQUIRE(implementationTensor != nullptr, "[ULP] Implementation tensor is missing");
Jack Frankland62737b12023-09-13 15:47:48 +010054
55 // Get number of elements
Jeremy Johnson2d70ac42023-11-06 17:46:02 +000056 const std::vector<int32_t> refShape(referenceTensor->shape, referenceTensor->shape + referenceTensor->num_dims);
57 const auto elementCount = numElements(refShape);
Jeremy Johnsonfc5e34e2023-10-24 14:45:12 +010058 TOSA_REF_REQUIRE(elementCount > 0, "[ULP] Invalid shape for reference tensor");
Jack Frankland62737b12023-09-13 15:47:48 +010059
Jeremy Johnsona4d907e2023-10-26 13:53:14 +010060 const double ulp = ulpInfo.ulp;
Jack Frankland62737b12023-09-13 15:47:48 +010061 switch (implementationTensor->data_type)
62 {
63 case tosa_datatype_fp32_t: {
Jeremy Johnsona4d907e2023-10-26 13:53:14 +010064 const auto* refData = reinterpret_cast<const double*>(referenceTensor->data);
Jeremy Johnsonfc5e34e2023-10-24 14:45:12 +010065 TOSA_REF_REQUIRE(refData != nullptr, "[ULP] Missing data for reference");
Jack Frankland62737b12023-09-13 15:47:48 +010066 const auto* impData = reinterpret_cast<const float*>(implementationTensor->data);
Jeremy Johnsonfc5e34e2023-10-24 14:45:12 +010067 TOSA_REF_REQUIRE(impData != nullptr, "[ULP] Missing data for implementation");
Jeremy Johnson2d70ac42023-11-06 17:46:02 +000068 const auto* refDataEnd = std::next(refData, elementCount);
69 // Use mismatch to get the location of the first unequal value
70 auto pair = std::mismatch(refData, refDataEnd, impData, std::next(impData, elementCount),
71 [ulp](const auto& referenceValue, const auto& implementationValue) {
Jeremy Johnson9a758382023-11-07 16:27:35 +000072 return tosaCheckULP(implementationValue, referenceValue, ulp);
Jeremy Johnson2d70ac42023-11-06 17:46:02 +000073 });
74 if (std::get<0>(pair) == refDataEnd)
75 {
76 // No mismatch found
77 return true;
78 }
79 else
80 {
81 auto pos = indexToPosition(std::get<0>(pair) - refData, refShape);
82 WARNING("[Verfier][ULP] Location %s", positionToString(pos).c_str());
83 return false;
84 }
Jack Frankland62737b12023-09-13 15:47:48 +010085 }
86 default:
Jeremy Johnsonfc5e34e2023-10-24 14:45:12 +010087 WARNING("[Verifier][ULP] Data-type not supported.");
Jack Frankland62737b12023-09-13 15:47:48 +010088 break;
89 }
90
91 return false;
92}
93} // namespace TosaReference