blob: 14c50613341699eed70f052d200456587aa0fc47 [file] [log] [blame]
Sadik Armagana097d2a2021-11-24 15:47:28 +00001//
Tianle Cheng988354d2023-06-28 13:20:47 +01002// Copyright © 2017, 2021-2023 Arm Ltd and Contributors. All rights reserved.
Sadik Armagana097d2a2021-11-24 15:47:28 +00003// SPDX-License-Identifier: MIT
4//
5#pragma once
6
7#include <armnnTestUtils/PredicateResult.hpp>
8
9#include <armnn/Tensor.hpp>
10#include <armnn/utility/Assert.hpp>
11#include <armnnUtils/FloatingPointComparison.hpp>
12
Colm Donelanc42a9872022-02-02 16:35:09 +000013#include <armnnUtils/QuantizeHelper.hpp>
Sadik Armagana097d2a2021-11-24 15:47:28 +000014
15#include <doctest/doctest.h>
16
17#include <array>
18#include <cmath>
19#include <random>
20#include <vector>
21
22constexpr float g_FloatCloseToZeroTolerance = 1.0e-6f;
23
24template<typename T, bool isQuantized = true>
25struct SelectiveComparer
26{
27 static bool Compare(T a, T b)
28 {
29 return (std::max(a, b) - std::min(a, b)) <= 1;
30 }
31
32};
33
34template<typename T>
35struct SelectiveComparer<T, false>
36{
37 static bool Compare(T a, T b)
38 {
39 // If a or b is zero, percent_tolerance does an exact match, so compare to a small, constant tolerance instead.
40 if (a == 0.0f || b == 0.0f)
41 {
42 return std::abs(a - b) <= g_FloatCloseToZeroTolerance;
43 }
44
45 if (std::isinf(a) && a == b)
46 {
47 return true;
48 }
49
Mike Kellya9c32672023-12-04 17:23:09 +000050 if (std::isnan(static_cast<float>(a)) && std::isnan(static_cast<float>(b)))
Sadik Armagana097d2a2021-11-24 15:47:28 +000051 {
52 return true;
53 }
54
55 // For unquantized floats we use a tolerance of 1%.
56 return armnnUtils::within_percentage_tolerance(a, b);
57 }
58};
59
60template<typename T>
61bool SelectiveCompare(T a, T b)
62{
63 return SelectiveComparer<T, armnn::IsQuantizedType<T>()>::Compare(a, b);
64};
65
66template<typename T>
67bool SelectiveCompareBoolean(T a, T b)
68{
69 return (((a == 0) && (b == 0)) || ((a != 0) && (b != 0)));
70};
71
72template <typename T>
73armnn::PredicateResult CompareTensors(const std::vector<T>& actualData,
74 const std::vector<T>& expectedData,
75 const armnn::TensorShape& actualShape,
76 const armnn::TensorShape& expectedShape,
77 bool compareBoolean = false,
78 bool isDynamic = false)
79{
80 if (actualData.size() != expectedData.size())
81 {
82 armnn::PredicateResult res(false);
83 res.Message() << "Different data size ["
84 << actualData.size()
85 << "!="
86 << expectedData.size()
87 << "]";
88 return res;
89 }
90
Tianle Cheng988354d2023-06-28 13:20:47 +010091 // Support for comparison between empty tensors
92 if (actualData.size() == 0 && expectedData.size() == 0)
93 {
94 armnn::PredicateResult comparisonResult(true);
95 return comparisonResult;
96 }
97
Sadik Armagana097d2a2021-11-24 15:47:28 +000098 if (actualShape.GetNumDimensions() != expectedShape.GetNumDimensions())
99 {
100 armnn::PredicateResult res(false);
101 res.Message() << "Different number of dimensions ["
102 << actualShape.GetNumDimensions()
103 << "!="
104 << expectedShape.GetNumDimensions()
105 << "]";
106 return res;
107 }
108
109 if (actualShape.GetNumElements() != expectedShape.GetNumElements())
110 {
111 armnn::PredicateResult res(false);
112 res.Message() << "Different number of elements ["
113 << actualShape.GetNumElements()
114 << "!="
115 << expectedShape.GetNumElements()
116 << "]";
117 return res;
118 }
119
120 unsigned int numberOfDimensions = actualShape.GetNumDimensions();
121
122 if (!isDynamic)
123 {
124 // Checks they are same shape.
125 for (unsigned int i = 0; i < numberOfDimensions; ++i)
126 {
127 if (actualShape[i] != expectedShape[i])
128 {
129 armnn::PredicateResult res(false);
130 res.Message() << "Different shapes ["
131 << actualShape[i]
132 << "!="
133 << expectedShape[i]
134 << "]";
135 return res;
136 }
137 }
138 }
139
140 // Fun iteration over n dimensions.
141 std::vector<unsigned int> indices;
142 for (unsigned int i = 0; i < numberOfDimensions; i++)
143 {
144 indices.emplace_back(0);
145 }
146
147 std::stringstream errorString;
148 int numFailedElements = 0;
149 constexpr int maxReportedDifferences = 3;
150 unsigned int index = 0;
151
152 // Compare data element by element.
153 while (true)
154 {
155 bool comparison;
156 // As true for uint8_t is non-zero (1-255) we must have a dedicated compare for Booleans.
157 if(compareBoolean)
158 {
159 comparison = SelectiveCompareBoolean(actualData[index], expectedData[index]);
160 }
161 else
162 {
163 comparison = SelectiveCompare(actualData[index], expectedData[index]);
164 }
165
166 if (!comparison)
167 {
168 ++numFailedElements;
169
170 if (numFailedElements <= maxReportedDifferences)
171 {
172 if (numFailedElements >= 2)
173 {
174 errorString << ", ";
175 }
176 errorString << "[";
177 for (unsigned int i = 0; i < numberOfDimensions; ++i)
178 {
179 errorString << indices[i];
180 if (i != numberOfDimensions - 1)
181 {
182 errorString << ",";
183 }
184 }
185 errorString << "]";
186
187 errorString << " (" << +actualData[index] << " != " << +expectedData[index] << ")";
188 }
189 }
190
191 ++indices[numberOfDimensions - 1];
192 for (unsigned int i=numberOfDimensions-1; i>0; i--)
193 {
194 if (indices[i] == actualShape[i])
195 {
196 indices[i] = 0;
197 ++indices[i - 1];
198 }
199 }
200 if (indices[0] == actualShape[0])
201 {
202 break;
203 }
204
205 index++;
206 }
207
208 armnn::PredicateResult comparisonResult(true);
209 if (numFailedElements > 0)
210 {
211 comparisonResult.SetResult(false);
212 comparisonResult.Message() << numFailedElements << " different values at: ";
213 if (numFailedElements > maxReportedDifferences)
214 {
215 errorString << ", ... (and " << (numFailedElements - maxReportedDifferences) << " other differences)";
216 }
217 comparisonResult.Message() << errorString.str();
218 }
219
220 return comparisonResult;
221}
222
223template <typename T>
224std::vector<T> MakeRandomTensor(const armnn::TensorInfo& tensorInfo,
225 unsigned int seed,
226 float min = -10.0f,
227 float max = 10.0f)
228{
229 std::mt19937 gen(seed);
230 std::uniform_real_distribution<float> dist(min, max);
231
232 std::vector<float> init(tensorInfo.GetNumElements());
233 for (unsigned int i = 0; i < init.size(); i++)
234 {
235 init[i] = dist(gen);
236 }
237
238 const float qScale = tensorInfo.GetQuantizationScale();
239 const int32_t qOffset = tensorInfo.GetQuantizationOffset();
240
241 return armnnUtils::QuantizedVector<T>(init, qScale, qOffset);
242}