ArmNN
 20.08
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>
9 
10 #include <QuantizeHelper.hpp>
11 
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  bool isDynamic = false)
79 {
80  if (!isDynamic)
81  {
82  // Checks they are same shape.
83  for (unsigned int i = 0;
84  i < n;
85  i++)
86  {
87  if (a.shape()[i] != b.shape()[i])
88  {
89  boost::test_tools::predicate_result res(false);
90  res.message() << "Different shapes ["
91  << a.shape()[i]
92  << "!="
93  << b.shape()[i]
94  << "]";
95  return res;
96  }
97  }
98  }
99 
100  // Now compares element-wise.
101 
102  // Fun iteration over n dimensions.
103  std::array<unsigned int, n> indices;
104  for (unsigned int i = 0; i < n; i++)
105  {
106  indices[i] = 0;
107  }
108 
109  std::stringstream errorString;
110  int numFailedElements = 0;
111  constexpr int maxReportedDifferences = 3;
112 
113  while (true)
114  {
115  bool comparison;
116  // As true for uint8_t is non-zero (1-255) we must have a dedicated compare for Booleans.
117  if(compareBoolean)
118  {
119  comparison = SelectiveCompareBoolean(a(indices), b(indices));
120  }
121  else
122  {
123  comparison = SelectiveCompare(a(indices), b(indices));
124  }
125 
126  if (!comparison)
127  {
128  ++numFailedElements;
129 
130  if (numFailedElements <= maxReportedDifferences)
131  {
132  if (numFailedElements >= 2)
133  {
134  errorString << ", ";
135  }
136  errorString << "[";
137  for (unsigned int i = 0; i < n; ++i)
138  {
139  errorString << indices[i];
140  if (i != n - 1)
141  {
142  errorString << ",";
143  }
144  }
145  errorString << "]";
146 
147  errorString << " (" << +a(indices) << " != " << +b(indices) << ")";
148  }
149  }
150 
151  ++indices[n - 1];
152  for (unsigned int i=n-1; i>0; i--)
153  {
154  if (indices[i] == a.shape()[i])
155  {
156  indices[i] = 0;
157  ++indices[i - 1];
158  }
159  }
160 
161  if (indices[0] == a.shape()[0])
162  {
163  break;
164  }
165  }
166 
167  boost::test_tools::predicate_result comparisonResult(true);
168  if (numFailedElements > 0)
169  {
170  comparisonResult = false;
171  comparisonResult.message() << numFailedElements << " different values at: ";
172  if (numFailedElements > maxReportedDifferences)
173  {
174  errorString << ", ... (and " << (numFailedElements - maxReportedDifferences) << " other differences)";
175  }
176  comparisonResult.message() << errorString.str();
177  }
178 
179  return comparisonResult;
180 }
181 
182 
183 // Creates a boost::multi_array with the shape defined by the given TensorInfo.
184 template <typename T, std::size_t n>
185 boost::multi_array<T, n> MakeTensor(const armnn::TensorInfo& tensorInfo)
186 {
187  std::array<unsigned int, n> shape;
188 
189  for (unsigned int i = 0; i < n; i++)
190  {
191  shape[i] = tensorInfo.GetShape()[i];
192  }
193 
194  return boost::multi_array<T, n>(shape);
195 }
196 
197 // Creates a boost::multi_array with the shape defined by the given TensorInfo and contents defined by the given vector.
198 template <typename T, std::size_t n>
199 boost::multi_array<T, n> MakeTensor(
200  const armnn::TensorInfo& tensorInfo, const std::vector<T>& flat, bool isDynamic = false)
201 {
202  if (!isDynamic)
203  {
204  ARMNN_ASSERT_MSG(flat.size() == tensorInfo.GetNumElements(), "Wrong number of components supplied to tensor");
205  }
206 
207  std::array<unsigned int, n> shape;
208 
209  // NOTE: tensorInfo.GetNumDimensions() might be different from n
210  const unsigned int returnDimensions = static_cast<unsigned int>(n);
211  const unsigned int actualDimensions = tensorInfo.GetNumDimensions();
212 
213  const unsigned int paddedDimensions =
214  returnDimensions > actualDimensions ? returnDimensions - actualDimensions : 0u;
215 
216  for (unsigned int i = 0u; i < returnDimensions; i++)
217  {
218  if (i < paddedDimensions)
219  {
220  shape[i] = 1u;
221  }
222  else
223  {
224  shape[i] = tensorInfo.GetShape()[i - paddedDimensions];
225  }
226  }
227 
228  boost::const_multi_array_ref<T, n> arrayRef(&flat[0], shape);
229  return boost::multi_array<T, n>(arrayRef);
230 }
231 
232 template <typename T, std::size_t n>
233 boost::multi_array<T, n> MakeRandomTensor(const armnn::TensorInfo& tensorInfo,
234  unsigned int seed,
235  float min = -10.0f,
236  float max = 10.0f)
237 {
238  boost::random::mt19937 gen(seed);
239  boost::random::uniform_real_distribution<float> dist(min, max);
240 
241  std::vector<float> init(tensorInfo.GetNumElements());
242  for (unsigned int i = 0; i < init.size(); i++)
243  {
244  init[i] = dist(gen);
245  }
246 
247  const float qScale = tensorInfo.GetQuantizationScale();
248  const int32_t qOffset = tensorInfo.GetQuantizationOffset();
249 
250  return MakeTensor<T, n>(tensorInfo, armnnUtils::QuantizedVector<T>(init, qScale, qOffset));
251 }
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::NHWC false
constexpr float g_FloatCloseToZeroTolerance
#define ARMNN_ASSERT_MSG(COND, MSG)
Definition: Assert.hpp:15
int32_t GetQuantizationOffset() const
Definition: Tensor.cpp:470
float GetQuantizationScale() const
Definition: Tensor.cpp:453
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:191
unsigned int GetNumElements() const
Definition: Tensor.hpp:192