blob: e4ff899a4eb7947426b2ff56afde32478fa11f88 [file] [log] [blame]
telsoa014fcda012018-03-09 14:13:49 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// See LICENSE file in the project root for full license information.
4//
5#pragma once
6
7#include <armnn/TensorFwd.hpp>
8#include <boost/test/unit_test.hpp>
9#include <boost/multi_array.hpp>
10#include <vector>
11#include <array>
12
13#include <boost/assert.hpp>
14#include <boost/test/tools/floating_point_comparison.hpp>
15#include <boost/random/uniform_real_distribution.hpp>
16#include <boost/random/mersenne_twister.hpp>
17#include <boost/numeric/conversion/cast.hpp>
18
19#include "armnn/Tensor.hpp"
20
21#include "backends/test/QuantizeHelper.hpp"
22
23#include <cmath>
24
25constexpr float g_FloatCloseToZeroTolerance = 1.0e-7f;
26
27template<typename T, bool isQuantized = true>
28struct SelectiveComparer
29{
30 static bool Compare(T a, T b)
31 {
32 return (std::max(a, b) - std::min(a, b)) <= 1;
33 }
34
35};
36
37template<typename T>
38struct SelectiveComparer<T, false>
39{
40 static bool Compare(T a, T b)
41 {
42 // if a or b is zero, percent_tolerance does an exact match, so compare to a small, constant tolerance instead
43 if (a == 0.0f || b == 0.0f)
44 {
45 return std::abs(a - b) <= g_FloatCloseToZeroTolerance;
46 }
47 // For unquantized floats we use a tolerance of 1%.
48 boost::math::fpc::close_at_tolerance<float> comparer(boost::math::fpc::percent_tolerance(1.0f));
49 return comparer(a, b);
50 }
51};
52
53template<typename T>
54bool SelectiveCompare(T a, T b)
55{
56 return SelectiveComparer<T, armnn::IsQuantizedType<T>()>::Compare(a, b);
57};
58
59
60
61template <typename T, std::size_t n>
62boost::test_tools::predicate_result CompareTensors(const boost::multi_array<T, n>& a,
63 const boost::multi_array<T, n>& b)
64{
65 // check they are same shape
66 for (unsigned int i=0; i<n; i++)
67 {
68 if (a.shape()[i] != b.shape()[i])
69 {
70 boost::test_tools::predicate_result res(false);
71 res.message() << "Different shapes ["
72 << a.shape()[i]
73 << "!="
74 << b.shape()[i]
75 << "]";
76 return res;
77 }
78 }
79
80 // now compare element-wise
81
82 // fun iteration over n dimensions
83 std::array<unsigned int, n> indices;
84 for (unsigned int i = 0; i < n; i++)
85 {
86 indices[i] = 0;
87 }
88
89 std::stringstream errorString;
90 int numFailedElements = 0;
91 constexpr int maxReportedDifferences = 3;
92
93 while (true)
94 {
95 bool comparison = SelectiveCompare(a(indices), b(indices));
96 if (!comparison)
97 {
98 ++numFailedElements;
99
100 if (numFailedElements <= maxReportedDifferences)
101 {
102 if (numFailedElements >= 2)
103 {
104 errorString << ", ";
105 }
106 errorString << "[";
107 for (unsigned int i = 0; i < n; ++i)
108 {
109 errorString << indices[i];
110 if (i != n - 1)
111 {
112 errorString << ",";
113 }
114 }
115 errorString << "]";
116
117 errorString << " (" << +a(indices) << " != " << +b(indices) << ")";
118 }
119 }
120
121 ++indices[n - 1];
122 for (unsigned int i=n-1; i>0; i--)
123 {
124 if (indices[i] == a.shape()[i])
125 {
126 indices[i] = 0;
127 ++indices[i - 1];
128 }
129 }
130
131 if (indices[0] == a.shape()[0])
132 {
133 break;
134 }
135 }
136
137 boost::test_tools::predicate_result comparisonResult(true);
138 if (numFailedElements > 0)
139 {
140 comparisonResult = false;
141 comparisonResult.message() << numFailedElements << " different values at: ";
142 if (numFailedElements > maxReportedDifferences)
143 {
144 errorString << ", ... (and " << (numFailedElements - maxReportedDifferences) << " other differences)";
145 }
146 comparisonResult.message() << errorString.str();
147 }
148
149 return comparisonResult;
150}
151
152
153// Creates a boost::multi_array with shape defined by the given TensorInfo.
154template <typename T, std::size_t n>
155boost::multi_array<T, n> MakeTensor(const armnn::TensorInfo& tensorInfo)
156{
157 std::array<unsigned int, n> shape;
158
159 for (unsigned int i = 0; i < n; i++)
160 {
161 shape[i] = tensorInfo.GetShape()[i];
162 }
163
164 return boost::multi_array<T, n>(shape);
165}
166
167// Creates a boost::multi_array with shape defined by the given TensorInfo and contents defined by the given vector.
168template <typename T, std::size_t n>
169boost::multi_array<T, n> MakeTensor(const armnn::TensorInfo& tensorInfo, const std::vector<T>& flat)
170{
171 BOOST_ASSERT_MSG(flat.size() == tensorInfo.GetNumElements(), "Wrong number of components supplied to tensor");
172
173 std::array<unsigned int, n> shape;
174
175 for (unsigned int i = 0; i < n; i++)
176 {
177 shape[i] = tensorInfo.GetShape()[i];
178 }
179
180 boost::const_multi_array_ref<T, n> arrayRef(&flat[0], shape);
181 return boost::multi_array<T, n>(arrayRef);
182}
183
184template <typename T, std::size_t n>
185boost::multi_array<T, n> MakeRandomTensor(const armnn::TensorInfo& tensorInfo,
186 unsigned int seed,
187 float min = -10.0f,
188 float max = 10.0f)
189{
190 boost::random::mt19937 gen(seed);
191 boost::random::uniform_real_distribution<float> dist(min, max);
192
193 std::vector<float> init(tensorInfo.GetNumElements());
194 for (unsigned int i = 0; i < init.size(); i++)
195 {
196 init[i] = dist(gen);
197 }
198 float qScale = tensorInfo.GetQuantizationScale();
199 int32_t qOffset = tensorInfo.GetQuantizationOffset();
200 return MakeTensor<T, n>(tensorInfo, QuantizedVector<T>(qScale, qOffset, init));
201}