blob: 89f91c0dd96768984d3373f2d5a5478eaacc1b6b [file] [log] [blame]
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001/*
2 * Copyright (c) 2017 ARM Limited.
3 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24#include "Validation.h"
25
26#include "IAccessor.h"
27#include "RawTensor.h"
28#include "TypePrinter.h"
29#include "Utils.h"
30
31#include "arm_compute/core/Coordinates.h"
32#include "arm_compute/core/Error.h"
33#include "arm_compute/core/FixedPoint.h"
34#include "arm_compute/core/TensorShape.h"
35#include "arm_compute/runtime/Tensor.h"
36
37#include <array>
38#include <cmath>
39#include <cstddef>
40#include <cstdint>
41#include <iomanip>
42
43namespace arm_compute
44{
45namespace test
46{
47namespace validation
48{
49namespace
50{
51/** Get the data from *ptr after casting according to @p data_type and then convert the data to double.
52 *
53 * @param[in] ptr Pointer to value.
54 * @param[in] data_type Data type of both values.
55 *
56 * @return The data from the ptr after converted to double.
57 */
58double get_double_data(const void *ptr, DataType data_type)
59{
steniu019746fd82017-06-15 10:49:37 +010060 if(ptr == nullptr)
61 {
62 ARM_COMPUTE_ERROR("Can't dereference a null pointer!");
63 }
64
Anthony Barbier6ff3b192017-09-04 18:44:23 +010065 switch(data_type)
66 {
67 case DataType::U8:
68 return *reinterpret_cast<const uint8_t *>(ptr);
69 case DataType::S8:
70 return *reinterpret_cast<const int8_t *>(ptr);
71 case DataType::QS8:
72 return *reinterpret_cast<const qint8_t *>(ptr);
73 case DataType::U16:
74 return *reinterpret_cast<const uint16_t *>(ptr);
75 case DataType::S16:
76 return *reinterpret_cast<const int16_t *>(ptr);
77 case DataType::U32:
78 return *reinterpret_cast<const uint32_t *>(ptr);
79 case DataType::S32:
80 return *reinterpret_cast<const int32_t *>(ptr);
81 case DataType::U64:
82 return *reinterpret_cast<const uint64_t *>(ptr);
83 case DataType::S64:
84 return *reinterpret_cast<const int64_t *>(ptr);
85#if ENABLE_FP16
86 case DataType::F16:
87 return *reinterpret_cast<const float16_t *>(ptr);
88#endif
89 case DataType::F32:
90 return *reinterpret_cast<const float *>(ptr);
91 case DataType::F64:
92 return *reinterpret_cast<const double *>(ptr);
93 case DataType::SIZET:
94 return *reinterpret_cast<const size_t *>(ptr);
95 default:
96 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
97 }
98}
99
steniu019746fd82017-06-15 10:49:37 +0100100bool is_equal(double target, double ref, double max_absolute_error = std::numeric_limits<double>::epsilon(), double max_relative_error = 0.0001f)
101{
102 if(!std::isfinite(target) || !std::isfinite(ref))
103 {
104 return false;
105 }
106
107 // No need further check if they are equal
108 if(ref == target)
109 {
110 return true;
111 }
112
113 // Need this check for the situation when the two values close to zero but have different sign
114 if(std::abs(std::abs(ref) - std::abs(target)) <= max_absolute_error)
115 {
116 return true;
117 }
118
119 double relative_error = 0;
120
121 if(std::abs(target) > std::abs(ref))
122 {
123 relative_error = std::abs((target - ref) / target);
124 }
125 else
126 {
127 relative_error = std::abs((ref - target) / ref);
128 }
129
130 return relative_error <= max_relative_error;
131}
132
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100133void check_border_element(const IAccessor &tensor, const Coordinates &id,
134 const BorderMode &border_mode, const void *border_value,
135 int64_t &num_elements, int64_t &num_mismatches)
136{
137 const size_t channel_size = element_size_from_data_type(tensor.data_type());
138 const auto ptr = static_cast<const uint8_t *>(tensor(id));
139
140 if(border_mode == BorderMode::REPLICATE)
141 {
142 Coordinates border_id{ id };
143 border_id.set(1, 0);
144 border_value = tensor(border_id);
145 }
146
147 // Iterate over all channels within one element
148 for(int channel = 0; channel < tensor.num_channels(); ++channel)
149 {
150 const size_t channel_offset = channel * channel_size;
151 const double target = get_double_data(ptr + channel_offset, tensor.data_type());
152 const double ref = get_double_data(static_cast<const uint8_t *>(border_value) + channel_offset, tensor.data_type());
steniu019746fd82017-06-15 10:49:37 +0100153 const bool equal = is_equal(target, ref);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100154
155 BOOST_TEST_INFO("id = " << id);
156 BOOST_TEST_INFO("channel = " << channel);
157 BOOST_TEST_INFO("reference = " << std::setprecision(5) << ref);
158 BOOST_TEST_INFO("target = " << std::setprecision(5) << target);
steniu019746fd82017-06-15 10:49:37 +0100159 BOOST_TEST_WARN(equal);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100160
steniu019746fd82017-06-15 10:49:37 +0100161 if(!equal)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100162 {
163 ++num_mismatches;
164 }
165
166 ++num_elements;
167 }
168}
169
170void check_single_element(const Coordinates &id, const IAccessor &tensor, const RawTensor &reference, float tolerance_value,
171 uint64_t wrap_range, int min_channels, size_t channel_size, int64_t &num_mismatches, int64_t &num_elements)
172{
173 const auto ptr = static_cast<const uint8_t *>(tensor(id));
174 const auto ref_ptr = static_cast<const uint8_t *>(reference(id));
175
176 // Iterate over all channels within one element
177 for(int channel = 0; channel < min_channels; ++channel)
178 {
179 const size_t channel_offset = channel * channel_size;
180 const double target = get_double_data(ptr + channel_offset, reference.data_type());
181 const double ref = get_double_data(ref_ptr + channel_offset, reference.data_type());
steniu019746fd82017-06-15 10:49:37 +0100182 bool equal = is_equal(target, ref, tolerance_value);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100183
steniu019746fd82017-06-15 10:49:37 +0100184 if(wrap_range != 0 && !equal)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100185 {
steniu019746fd82017-06-15 10:49:37 +0100186 equal = is_equal(target, ref, wrap_range - tolerance_value);
187 }
188
189 if(!equal)
190 {
191 BOOST_TEST_INFO("id = " << id);
192 BOOST_TEST_INFO("channel = " << channel);
193 BOOST_TEST_INFO("reference = " << std::setprecision(5) << ref);
194 BOOST_TEST_INFO("target = " << std::setprecision(5) << target);
195 BOOST_TEST_WARN(equal);
196
197 ++num_mismatches;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100198 }
199 ++num_elements;
200 }
201}
202} // namespace
203
204void validate(const arm_compute::ValidRegion &region, const arm_compute::ValidRegion &reference)
205{
206 BOOST_TEST(region.anchor.num_dimensions() == reference.anchor.num_dimensions());
207 BOOST_TEST(region.shape.num_dimensions() == reference.shape.num_dimensions());
208
209 for(unsigned int d = 0; d < region.anchor.num_dimensions(); ++d)
210 {
211 BOOST_TEST(region.anchor[d] == reference.anchor[d]);
212 }
213
214 for(unsigned int d = 0; d < region.shape.num_dimensions(); ++d)
215 {
216 BOOST_TEST(region.shape[d] == reference.shape[d]);
217 }
218}
219
220void validate(const arm_compute::PaddingSize &padding, const arm_compute::PaddingSize &reference)
221{
222 BOOST_TEST(padding.top == reference.top);
223 BOOST_TEST(padding.right == reference.right);
224 BOOST_TEST(padding.bottom == reference.bottom);
225 BOOST_TEST(padding.left == reference.left);
226}
227
228void validate(const IAccessor &tensor, const RawTensor &reference, float tolerance_value, float tolerance_number, uint64_t wrap_range)
229{
230 // Validate with valid region covering the entire shape
231 validate(tensor, reference, shape_to_valid_region(tensor.shape()), tolerance_value, tolerance_number, wrap_range);
232}
233
234void validate(const IAccessor &tensor, const RawTensor &reference, const ValidRegion &valid_region, float tolerance_value, float tolerance_number, uint64_t wrap_range)
235{
236 int64_t num_mismatches = 0;
237 int64_t num_elements = 0;
238
239 BOOST_TEST(tensor.element_size() == reference.element_size());
240 BOOST_TEST(tensor.format() == reference.format());
241 BOOST_TEST(tensor.data_type() == reference.data_type());
242 BOOST_TEST(tensor.num_channels() == reference.num_channels());
243 BOOST_TEST(compare_dimensions(tensor.shape(), reference.shape()));
244
245 const int min_elements = std::min(tensor.num_elements(), reference.num_elements());
246 const int min_channels = std::min(tensor.num_channels(), reference.num_channels());
247 const size_t channel_size = element_size_from_data_type(reference.data_type());
248
249 // Iterate over all elements within valid region, e.g. U8, S16, RGB888, ...
250 for(int element_idx = 0; element_idx < min_elements; ++element_idx)
251 {
252 const Coordinates id = index2coord(reference.shape(), element_idx);
253 if(is_in_valid_region(valid_region, id))
254 {
255 check_single_element(id, tensor, reference, tolerance_value, wrap_range, min_channels, channel_size, num_mismatches, num_elements);
256 }
257 }
258
259 const int64_t absolute_tolerance_number = tolerance_number * num_elements;
260 const float percent_mismatches = static_cast<float>(num_mismatches) / num_elements * 100.f;
261
262 BOOST_TEST(num_mismatches <= absolute_tolerance_number,
263 num_mismatches << " values (" << std::setprecision(2) << percent_mismatches
264 << "%) mismatched (maximum tolerated " << std::setprecision(2) << tolerance_number << "%)");
265}
266
267void validate(const IAccessor &tensor, const void *reference_value)
268{
269 BOOST_TEST_REQUIRE((reference_value != nullptr));
270
271 int64_t num_mismatches = 0;
272 int64_t num_elements = 0;
273 const size_t channel_size = element_size_from_data_type(tensor.data_type());
274
275 // Iterate over all elements, e.g. U8, S16, RGB888, ...
276 for(int element_idx = 0; element_idx < tensor.num_elements(); ++element_idx)
277 {
278 const Coordinates id = index2coord(tensor.shape(), element_idx);
279
280 const auto ptr = static_cast<const uint8_t *>(tensor(id));
281
282 // Iterate over all channels within one element
283 for(int channel = 0; channel < tensor.num_channels(); ++channel)
284 {
285 const size_t channel_offset = channel * channel_size;
286 const double target = get_double_data(ptr + channel_offset, tensor.data_type());
287 const double ref = get_double_data(reference_value, tensor.data_type());
steniu019746fd82017-06-15 10:49:37 +0100288 const bool equal = is_equal(target, ref);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100289
290 BOOST_TEST_INFO("id = " << id);
291 BOOST_TEST_INFO("channel = " << channel);
292 BOOST_TEST_INFO("reference = " << std::setprecision(5) << ref);
293 BOOST_TEST_INFO("target = " << std::setprecision(5) << target);
steniu019746fd82017-06-15 10:49:37 +0100294 BOOST_TEST_WARN(equal);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100295
steniu019746fd82017-06-15 10:49:37 +0100296 if(!equal)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100297 {
298 ++num_mismatches;
299 }
300
301 ++num_elements;
302 }
303 }
304
305 const float percent_mismatches = static_cast<float>(num_mismatches) / num_elements * 100.f;
306
307 BOOST_TEST(num_mismatches == 0,
308 num_mismatches << " values (" << std::setprecision(2) << percent_mismatches << "%) mismatched");
309}
310
311void validate(const IAccessor &tensor, BorderSize border_size, const BorderMode &border_mode, const void *border_value)
312{
313 if(border_mode == BorderMode::UNDEFINED)
314 {
315 return;
316 }
317 else if(border_mode == BorderMode::CONSTANT)
318 {
319 BOOST_TEST((border_value != nullptr));
320 }
321
322 int64_t num_mismatches = 0;
323 int64_t num_elements = 0;
324 const int slice_size = tensor.shape()[0] * tensor.shape()[1];
325
326 for(int element_idx = 0; element_idx < tensor.num_elements(); element_idx += slice_size)
327 {
328 Coordinates id = index2coord(tensor.shape(), element_idx);
329
330 // Top border
331 for(int y = -border_size.top; y < 0; ++y)
332 {
333 id.set(1, y);
334
335 for(int x = -border_size.left; x < static_cast<int>(tensor.shape()[0]) + static_cast<int>(border_size.right); ++x)
336 {
337 id.set(0, x);
338
339 check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches);
340 }
341 }
342
343 // Bottom border
344 for(int y = tensor.shape()[1]; y < static_cast<int>(tensor.shape()[1]) + static_cast<int>(border_size.bottom); ++y)
345 {
346 id.set(1, y);
347
348 for(int x = -border_size.left; x < static_cast<int>(tensor.shape()[0]) + static_cast<int>(border_size.right); ++x)
349 {
350 id.set(0, x);
351
352 check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches);
353 }
354 }
355
356 // Left/right border
357 for(int y = 0; y < static_cast<int>(tensor.shape()[1]); ++y)
358 {
359 id.set(1, y);
360
361 // Left border
362 for(int x = -border_size.left; x < 0; ++x)
363 {
364 id.set(0, x);
365
366 check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches);
367 }
368
369 // Right border
370 for(int x = tensor.shape()[0]; x < static_cast<int>(tensor.shape()[0]) + static_cast<int>(border_size.right); ++x)
371 {
372 id.set(0, x);
373
374 check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches);
375 }
376 }
377 }
378
379 const float percent_mismatches = static_cast<float>(num_mismatches) / num_elements * 100.f;
380
381 BOOST_TEST(num_mismatches == 0,
382 num_mismatches << " values (" << std::setprecision(2) << percent_mismatches << "%) mismatched");
383}
384
385void validate(std::vector<unsigned int> classified_labels, std::vector<unsigned int> expected_labels)
386{
387 BOOST_TEST(expected_labels.size() != 0);
388 BOOST_TEST(classified_labels.size() == expected_labels.size());
389
390 for(unsigned int i = 0; i < expected_labels.size(); ++i)
391 {
392 BOOST_TEST(classified_labels[i] == expected_labels[i]);
393 }
394}
steniu01423d88f2017-06-22 10:20:37 +0100395
396void validate(float target, float ref, float tolerance_abs_error = std::numeric_limits<float>::epsilon(), float tolerance_relative_error = 0.0001f)
397{
398 const bool equal = is_equal(target, ref, tolerance_abs_error, tolerance_relative_error);
399
400 BOOST_TEST_INFO("reference = " << std::setprecision(5) << ref);
401 BOOST_TEST_INFO("target = " << std::setprecision(5) << target);
402 BOOST_TEST(equal);
403}
404
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100405} // namespace validation
406} // namespace test
407} // namespace arm_compute