IVGCVSW-2102: Fix Pooling2D CpuRef indexing bug
[platform/upstream/armnn.git] / src / armnn / test / TensorHelpers.hpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #pragma once
6
7 #include <armnn/TensorFwd.hpp>
8 #include <boost/test/unit_test.hpp>
9 #include <boost/multi_array.hpp>
10 #include <vector>
11 #include <array>
12
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>
18
19 #include <armnn/Tensor.hpp>
20
21 #include <backends/test/QuantizeHelper.hpp>
22
23 #include <cmath>
24
25 constexpr float g_FloatCloseToZeroTolerance = 1.0e-6f;
26
27 template<typename T, bool isQuantized = true>
28 struct SelectiveComparer
29 {
30     static bool Compare(T a, T b)
31     {
32         return (std::max(a, b) - std::min(a, b)) <= 1;
33     }
34
35 };
36
37 template<typename T>
38 struct SelectiveComparer<T, false>
39 {
40     static bool Compare(T a, T b)
41     {
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)
44         {
45             return std::abs(a - b) <= g_FloatCloseToZeroTolerance;
46         }
47
48         if (std::isinf(a) && a == b)
49         {
50             return true;
51         }
52
53         if (std::isnan(a) && std::isnan(b))
54         {
55             return true;
56         }
57
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);
61     }
62 };
63
64 template<typename T>
65 bool SelectiveCompare(T a, T b)
66 {
67     return SelectiveComparer<T, armnn::IsQuantizedType<T>()>::Compare(a, b);
68 };
69
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 {
76     // Checks they are same shape.
77     for (unsigned int i=0; i<n; i++)
78     {
79         if (a.shape()[i] != b.shape()[i])
80         {
81             boost::test_tools::predicate_result res(false);
82             res.message() << "Different shapes ["
83                         << a.shape()[i]
84                         << "!="
85                         << b.shape()[i]
86                         << "]";
87             return res;
88         }
89     }
90
91     // Now compares element-wise.
92
93     // Fun iteration over n dimensions.
94     std::array<unsigned int, n> indices;
95     for (unsigned int i = 0; i < n; i++)
96     {
97         indices[i] = 0;
98     }
99
100     std::stringstream errorString;
101     int numFailedElements = 0;
102     constexpr int maxReportedDifferences = 3;
103
104     while (true)
105     {
106         bool comparison = SelectiveCompare(a(indices), b(indices));
107         if (!comparison)
108         {
109             ++numFailedElements;
110
111             if (numFailedElements <= maxReportedDifferences)
112             {
113                 if (numFailedElements >= 2)
114                 {
115                     errorString << ", ";
116                 }
117                 errorString << "[";
118                 for (unsigned int i = 0; i < n; ++i)
119                 {
120                     errorString << indices[i];
121                     if (i != n - 1)
122                     {
123                         errorString << ",";
124                     }
125                 }
126                 errorString << "]";
127
128                 errorString << " (" << +a(indices) << " != " << +b(indices) << ")";
129             }
130         }
131
132         ++indices[n - 1];
133         for (unsigned int i=n-1; i>0; i--)
134         {
135             if (indices[i] == a.shape()[i])
136             {
137                 indices[i] = 0;
138                 ++indices[i - 1];
139             }
140         }
141
142         if (indices[0] == a.shape()[0])
143         {
144             break;
145         }
146     }
147
148     boost::test_tools::predicate_result comparisonResult(true);
149     if (numFailedElements > 0)
150     {
151         comparisonResult = false;
152         comparisonResult.message() << numFailedElements << " different values at: ";
153         if (numFailedElements > maxReportedDifferences)
154         {
155             errorString << ", ... (and " << (numFailedElements - maxReportedDifferences) << " other differences)";
156         }
157         comparisonResult.message() << errorString.str();
158     }
159
160     return comparisonResult;
161 }
162
163
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)
167 {
168     std::array<unsigned int, n> shape;
169
170     for (unsigned int i = 0; i < n; i++)
171     {
172         shape[i] = tensorInfo.GetShape()[i];
173     }
174
175     return boost::multi_array<T, n>(shape);
176 }
177
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)
181 {
182     BOOST_ASSERT_MSG(flat.size() == tensorInfo.GetNumElements(), "Wrong number of components supplied to tensor");
183
184     std::array<unsigned int, n> shape;
185
186     for (unsigned int i = 0; i < n; i++)
187     {
188         shape[i] = tensorInfo.GetShape()[i];
189     }
190
191     boost::const_multi_array_ref<T, n> arrayRef(&flat[0], shape);
192     return boost::multi_array<T, n>(arrayRef);
193 }
194
195 template <typename T, std::size_t n>
196 boost::multi_array<T, n> MakeRandomTensor(const armnn::TensorInfo& tensorInfo,
197                                           unsigned int seed,
198                                           float        min = -10.0f,
199                                           float        max = 10.0f)
200 {
201     boost::random::mt19937                          gen(seed);
202     boost::random::uniform_real_distribution<float> dist(min, max);
203
204     std::vector<float> init(tensorInfo.GetNumElements());
205     for (unsigned int i = 0; i < init.size(); i++)
206     {
207         init[i] = dist(gen);
208     }
209     float qScale = tensorInfo.GetQuantizationScale();
210     int32_t qOffset = tensorInfo.GetQuantizationOffset();
211     return MakeTensor<T, n>(tensorInfo, QuantizedVector<T>(qScale, qOffset, init));
212 }
213
214 template<typename T>
215 armnn::TensorInfo GetTensorInfo(unsigned int numberOfBatches,
216                                 unsigned int numberOfChannels,
217                                 unsigned int height,
218                                 unsigned int width,
219                                 const armnn::DataLayoutIndexed& dataLayout)
220 {
221     switch (dataLayout.GetDataLayout())
222     {
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>());
227         default:
228             throw armnn::InvalidArgumentException("unknown data layout ["
229                                                   + std::to_string(static_cast<int>(dataLayout.GetDataLayout())) + "]");
230     }
231 }