2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
7 #include <armnn/TensorFwd.hpp>
8 #include <boost/test/unit_test.hpp>
9 #include <boost/multi_array.hpp>
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>
19 #include <armnn/Tensor.hpp>
21 #include <backends/test/QuantizeHelper.hpp>
25 constexpr float g_FloatCloseToZeroTolerance = 1.0e-6f;
27 template<typename T, bool isQuantized = true>
28 struct SelectiveComparer
30 static bool Compare(T a, T b)
32 return (std::max(a, b) - std::min(a, b)) <= 1;
38 struct SelectiveComparer<T, false>
40 static bool Compare(T a, T b)
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)
45 return std::abs(a - b) <= g_FloatCloseToZeroTolerance;
48 if (std::isinf(a) && a == b)
53 if (std::isnan(a) && std::isnan(b))
58 // For unquantized floats we use a tolerance of 1%.
59 boost::math::fpc::close_at_tolerance<float> comparer(boost::math::fpc::percent_tolerance(1.0f));
60 return comparer(a, b);
65 bool SelectiveCompare(T a, T b)
67 return SelectiveComparer<T, armnn::IsQuantizedType<T>()>::Compare(a, b);
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)
76 // Checks they are same shape.
77 for (unsigned int i=0; i<n; i++)
79 if (a.shape()[i] != b.shape()[i])
81 boost::test_tools::predicate_result res(false);
82 res.message() << "Different shapes ["
91 // Now compares element-wise.
93 // Fun iteration over n dimensions.
94 std::array<unsigned int, n> indices;
95 for (unsigned int i = 0; i < n; i++)
100 std::stringstream errorString;
101 int numFailedElements = 0;
102 constexpr int maxReportedDifferences = 3;
106 bool comparison = SelectiveCompare(a(indices), b(indices));
111 if (numFailedElements <= maxReportedDifferences)
113 if (numFailedElements >= 2)
118 for (unsigned int i = 0; i < n; ++i)
120 errorString << indices[i];
128 errorString << " (" << +a(indices) << " != " << +b(indices) << ")";
133 for (unsigned int i=n-1; i>0; i--)
135 if (indices[i] == a.shape()[i])
142 if (indices[0] == a.shape()[0])
148 boost::test_tools::predicate_result comparisonResult(true);
149 if (numFailedElements > 0)
151 comparisonResult = false;
152 comparisonResult.message() << numFailedElements << " different values at: ";
153 if (numFailedElements > maxReportedDifferences)
155 errorString << ", ... (and " << (numFailedElements - maxReportedDifferences) << " other differences)";
157 comparisonResult.message() << errorString.str();
160 return comparisonResult;
164 // Creates a boost::multi_array with the shape defined by the given TensorInfo.
165 template <typename T, std::size_t n>
166 boost::multi_array<T, n> MakeTensor(const armnn::TensorInfo& tensorInfo)
168 std::array<unsigned int, n> shape;
170 for (unsigned int i = 0; i < n; i++)
172 shape[i] = tensorInfo.GetShape()[i];
175 return boost::multi_array<T, n>(shape);
178 // Creates a boost::multi_array with the shape defined by the given TensorInfo and contents defined by the given vector.
179 template <typename T, std::size_t n>
180 boost::multi_array<T, n> MakeTensor(const armnn::TensorInfo& tensorInfo, const std::vector<T>& flat)
182 BOOST_ASSERT_MSG(flat.size() == tensorInfo.GetNumElements(), "Wrong number of components supplied to tensor");
184 std::array<unsigned int, n> shape;
186 for (unsigned int i = 0; i < n; i++)
188 shape[i] = tensorInfo.GetShape()[i];
191 boost::const_multi_array_ref<T, n> arrayRef(&flat[0], shape);
192 return boost::multi_array<T, n>(arrayRef);
195 template <typename T, std::size_t n>
196 boost::multi_array<T, n> MakeRandomTensor(const armnn::TensorInfo& tensorInfo,
201 boost::random::mt19937 gen(seed);
202 boost::random::uniform_real_distribution<float> dist(min, max);
204 std::vector<float> init(tensorInfo.GetNumElements());
205 for (unsigned int i = 0; i < init.size(); i++)
209 float qScale = tensorInfo.GetQuantizationScale();
210 int32_t qOffset = tensorInfo.GetQuantizationOffset();
211 return MakeTensor<T, n>(tensorInfo, QuantizedVector<T>(qScale, qOffset, init));
215 armnn::TensorInfo GetTensorInfo(unsigned int numberOfBatches,
216 unsigned int numberOfChannels,
219 const armnn::DataLayoutIndexed& dataLayout)
221 switch (dataLayout.GetDataLayout())
223 case armnn::DataLayout::NCHW:
224 return armnn::TensorInfo({numberOfBatches, numberOfChannels, height, width}, armnn::GetDataType<T>());
225 case armnn::DataLayout::NHWC:
226 return armnn::TensorInfo({numberOfBatches, height, width, numberOfChannels}, armnn::GetDataType<T>());
228 throw armnn::InvalidArgumentException("unknown data layout ["
229 + std::to_string(static_cast<int>(dataLayout.GetDataLayout())) + "]");