ArmNN
 20.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>
8 
9 #include <QuantizeHelper.hpp>
10 
11 #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>
20 #include <cmath>
21 #include <vector>
22 
23 constexpr float g_FloatCloseToZeroTolerance = 1.0e-6f;
24 
25 template<typename T, bool isQuantized = true>
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 
35 template<typename T>
37 {
38  static bool Compare(T a, T b)
39  {
40  // If a or b is zero, percent_tolerance does an exact match, so compare to a small, constant tolerance instead.
41  if (a == 0.0f || b == 0.0f)
42  {
43  return std::abs(a - b) <= g_FloatCloseToZeroTolerance;
44  }
45 
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 
56  // 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 
62 template<typename T>
63 bool SelectiveCompare(T a, T b)
64 {
66 };
67 
68 template<typename T>
70 {
71  return (((a == 0) && (b == 0)) || ((a != 0) && (b != 0)));
72 };
73 
74 template <typename T, std::size_t n>
75 boost::test_tools::predicate_result CompareTensors(const boost::multi_array<T, n>& a,
76  const boost::multi_array<T, n>& b,
77  bool compareBoolean = false)
78 {
79  // Checks they are same shape.
80  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 
94  // Now compares element-wise.
95 
96  // Fun iteration over n dimensions.
97  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  {
109  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 
120  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 
177 // Creates a boost::multi_array with the shape defined by the given TensorInfo.
178 template <typename T, std::size_t n>
179 boost::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 
191 // Creates a boost::multi_array with the shape defined by the given TensorInfo and contents defined by the given vector.
192 template <typename T, std::size_t n>
193 boost::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 
199  // 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++)
207  {
208  if (i < paddedDimensions)
209  {
210  shape[i] = 1u;
211  }
212  else
213  {
214  shape[i] = tensorInfo.GetShape()[i - paddedDimensions];
215  }
216  }
217 
218  boost::const_multi_array_ref<T, n> arrayRef(&flat[0], shape);
219  return boost::multi_array<T, n>(arrayRef);
220 }
221 
222 template <typename T, std::size_t n>
223 boost::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  }
236 
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));
241 }
static bool Compare(T a, T b)
const TensorShape & GetShape() const
Definition: Tensor.hpp:88
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)
DataLayout::NHWC false
constexpr float g_FloatCloseToZeroTolerance
int32_t GetQuantizationOffset() const
Definition: Tensor.cpp:264
float GetQuantizationScale() const
Definition: Tensor.cpp:247
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)
static bool Compare(T a, T b)
unsigned int GetNumDimensions() const
Definition: Tensor.hpp:92
unsigned int GetNumElements() const
Definition: Tensor.hpp:93