blob: 3f8589353ce82218f31b283db43ac89d8dbba086 [file] [log] [blame]
telsoa014fcda012018-03-09 14:13:49 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
5#pragma once
6
David Beckac42efd2018-09-26 17:41:13 +01007#include <armnn/Tensor.hpp>
telsoa014fcda012018-03-09 14:13:49 +00008
Aron Virginas-Tar48623a02019-10-22 10:00:28 +01009#include <QuantizeHelper.hpp>
telsoa014fcda012018-03-09 14:13:49 +000010
Aron Virginas-Tar48623a02019-10-22 10:00:28 +010011#include <boost/assert.hpp>
12#include <boost/multi_array.hpp>
13#include <boost/numeric/conversion/cast.hpp>
14#include <boost/random/uniform_real_distribution.hpp>
15#include <boost/random/mersenne_twister.hpp>
16#include <boost/test/tools/floating_point_comparison.hpp>
17#include <boost/test/unit_test.hpp>
18
19#include <array>
telsoa014fcda012018-03-09 14:13:49 +000020#include <cmath>
Aron Virginas-Tar48623a02019-10-22 10:00:28 +010021#include <vector>
telsoa014fcda012018-03-09 14:13:49 +000022
surmeh013537c2c2018-05-18 16:31:43 +010023constexpr float g_FloatCloseToZeroTolerance = 1.0e-6f;
telsoa014fcda012018-03-09 14:13:49 +000024
25template<typename T, bool isQuantized = true>
26struct SelectiveComparer
27{
28 static bool Compare(T a, T b)
29 {
30 return (std::max(a, b) - std::min(a, b)) <= 1;
31 }
32
33};
34
35template<typename T>
36struct SelectiveComparer<T, false>
37{
38 static bool Compare(T a, T b)
39 {
telsoa01c577f2c2018-08-31 09:22:23 +010040 // If a or b is zero, percent_tolerance does an exact match, so compare to a small, constant tolerance instead.
telsoa014fcda012018-03-09 14:13:49 +000041 if (a == 0.0f || b == 0.0f)
42 {
43 return std::abs(a - b) <= g_FloatCloseToZeroTolerance;
44 }
Francis Murtagh8c5e3dc2018-08-30 17:18:37 +010045
46 if (std::isinf(a) && a == b)
47 {
48 return true;
49 }
50
51 if (std::isnan(a) && std::isnan(b))
52 {
53 return true;
54 }
55
telsoa014fcda012018-03-09 14:13:49 +000056 // For unquantized floats we use a tolerance of 1%.
57 boost::math::fpc::close_at_tolerance<float> comparer(boost::math::fpc::percent_tolerance(1.0f));
58 return comparer(a, b);
59 }
60};
61
62template<typename T>
63bool SelectiveCompare(T a, T b)
64{
65 return SelectiveComparer<T, armnn::IsQuantizedType<T>()>::Compare(a, b);
66};
67
kevmay012b4d88e2019-01-24 14:05:09 +000068template<typename T>
69bool SelectiveCompareBoolean(T a, T b)
70{
71 return (((a == 0) && (b == 0)) || ((a != 0) && (b != 0)));
72};
telsoa014fcda012018-03-09 14:13:49 +000073
74template <typename T, std::size_t n>
75boost::test_tools::predicate_result CompareTensors(const boost::multi_array<T, n>& a,
kevmay012b4d88e2019-01-24 14:05:09 +000076 const boost::multi_array<T, n>& b,
77 bool compareBoolean = false)
telsoa014fcda012018-03-09 14:13:49 +000078{
telsoa01c577f2c2018-08-31 09:22:23 +010079 // Checks they are same shape.
telsoa014fcda012018-03-09 14:13:49 +000080 for (unsigned int i=0; i<n; i++)
81 {
82 if (a.shape()[i] != b.shape()[i])
83 {
84 boost::test_tools::predicate_result res(false);
85 res.message() << "Different shapes ["
86 << a.shape()[i]
87 << "!="
88 << b.shape()[i]
89 << "]";
90 return res;
91 }
92 }
93
telsoa01c577f2c2018-08-31 09:22:23 +010094 // Now compares element-wise.
telsoa014fcda012018-03-09 14:13:49 +000095
telsoa01c577f2c2018-08-31 09:22:23 +010096 // Fun iteration over n dimensions.
telsoa014fcda012018-03-09 14:13:49 +000097 std::array<unsigned int, n> indices;
98 for (unsigned int i = 0; i < n; i++)
99 {
100 indices[i] = 0;
101 }
102
103 std::stringstream errorString;
104 int numFailedElements = 0;
105 constexpr int maxReportedDifferences = 3;
106
107 while (true)
108 {
kevmay012b4d88e2019-01-24 14:05:09 +0000109 bool comparison;
110 // As true for uint8_t is non-zero (1-255) we must have a dedicated compare for Booleans.
111 if(compareBoolean)
112 {
113 comparison = SelectiveCompareBoolean(a(indices), b(indices));
114 }
115 else
116 {
117 comparison = SelectiveCompare(a(indices), b(indices));
118 }
119
telsoa014fcda012018-03-09 14:13:49 +0000120 if (!comparison)
121 {
122 ++numFailedElements;
123
124 if (numFailedElements <= maxReportedDifferences)
125 {
126 if (numFailedElements >= 2)
127 {
128 errorString << ", ";
129 }
130 errorString << "[";
131 for (unsigned int i = 0; i < n; ++i)
132 {
133 errorString << indices[i];
134 if (i != n - 1)
135 {
136 errorString << ",";
137 }
138 }
139 errorString << "]";
140
141 errorString << " (" << +a(indices) << " != " << +b(indices) << ")";
142 }
143 }
144
145 ++indices[n - 1];
146 for (unsigned int i=n-1; i>0; i--)
147 {
148 if (indices[i] == a.shape()[i])
149 {
150 indices[i] = 0;
151 ++indices[i - 1];
152 }
153 }
154
155 if (indices[0] == a.shape()[0])
156 {
157 break;
158 }
159 }
160
161 boost::test_tools::predicate_result comparisonResult(true);
162 if (numFailedElements > 0)
163 {
164 comparisonResult = false;
165 comparisonResult.message() << numFailedElements << " different values at: ";
166 if (numFailedElements > maxReportedDifferences)
167 {
168 errorString << ", ... (and " << (numFailedElements - maxReportedDifferences) << " other differences)";
169 }
170 comparisonResult.message() << errorString.str();
171 }
172
173 return comparisonResult;
174}
175
176
telsoa01c577f2c2018-08-31 09:22:23 +0100177// Creates a boost::multi_array with the shape defined by the given TensorInfo.
telsoa014fcda012018-03-09 14:13:49 +0000178template <typename T, std::size_t n>
179boost::multi_array<T, n> MakeTensor(const armnn::TensorInfo& tensorInfo)
180{
181 std::array<unsigned int, n> shape;
182
183 for (unsigned int i = 0; i < n; i++)
184 {
185 shape[i] = tensorInfo.GetShape()[i];
186 }
187
188 return boost::multi_array<T, n>(shape);
189}
190
telsoa01c577f2c2018-08-31 09:22:23 +0100191// Creates a boost::multi_array with the shape defined by the given TensorInfo and contents defined by the given vector.
telsoa014fcda012018-03-09 14:13:49 +0000192template <typename T, std::size_t n>
193boost::multi_array<T, n> MakeTensor(const armnn::TensorInfo& tensorInfo, const std::vector<T>& flat)
194{
195 BOOST_ASSERT_MSG(flat.size() == tensorInfo.GetNumElements(), "Wrong number of components supplied to tensor");
196
197 std::array<unsigned int, n> shape;
198
Aron Virginas-Tare74e5542019-02-21 14:57:08 +0000199 // NOTE: tensorInfo.GetNumDimensions() might be different from n
200 const unsigned int returnDimensions = static_cast<unsigned int>(n);
201 const unsigned int actualDimensions = tensorInfo.GetNumDimensions();
202
203 const unsigned int paddedDimensions =
204 returnDimensions > actualDimensions ? returnDimensions - actualDimensions : 0u;
205
206 for (unsigned int i = 0u; i < returnDimensions; i++)
telsoa014fcda012018-03-09 14:13:49 +0000207 {
Aron Virginas-Tare74e5542019-02-21 14:57:08 +0000208 if (i < paddedDimensions)
209 {
210 shape[i] = 1u;
211 }
212 else
213 {
214 shape[i] = tensorInfo.GetShape()[i - paddedDimensions];
215 }
telsoa014fcda012018-03-09 14:13:49 +0000216 }
217
218 boost::const_multi_array_ref<T, n> arrayRef(&flat[0], shape);
219 return boost::multi_array<T, n>(arrayRef);
220}
221
222template <typename T, std::size_t n>
223boost::multi_array<T, n> MakeRandomTensor(const armnn::TensorInfo& tensorInfo,
224 unsigned int seed,
225 float min = -10.0f,
226 float max = 10.0f)
227{
228 boost::random::mt19937 gen(seed);
229 boost::random::uniform_real_distribution<float> dist(min, max);
230
231 std::vector<float> init(tensorInfo.GetNumElements());
232 for (unsigned int i = 0; i < init.size(); i++)
233 {
234 init[i] = dist(gen);
235 }
Aron Virginas-Tar48623a02019-10-22 10:00:28 +0100236
237 const float qScale = tensorInfo.GetQuantizationScale();
238 const int32_t qOffset = tensorInfo.GetQuantizationOffset();
239
240 return MakeTensor<T, n>(tensorInfo, armnnUtils::QuantizedVector<T>(init, qScale, qOffset));
telsoa014fcda012018-03-09 14:13:49 +0000241}