Release 18.08
[platform/upstream/armnn.git] / src / armnn / test / TensorHelpers.hpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // See LICENSE file in the project root for full license information.
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         // For unquantized floats we use a tolerance of 1%.
48         boost::math::fpc::close_at_tolerance<float> comparer(boost::math::fpc::percent_tolerance(1.0f));
49         return comparer(a, b);
50     }
51 };
52
53 template<typename T>
54 bool SelectiveCompare(T a, T b)
55 {
56     return SelectiveComparer<T, armnn::IsQuantizedType<T>()>::Compare(a, b);
57 };
58
59
60
61 template <typename T, std::size_t n>
62 boost::test_tools::predicate_result CompareTensors(const boost::multi_array<T, n>& a,
63                                                    const boost::multi_array<T, n>& b)
64 {
65     // Checks they are same shape.
66     for (unsigned int i=0; i<n; i++)
67     {
68         if (a.shape()[i] != b.shape()[i])
69         {
70             boost::test_tools::predicate_result res(false);
71             res.message() << "Different shapes ["
72                         << a.shape()[i]
73                         << "!="
74                         << b.shape()[i]
75                         << "]";
76             return res;
77         }
78     }
79
80     // Now compares element-wise.
81
82     // Fun iteration over n dimensions.
83     std::array<unsigned int, n> indices;
84     for (unsigned int i = 0; i < n; i++)
85     {
86         indices[i] = 0;
87     }
88
89     std::stringstream errorString;
90     int numFailedElements = 0;
91     constexpr int maxReportedDifferences = 3;
92
93     while (true)
94     {
95         bool comparison = SelectiveCompare(a(indices), b(indices));
96         if (!comparison)
97         {
98             ++numFailedElements;
99
100             if (numFailedElements <= maxReportedDifferences)
101             {
102                 if (numFailedElements >= 2)
103                 {
104                     errorString << ", ";
105                 }
106                 errorString << "[";
107                 for (unsigned int i = 0; i < n; ++i)
108                 {
109                     errorString << indices[i];
110                     if (i != n - 1)
111                     {
112                         errorString << ",";
113                     }
114                 }
115                 errorString << "]";
116
117                 errorString << " (" << +a(indices) << " != " << +b(indices) << ")";
118             }
119         }
120
121         ++indices[n - 1];
122         for (unsigned int i=n-1; i>0; i--)
123         {
124             if (indices[i] == a.shape()[i])
125             {
126                 indices[i] = 0;
127                 ++indices[i - 1];
128             }
129         }
130
131         if (indices[0] == a.shape()[0])
132         {
133             break;
134         }
135     }
136
137     boost::test_tools::predicate_result comparisonResult(true);
138     if (numFailedElements > 0)
139     {
140         comparisonResult = false;
141         comparisonResult.message() << numFailedElements << " different values at: ";
142         if (numFailedElements > maxReportedDifferences)
143         {
144             errorString << ", ... (and " << (numFailedElements - maxReportedDifferences) << " other differences)";
145         }
146         comparisonResult.message() << errorString.str();
147     }
148
149     return comparisonResult;
150 }
151
152
153 // Creates a boost::multi_array with the shape defined by the given TensorInfo.
154 template <typename T, std::size_t n>
155 boost::multi_array<T, n> MakeTensor(const armnn::TensorInfo& tensorInfo)
156 {
157     std::array<unsigned int, n> shape;
158
159     for (unsigned int i = 0; i < n; i++)
160     {
161         shape[i] = tensorInfo.GetShape()[i];
162     }
163
164     return boost::multi_array<T, n>(shape);
165 }
166
167 // Creates a boost::multi_array with the shape defined by the given TensorInfo and contents defined by the given vector.
168 template <typename T, std::size_t n>
169 boost::multi_array<T, n> MakeTensor(const armnn::TensorInfo& tensorInfo, const std::vector<T>& flat)
170 {
171     BOOST_ASSERT_MSG(flat.size() == tensorInfo.GetNumElements(), "Wrong number of components supplied to tensor");
172
173     std::array<unsigned int, n> shape;
174
175     for (unsigned int i = 0; i < n; i++)
176     {
177         shape[i] = tensorInfo.GetShape()[i];
178     }
179
180     boost::const_multi_array_ref<T, n> arrayRef(&flat[0], shape);
181     return boost::multi_array<T, n>(arrayRef);
182 }
183
184 template <typename T, std::size_t n>
185 boost::multi_array<T, n> MakeRandomTensor(const armnn::TensorInfo& tensorInfo,
186                                           unsigned int seed,
187                                           float        min = -10.0f,
188                                           float        max = 10.0f)
189 {
190     boost::random::mt19937                          gen(seed);
191     boost::random::uniform_real_distribution<float> dist(min, max);
192
193     std::vector<float> init(tensorInfo.GetNumElements());
194     for (unsigned int i = 0; i < init.size(); i++)
195     {
196         init[i] = dist(gen);
197     }
198     float qScale = tensorInfo.GetQuantizationScale();
199     int32_t qOffset = tensorInfo.GetQuantizationOffset();
200     return MakeTensor<T, n>(tensorInfo, QuantizedVector<T>(qScale, qOffset, init));
201 }