blob: 9aa6ba211480c143ae6861ea70a60dab02085e9e [file] [log] [blame]
Georgios Pinitas7021ef02023-08-22 08:25:57 +01001
2// Copyright (c) 2023, ARM Limited.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#include "verify_utils.h"
17
18#include <nlohmann/json.hpp>
19
20#include <algorithm>
21#include <map>
22
23namespace tosa
24{
25
26NLOHMANN_JSON_SERIALIZE_ENUM(DType,
27 {
Jeremy Johnsonfc5e34e2023-10-24 14:45:12 +010028 { DType::DType_UNKNOWN, "UNKNOWN" },
Georgios Pinitas7021ef02023-08-22 08:25:57 +010029 { DType::DType_BOOL, "BOOL" },
30 { DType::DType_INT4, "INT4" },
31 { DType::DType_INT8, "INT8" },
32 { DType::DType_INT16, "INT16" },
33 { DType::DType_INT32, "INT32" },
34 { DType::DType_INT48, "INT48" },
35 { DType::DType_FP16, "FP16" },
36 { DType::DType_BF16, "BF16" },
37 { DType::DType_FP32, "FP32" },
38 })
39
40} // namespace tosa
41
42namespace TosaReference
43{
44
45NLOHMANN_JSON_SERIALIZE_ENUM(VerifyMode,
46 {
Jeremy Johnsonfc5e34e2023-10-24 14:45:12 +010047 { VerifyMode::Unknown, "UNKNOWN" },
Georgios Pinitas7021ef02023-08-22 08:25:57 +010048 { VerifyMode::Exact, "EXACT" },
49 { VerifyMode::Ulp, "ULP" },
50 { VerifyMode::DotProduct, "DOT_PRODUCT" },
Georgios Pinitas7021ef02023-08-22 08:25:57 +010051 { VerifyMode::FpSpecial, "FP_SPECIAL" },
Jeremy Johnson9a758382023-11-07 16:27:35 +000052 { VerifyMode::ReduceProduct, "REDUCE_PRODUCT" },
53 { VerifyMode::AbsError, "ABS_ERROR" },
Georgios Pinitas7021ef02023-08-22 08:25:57 +010054 })
55
56void from_json(const nlohmann::json& j, UlpInfo& ulpInfo)
57{
58 j.at("ulp").get_to(ulpInfo.ulp);
59}
60
61void from_json(const nlohmann::json& j, DotProductVerifyInfo& dotProductInfo)
62{
Georgios Pinitas7021ef02023-08-22 08:25:57 +010063 j.at("s").get_to(dotProductInfo.s);
64 j.at("ks").get_to(dotProductInfo.ks);
65}
66
Jack Frankland12ee1a72023-09-20 09:08:34 +010067void from_json(const nlohmann::json& j, ReduceProductVerifyInfo& reduceProduceInfo)
68{
69 j.at("m").get_to(reduceProduceInfo.m);
70 j.at("n").get_to(reduceProduceInfo.n);
71}
72
Georgios Pinitas7021ef02023-08-22 08:25:57 +010073void from_json(const nlohmann::json& j, VerifyConfig& cfg)
74{
75 j.at("mode").get_to(cfg.mode);
Jeremy Johnsonbb0935f2023-09-14 16:43:48 +010076 j.at("data_type").get_to(cfg.dataType);
Georgios Pinitas7021ef02023-08-22 08:25:57 +010077 if (j.contains("ulp_info"))
78 {
79 j.at("ulp_info").get_to(cfg.ulpInfo);
80 }
81 if (j.contains("dot_product_info"))
82 {
83 j.at("dot_product_info").get_to(cfg.dotProductInfo);
84 }
Jack Frankland12ee1a72023-09-20 09:08:34 +010085 if (j.contains("reduce_product_info"))
86 {
87 j.at("reduce_product_info").get_to(cfg.reduceProductInfo);
88 }
Georgios Pinitas7021ef02023-08-22 08:25:57 +010089}
90
91std::optional<VerifyConfig> parseVerifyConfig(const char* tensorName, const char* json)
92{
93 if (!tensorName)
94 return std::nullopt;
95
96 auto jsonCfg = nlohmann::json::parse(json, nullptr, /* allow exceptions */ false);
97
98 if (jsonCfg.is_discarded())
Jeremy Johnsonfc5e34e2023-10-24 14:45:12 +010099 {
100 WARNING("[Verifier] Invalid json config.");
Georgios Pinitas7021ef02023-08-22 08:25:57 +0100101 return std::nullopt;
Jeremy Johnsonfc5e34e2023-10-24 14:45:12 +0100102 }
Georgios Pinitas7021ef02023-08-22 08:25:57 +0100103 if (!jsonCfg.contains("tensors"))
Jeremy Johnsonfc5e34e2023-10-24 14:45:12 +0100104 {
105 WARNING("[Verifier] Missing tensors in json config.");
Georgios Pinitas7021ef02023-08-22 08:25:57 +0100106 return std::nullopt;
Jeremy Johnsonfc5e34e2023-10-24 14:45:12 +0100107 }
Georgios Pinitas7021ef02023-08-22 08:25:57 +0100108
109 const auto& tensors = jsonCfg["tensors"];
110 if (!tensors.contains(tensorName))
Jeremy Johnsonfc5e34e2023-10-24 14:45:12 +0100111 if (!tensors.contains(tensorName))
112 {
113 WARNING("[Verifier] Missing tensor %s in json config.", tensorName);
114 return std::nullopt;
115 }
Georgios Pinitas7021ef02023-08-22 08:25:57 +0100116 const auto& namedTensor = tensors[tensorName];
117 return namedTensor.get<VerifyConfig>();
118}
119
120int64_t numElements(const std::vector<int32_t>& shape)
121{
122 return std::accumulate(std::begin(shape), std::end(shape), 1, std::multiplies<int64_t>());
123}
124
Jeremy Johnson2d70ac42023-11-06 17:46:02 +0000125std::vector<int32_t> indexToPosition(int64_t index, const std::vector<int32_t>& shape)
126{
127 std::vector<int32_t> pos;
128 for (auto d = shape.end() - 1; d >= shape.begin(); --d)
129 {
130 pos.insert(pos.begin(), index % *d);
131 index /= *d;
132 }
133 return pos;
134}
135
136std::string positionToString(const std::vector<int32_t>& pos)
137{
138 std::string str = "[";
139 for (auto d = pos.begin(); d < pos.end(); ++d)
140 {
141 str.append(std::to_string(*d));
142 if (pos.end() - d > 1)
143 {
144 str.append(",");
145 }
146 }
147 str.append("]");
148 return str;
149}
150
Georgios Pinitas7021ef02023-08-22 08:25:57 +0100151DType mapToDType(tosa_datatype_t dataType)
152{
153 static std::map<tosa_datatype_t, DType> typeMap = {
154 { tosa_datatype_bool_t, DType_BOOL }, { tosa_datatype_int4_t, DType_INT4 },
155 { tosa_datatype_int8_t, DType_INT8 }, { tosa_datatype_uint16_t, DType_UINT16 },
156 { tosa_datatype_int16_t, DType_INT16 }, { tosa_datatype_int32_t, DType_INT32 },
157 { tosa_datatype_int48_t, DType_INT48 }, { tosa_datatype_fp16_t, DType_FP16 },
158 { tosa_datatype_bf16_t, DType_BF16 }, { tosa_datatype_fp32_t, DType_FP32 },
159 { tosa_datatype_shape_t, DType_SHAPE },
160 };
161
162 if (typeMap.count(dataType))
163 {
164 return typeMap[dataType];
165 }
166
167 return DType_UNKNOWN;
168}
Jeremy Johnsond1a08ce2023-10-18 17:22:21 +0100169
170// Like const_exp2 but for use during runtime
171double exp2(int32_t n)
172{
Jeremy Johnson0bbd8bc2023-11-09 16:56:07 +0000173 if (n < -1075)
174 {
175 return 0.0; // smaller than smallest denormal
176 }
177 TOSA_REF_REQUIRE(n <= 1023, " Invalid exponent value (%d) in exp2", n);
Jeremy Johnsond1a08ce2023-10-18 17:22:21 +0100178 return const_exp2(n);
179}
Jeremy Johnsona4d907e2023-10-26 13:53:14 +0100180
181int32_t ilog2(double v)
182{
183 TOSA_REF_REQUIRE(0.0 < v && v < std::numeric_limits<double>::infinity(), " Value out of range (%g) in ilog2", v);
184 int32_t n = 0;
185 while (v >= 2.0)
186 {
187 v = v / 2.0;
188 n++;
189 }
190 while (v < 1.0)
191 {
192 v = v * 2.0;
193 n--;
194 }
195 return n;
196}
Jeremy Johnson9a758382023-11-07 16:27:35 +0000197
198static_assert(std::numeric_limits<float>::is_iec559,
199 "TOSA Reference Model has not been built with standard IEEE 754 32-bit float support; Bounds based "
200 "verification is invalid");
201static_assert(std::numeric_limits<double>::is_iec559,
202 "TOSA Reference Model has not been built with standard IEEE 754 64-bit float support; Bounds based "
203 "verification is invalid");
204
205bool tosaCheckFloatBound(float testValue, double referenceValue, double errorBound)
206{
207 // Both must be NaNs to be correct
208 if (std::isnan(referenceValue) || std::isnan(testValue))
209 {
210 if (std::isnan(referenceValue) && std::isnan(testValue))
211 {
212 return true;
213 }
214 WARNING("[Verifier][Bound] Non-matching NaN values - ref (%10f) versus test (%10f).", referenceValue,
215 testValue);
216 return false;
217 }
218
Jeremy Johnson0bbd8bc2023-11-09 16:56:07 +0000219 // Check the errorBound
220 TOSA_REF_REQUIRE(errorBound >= 0.f, " Invalid error bound (%g)", errorBound);
221
Jeremy Johnson9a758382023-11-07 16:27:35 +0000222 // Make the sign of the reference value positive
223 // and adjust the test value appropriately.
224 if (referenceValue < 0)
225 {
226 referenceValue = -referenceValue;
227 testValue = -testValue;
228 }
Jeremy Johnson9a758382023-11-07 16:27:35 +0000229
230 // At this point we are ready to calculate the ULP bounds for the reference value.
231 double referenceMin, referenceMax;
232
233 // If the reference is infinity e.g. the result of an overflow the test value must
234 // be infinity of an appropriate sign.
235 if (std::isinf(referenceValue))
236 {
237 // We already canonicalized the input such that the reference value is positive
238 // so no need to check again here.
239 referenceMin = std::numeric_limits<float>::infinity();
240 referenceMax = std::numeric_limits<float>::infinity();
241 }
242 else if (referenceValue == 0)
243 {
244 // For zero we require that the results match exactly with the correct sign.
245 referenceMin = 0;
246 referenceMax = 0;
247 }
248 else
249 {
250
251 // Scale by the number of ULPs requested by the user.
252 referenceMax = referenceValue + errorBound;
253 referenceMin = referenceValue - errorBound;
254
255 // Handle the overflow cases.
256 if (referenceMax > AccPrecision<float>::normal_max)
257 {
258 referenceMax = std::numeric_limits<float>::infinity();
259 }
260
261 if (referenceMin > AccPrecision<float>::normal_max)
262 {
263 referenceMin = std::numeric_limits<float>::infinity();
264 }
265
266 // And the underflow cases.
267 if (referenceMax < AccPrecision<float>::normal_min)
268 {
269 referenceMax = AccPrecision<float>::normal_min;
270 }
271
272 if (referenceMin < AccPrecision<float>::normal_min)
273 {
274 referenceMin = 0.0;
275 }
276 }
277
278 // And finally... Do the comparison.
279 double testValue64 = static_cast<double>(testValue);
280 bool withinBound = testValue64 >= referenceMin && testValue64 <= referenceMax;
281 if (!withinBound)
282 {
283 WARNING(
284 "[Verifier][Bound] value (%10.10f) is not in error bound %g range (%10.10f <= ref (%10.10f) <= %10.10f).",
285 testValue64, errorBound, referenceMin, referenceValue, referenceMax);
286 }
287 return withinBound;
288}
Georgios Pinitas7021ef02023-08-22 08:25:57 +0100289} // namespace TosaReference