• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #pragma once
6 
7 #include <armnn/Tensor.hpp>
8 #include <armnn/utility/Assert.hpp>
9 #include <armnnUtils/FloatingPointComparison.hpp>
10 
11 #include <QuantizeHelper.hpp>
12 
13 #include <boost/multi_array.hpp>
14 #include <boost/random/uniform_real_distribution.hpp>
15 #include <boost/random/mersenne_twister.hpp>
16 #include <boost/test/unit_test.hpp>
17 
18 #include <array>
19 #include <cmath>
20 #include <vector>
21 
22 constexpr float g_FloatCloseToZeroTolerance = 1.0e-6f;
23 
24 template<typename T, bool isQuantized = true>
25 struct SelectiveComparer
26 {
CompareSelectiveComparer27     static bool Compare(T a, T b)
28     {
29         return (std::max(a, b) - std::min(a, b)) <= 1;
30     }
31 
32 };
33 
34 template<typename T>
35 struct SelectiveComparer<T, false>
36 {
CompareSelectiveComparer37     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 
50         if (std::isnan(a) && std::isnan(b))
51         {
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 
60 template<typename T>
SelectiveCompare(T a,T b)61 bool SelectiveCompare(T a, T b)
62 {
63     return SelectiveComparer<T, armnn::IsQuantizedType<T>()>::Compare(a, b);
64 };
65 
66 template<typename T>
SelectiveCompareBoolean(T a,T b)67 bool SelectiveCompareBoolean(T a, T b)
68 {
69     return (((a == 0) && (b == 0)) || ((a != 0) && (b != 0)));
70 };
71 
72 template <typename T, std::size_t n>
CompareTensors(const boost::multi_array<T,n> & a,const boost::multi_array<T,n> & b,bool compareBoolean=false,bool isDynamic=false)73 boost::test_tools::predicate_result CompareTensors(const boost::multi_array<T, n>& a,
74                                                    const boost::multi_array<T, n>& b,
75                                                    bool compareBoolean = false,
76                                                    bool isDynamic = false)
77 {
78     if (!isDynamic)
79     {
80         // Checks they are same shape.
81         for (unsigned int i = 0;
82              i < n;
83              i++)
84         {
85             if (a.shape()[i] != b.shape()[i])
86             {
87                 boost::test_tools::predicate_result res(false);
88                 res.message() << "Different shapes ["
89                               << a.shape()[i]
90                               << "!="
91                               << b.shape()[i]
92                               << "]";
93                 return res;
94             }
95         }
96     }
97 
98     // Now compares element-wise.
99 
100     // Fun iteration over n dimensions.
101     std::array<unsigned int, n> indices;
102     for (unsigned int i = 0; i < n; i++)
103     {
104         indices[i] = 0;
105     }
106 
107     std::stringstream errorString;
108     int numFailedElements = 0;
109     constexpr int maxReportedDifferences = 3;
110 
111     while (true)
112     {
113         bool comparison;
114         // As true for uint8_t is non-zero (1-255) we must have a dedicated compare for Booleans.
115         if(compareBoolean)
116         {
117             comparison = SelectiveCompareBoolean(a(indices), b(indices));
118         }
119         else
120         {
121             comparison = SelectiveCompare(a(indices), b(indices));
122         }
123 
124         if (!comparison)
125         {
126             ++numFailedElements;
127 
128             if (numFailedElements <= maxReportedDifferences)
129             {
130                 if (numFailedElements >= 2)
131                 {
132                     errorString << ", ";
133                 }
134                 errorString << "[";
135                 for (unsigned int i = 0; i < n; ++i)
136                 {
137                     errorString << indices[i];
138                     if (i != n - 1)
139                     {
140                         errorString << ",";
141                     }
142                 }
143                 errorString << "]";
144 
145                 errorString << " (" << +a(indices) << " != " << +b(indices) << ")";
146             }
147         }
148 
149         ++indices[n - 1];
150         for (unsigned int i=n-1; i>0; i--)
151         {
152             if (indices[i] == a.shape()[i])
153             {
154                 indices[i] = 0;
155                 ++indices[i - 1];
156             }
157         }
158 
159         if (indices[0] == a.shape()[0])
160         {
161             break;
162         }
163     }
164 
165     boost::test_tools::predicate_result comparisonResult(true);
166     if (numFailedElements > 0)
167     {
168         comparisonResult = false;
169         comparisonResult.message() << numFailedElements << " different values at: ";
170         if (numFailedElements > maxReportedDifferences)
171         {
172             errorString << ", ... (and " << (numFailedElements - maxReportedDifferences) << " other differences)";
173         }
174         comparisonResult.message() << errorString.str();
175     }
176 
177     return comparisonResult;
178 }
179 
180 
181 // Creates a boost::multi_array with the shape defined by the given TensorInfo.
182 template <typename T, std::size_t n>
MakeTensor(const armnn::TensorInfo & tensorInfo)183 boost::multi_array<T, n> MakeTensor(const armnn::TensorInfo& tensorInfo)
184 {
185     std::array<unsigned int, n> shape;
186 
187     for (unsigned int i = 0; i < n; i++)
188     {
189         shape[i] = tensorInfo.GetShape()[i];
190     }
191 
192     return boost::multi_array<T, n>(shape);
193 }
194 
195 // Creates a boost::multi_array with the shape defined by the given TensorInfo and contents defined by the given vector.
196 template <typename T, std::size_t n>
MakeTensor(const armnn::TensorInfo & tensorInfo,const std::vector<T> & flat,bool isDynamic=false)197 boost::multi_array<T, n> MakeTensor(
198     const armnn::TensorInfo& tensorInfo, const std::vector<T>& flat, bool isDynamic = false)
199 {
200     if (!isDynamic)
201     {
202         ARMNN_ASSERT_MSG(flat.size() == tensorInfo.GetNumElements(), "Wrong number of components supplied to tensor");
203     }
204 
205     std::array<unsigned int, n> shape;
206 
207     // NOTE: tensorInfo.GetNumDimensions() might be different from n
208     const unsigned int returnDimensions = static_cast<unsigned int>(n);
209     const unsigned int actualDimensions = tensorInfo.GetNumDimensions();
210 
211     const unsigned int paddedDimensions =
212         returnDimensions > actualDimensions ? returnDimensions - actualDimensions : 0u;
213 
214     for (unsigned int i = 0u; i < returnDimensions; i++)
215     {
216         if (i < paddedDimensions)
217         {
218             shape[i] = 1u;
219         }
220         else
221         {
222             shape[i] = tensorInfo.GetShape()[i - paddedDimensions];
223         }
224     }
225 
226     boost::const_multi_array_ref<T, n> arrayRef(&flat[0], shape);
227     return boost::multi_array<T, n>(arrayRef);
228 }
229 
230 template <typename T, std::size_t n>
MakeRandomTensor(const armnn::TensorInfo & tensorInfo,unsigned int seed,float min=-10.0f,float max=10.0f)231 boost::multi_array<T, n> MakeRandomTensor(const armnn::TensorInfo& tensorInfo,
232                                           unsigned int seed,
233                                           float        min = -10.0f,
234                                           float        max = 10.0f)
235 {
236     boost::random::mt19937                          gen(seed);
237     boost::random::uniform_real_distribution<float> dist(min, max);
238 
239     std::vector<float> init(tensorInfo.GetNumElements());
240     for (unsigned int i = 0; i < init.size(); i++)
241     {
242         init[i] = dist(gen);
243     }
244 
245     const float   qScale  = tensorInfo.GetQuantizationScale();
246     const int32_t qOffset = tensorInfo.GetQuantizationOffset();
247 
248     return MakeTensor<T, n>(tensorInfo, armnnUtils::QuantizedVector<T>(init, qScale, qOffset));
249 }
250