ArmNN
 21.02
TensorHelpers.hpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #pragma once
6 
7 #include <armnn/Tensor.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>
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 
34 template<typename T>
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 
50  if (std::isnan(a) && std::isnan(b))
51  {
52  return true;
53  }
54 
55  // For unquantized floats we use a tolerance of 1%.
57  }
58 };
59 
60 template<typename T>
61 bool SelectiveCompare(T a, T b)
62 {
64 };
65 
66 template<typename T>
68 {
69  return (((a == 0) && (b == 0)) || ((a != 0) && (b != 0)));
70 };
71 
72 template <typename T, std::size_t n>
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>
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>
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>
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 }
static bool Compare(T a, T b)
const TensorShape & GetShape() const
Definition: Tensor.hpp:187
boost::multi_array< T, n > MakeTensor(const armnn::TensorInfo &tensorInfo)
bool SelectiveCompareBoolean(T a, T b)
boost::test_tools::predicate_result CompareTensors(const boost::multi_array< T, n > &a, const boost::multi_array< T, n > &b, bool compareBoolean=false, bool isDynamic=false)
DataLayout::NCHW false
constexpr float g_FloatCloseToZeroTolerance
#define ARMNN_ASSERT_MSG(COND, MSG)
Definition: Assert.hpp:15
int32_t GetQuantizationOffset() const
Definition: Tensor.cpp:469
float GetQuantizationScale() const
Definition: Tensor.cpp:452
bool SelectiveCompare(T a, T b)
boost::multi_array< T, n > MakeRandomTensor(const armnn::TensorInfo &tensorInfo, unsigned int seed, float min=-10.0f, float max=10.0f)
bool within_percentage_tolerance(float a, float b, float tolerancePercent=1.0f)
Compare two floats and return true if their values are within a specified tolerance of each other...
static bool Compare(T a, T b)
unsigned int GetNumDimensions() const
Definition: Tensor.hpp:191
unsigned int GetNumElements() const
Definition: Tensor.hpp:192