blob: 8aada0cb0ef2018276fc99b3ae7125772605c21a [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
Pablo Tello383deec2017-06-23 10:40:05 +010043#if ARM_COMPUTE_ENABLE_FP16
44#include <arm_fp16.h> // needed for float16_t
45#endif
46
Anthony Barbier6ff3b192017-09-04 18:44:23 +010047namespace arm_compute
48{
49namespace test
50{
51namespace validation
52{
53namespace
54{
55/** Get the data from *ptr after casting according to @p data_type and then convert the data to double.
56 *
57 * @param[in] ptr Pointer to value.
58 * @param[in] data_type Data type of both values.
59 *
60 * @return The data from the ptr after converted to double.
61 */
62double get_double_data(const void *ptr, DataType data_type)
63{
steniu019746fd82017-06-15 10:49:37 +010064 if(ptr == nullptr)
65 {
66 ARM_COMPUTE_ERROR("Can't dereference a null pointer!");
67 }
68
Anthony Barbier6ff3b192017-09-04 18:44:23 +010069 switch(data_type)
70 {
71 case DataType::U8:
72 return *reinterpret_cast<const uint8_t *>(ptr);
73 case DataType::S8:
74 return *reinterpret_cast<const int8_t *>(ptr);
75 case DataType::QS8:
76 return *reinterpret_cast<const qint8_t *>(ptr);
77 case DataType::U16:
78 return *reinterpret_cast<const uint16_t *>(ptr);
79 case DataType::S16:
80 return *reinterpret_cast<const int16_t *>(ptr);
81 case DataType::U32:
82 return *reinterpret_cast<const uint32_t *>(ptr);
83 case DataType::S32:
84 return *reinterpret_cast<const int32_t *>(ptr);
85 case DataType::U64:
86 return *reinterpret_cast<const uint64_t *>(ptr);
87 case DataType::S64:
88 return *reinterpret_cast<const int64_t *>(ptr);
Pablo Tello383deec2017-06-23 10:40:05 +010089#if ARM_COMPUTE_ENABLE_FP16
Anthony Barbier6ff3b192017-09-04 18:44:23 +010090 case DataType::F16:
91 return *reinterpret_cast<const float16_t *>(ptr);
92#endif
93 case DataType::F32:
94 return *reinterpret_cast<const float *>(ptr);
95 case DataType::F64:
96 return *reinterpret_cast<const double *>(ptr);
97 case DataType::SIZET:
98 return *reinterpret_cast<const size_t *>(ptr);
99 default:
100 ARM_COMPUTE_ERROR("NOT SUPPORTED!");
101 }
102}
103
steniu019746fd82017-06-15 10:49:37 +0100104bool is_equal(double target, double ref, double max_absolute_error = std::numeric_limits<double>::epsilon(), double max_relative_error = 0.0001f)
105{
106 if(!std::isfinite(target) || !std::isfinite(ref))
107 {
108 return false;
109 }
110
111 // No need further check if they are equal
112 if(ref == target)
113 {
114 return true;
115 }
116
117 // Need this check for the situation when the two values close to zero but have different sign
118 if(std::abs(std::abs(ref) - std::abs(target)) <= max_absolute_error)
119 {
120 return true;
121 }
122
123 double relative_error = 0;
124
125 if(std::abs(target) > std::abs(ref))
126 {
127 relative_error = std::abs((target - ref) / target);
128 }
129 else
130 {
131 relative_error = std::abs((ref - target) / ref);
132 }
133
134 return relative_error <= max_relative_error;
135}
136
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100137void check_border_element(const IAccessor &tensor, const Coordinates &id,
138 const BorderMode &border_mode, const void *border_value,
139 int64_t &num_elements, int64_t &num_mismatches)
140{
141 const size_t channel_size = element_size_from_data_type(tensor.data_type());
142 const auto ptr = static_cast<const uint8_t *>(tensor(id));
143
144 if(border_mode == BorderMode::REPLICATE)
145 {
146 Coordinates border_id{ id };
147 border_id.set(1, 0);
148 border_value = tensor(border_id);
149 }
150
151 // Iterate over all channels within one element
152 for(int channel = 0; channel < tensor.num_channels(); ++channel)
153 {
154 const size_t channel_offset = channel * channel_size;
155 const double target = get_double_data(ptr + channel_offset, tensor.data_type());
156 const double ref = get_double_data(static_cast<const uint8_t *>(border_value) + channel_offset, tensor.data_type());
steniu019746fd82017-06-15 10:49:37 +0100157 const bool equal = is_equal(target, ref);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100158
159 BOOST_TEST_INFO("id = " << id);
160 BOOST_TEST_INFO("channel = " << channel);
161 BOOST_TEST_INFO("reference = " << std::setprecision(5) << ref);
162 BOOST_TEST_INFO("target = " << std::setprecision(5) << target);
steniu019746fd82017-06-15 10:49:37 +0100163 BOOST_TEST_WARN(equal);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100164
steniu019746fd82017-06-15 10:49:37 +0100165 if(!equal)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100166 {
167 ++num_mismatches;
168 }
169
170 ++num_elements;
171 }
172}
173
174void check_single_element(const Coordinates &id, const IAccessor &tensor, const RawTensor &reference, float tolerance_value,
175 uint64_t wrap_range, int min_channels, size_t channel_size, int64_t &num_mismatches, int64_t &num_elements)
176{
177 const auto ptr = static_cast<const uint8_t *>(tensor(id));
178 const auto ref_ptr = static_cast<const uint8_t *>(reference(id));
179
180 // Iterate over all channels within one element
181 for(int channel = 0; channel < min_channels; ++channel)
182 {
183 const size_t channel_offset = channel * channel_size;
184 const double target = get_double_data(ptr + channel_offset, reference.data_type());
185 const double ref = get_double_data(ref_ptr + channel_offset, reference.data_type());
steniu019746fd82017-06-15 10:49:37 +0100186 bool equal = is_equal(target, ref, tolerance_value);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100187
steniu019746fd82017-06-15 10:49:37 +0100188 if(wrap_range != 0 && !equal)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100189 {
steniu019746fd82017-06-15 10:49:37 +0100190 equal = is_equal(target, ref, wrap_range - tolerance_value);
191 }
192
193 if(!equal)
194 {
195 BOOST_TEST_INFO("id = " << id);
196 BOOST_TEST_INFO("channel = " << channel);
197 BOOST_TEST_INFO("reference = " << std::setprecision(5) << ref);
198 BOOST_TEST_INFO("target = " << std::setprecision(5) << target);
199 BOOST_TEST_WARN(equal);
200
201 ++num_mismatches;
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100202 }
203 ++num_elements;
204 }
205}
206} // namespace
207
208void validate(const arm_compute::ValidRegion &region, const arm_compute::ValidRegion &reference)
209{
210 BOOST_TEST(region.anchor.num_dimensions() == reference.anchor.num_dimensions());
211 BOOST_TEST(region.shape.num_dimensions() == reference.shape.num_dimensions());
212
213 for(unsigned int d = 0; d < region.anchor.num_dimensions(); ++d)
214 {
215 BOOST_TEST(region.anchor[d] == reference.anchor[d]);
216 }
217
218 for(unsigned int d = 0; d < region.shape.num_dimensions(); ++d)
219 {
220 BOOST_TEST(region.shape[d] == reference.shape[d]);
221 }
222}
223
224void validate(const arm_compute::PaddingSize &padding, const arm_compute::PaddingSize &reference)
225{
226 BOOST_TEST(padding.top == reference.top);
227 BOOST_TEST(padding.right == reference.right);
228 BOOST_TEST(padding.bottom == reference.bottom);
229 BOOST_TEST(padding.left == reference.left);
230}
231
232void validate(const IAccessor &tensor, const RawTensor &reference, float tolerance_value, float tolerance_number, uint64_t wrap_range)
233{
234 // Validate with valid region covering the entire shape
235 validate(tensor, reference, shape_to_valid_region(tensor.shape()), tolerance_value, tolerance_number, wrap_range);
236}
237
238void validate(const IAccessor &tensor, const RawTensor &reference, const ValidRegion &valid_region, float tolerance_value, float tolerance_number, uint64_t wrap_range)
239{
240 int64_t num_mismatches = 0;
241 int64_t num_elements = 0;
242
243 BOOST_TEST(tensor.element_size() == reference.element_size());
244 BOOST_TEST(tensor.format() == reference.format());
245 BOOST_TEST(tensor.data_type() == reference.data_type());
246 BOOST_TEST(tensor.num_channels() == reference.num_channels());
247 BOOST_TEST(compare_dimensions(tensor.shape(), reference.shape()));
248
249 const int min_elements = std::min(tensor.num_elements(), reference.num_elements());
250 const int min_channels = std::min(tensor.num_channels(), reference.num_channels());
251 const size_t channel_size = element_size_from_data_type(reference.data_type());
252
253 // Iterate over all elements within valid region, e.g. U8, S16, RGB888, ...
254 for(int element_idx = 0; element_idx < min_elements; ++element_idx)
255 {
256 const Coordinates id = index2coord(reference.shape(), element_idx);
257 if(is_in_valid_region(valid_region, id))
258 {
259 check_single_element(id, tensor, reference, tolerance_value, wrap_range, min_channels, channel_size, num_mismatches, num_elements);
260 }
261 }
262
263 const int64_t absolute_tolerance_number = tolerance_number * num_elements;
264 const float percent_mismatches = static_cast<float>(num_mismatches) / num_elements * 100.f;
265
266 BOOST_TEST(num_mismatches <= absolute_tolerance_number,
267 num_mismatches << " values (" << std::setprecision(2) << percent_mismatches
268 << "%) mismatched (maximum tolerated " << std::setprecision(2) << tolerance_number << "%)");
269}
270
271void validate(const IAccessor &tensor, const void *reference_value)
272{
273 BOOST_TEST_REQUIRE((reference_value != nullptr));
274
275 int64_t num_mismatches = 0;
276 int64_t num_elements = 0;
277 const size_t channel_size = element_size_from_data_type(tensor.data_type());
278
279 // Iterate over all elements, e.g. U8, S16, RGB888, ...
280 for(int element_idx = 0; element_idx < tensor.num_elements(); ++element_idx)
281 {
282 const Coordinates id = index2coord(tensor.shape(), element_idx);
283
284 const auto ptr = static_cast<const uint8_t *>(tensor(id));
285
286 // Iterate over all channels within one element
287 for(int channel = 0; channel < tensor.num_channels(); ++channel)
288 {
289 const size_t channel_offset = channel * channel_size;
290 const double target = get_double_data(ptr + channel_offset, tensor.data_type());
291 const double ref = get_double_data(reference_value, tensor.data_type());
steniu019746fd82017-06-15 10:49:37 +0100292 const bool equal = is_equal(target, ref);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100293
294 BOOST_TEST_INFO("id = " << id);
295 BOOST_TEST_INFO("channel = " << channel);
296 BOOST_TEST_INFO("reference = " << std::setprecision(5) << ref);
297 BOOST_TEST_INFO("target = " << std::setprecision(5) << target);
steniu019746fd82017-06-15 10:49:37 +0100298 BOOST_TEST_WARN(equal);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100299
steniu019746fd82017-06-15 10:49:37 +0100300 if(!equal)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100301 {
302 ++num_mismatches;
303 }
304
305 ++num_elements;
306 }
307 }
308
309 const float percent_mismatches = static_cast<float>(num_mismatches) / num_elements * 100.f;
310
311 BOOST_TEST(num_mismatches == 0,
312 num_mismatches << " values (" << std::setprecision(2) << percent_mismatches << "%) mismatched");
313}
314
315void validate(const IAccessor &tensor, BorderSize border_size, const BorderMode &border_mode, const void *border_value)
316{
317 if(border_mode == BorderMode::UNDEFINED)
318 {
319 return;
320 }
321 else if(border_mode == BorderMode::CONSTANT)
322 {
323 BOOST_TEST((border_value != nullptr));
324 }
325
326 int64_t num_mismatches = 0;
327 int64_t num_elements = 0;
328 const int slice_size = tensor.shape()[0] * tensor.shape()[1];
329
330 for(int element_idx = 0; element_idx < tensor.num_elements(); element_idx += slice_size)
331 {
332 Coordinates id = index2coord(tensor.shape(), element_idx);
333
334 // Top border
335 for(int y = -border_size.top; y < 0; ++y)
336 {
337 id.set(1, y);
338
339 for(int x = -border_size.left; x < static_cast<int>(tensor.shape()[0]) + static_cast<int>(border_size.right); ++x)
340 {
341 id.set(0, x);
342
343 check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches);
344 }
345 }
346
347 // Bottom border
348 for(int y = tensor.shape()[1]; y < static_cast<int>(tensor.shape()[1]) + static_cast<int>(border_size.bottom); ++y)
349 {
350 id.set(1, y);
351
352 for(int x = -border_size.left; x < static_cast<int>(tensor.shape()[0]) + static_cast<int>(border_size.right); ++x)
353 {
354 id.set(0, x);
355
356 check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches);
357 }
358 }
359
360 // Left/right border
361 for(int y = 0; y < static_cast<int>(tensor.shape()[1]); ++y)
362 {
363 id.set(1, y);
364
365 // Left border
366 for(int x = -border_size.left; x < 0; ++x)
367 {
368 id.set(0, x);
369
370 check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches);
371 }
372
373 // Right border
374 for(int x = tensor.shape()[0]; x < static_cast<int>(tensor.shape()[0]) + static_cast<int>(border_size.right); ++x)
375 {
376 id.set(0, x);
377
378 check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches);
379 }
380 }
381 }
382
383 const float percent_mismatches = static_cast<float>(num_mismatches) / num_elements * 100.f;
384
385 BOOST_TEST(num_mismatches == 0,
386 num_mismatches << " values (" << std::setprecision(2) << percent_mismatches << "%) mismatched");
387}
388
389void validate(std::vector<unsigned int> classified_labels, std::vector<unsigned int> expected_labels)
390{
Pablo Tello383deec2017-06-23 10:40:05 +0100391 ARM_COMPUTE_UNUSED(classified_labels);
392 ARM_COMPUTE_UNUSED(expected_labels);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100393 BOOST_TEST(expected_labels.size() != 0);
394 BOOST_TEST(classified_labels.size() == expected_labels.size());
395
396 for(unsigned int i = 0; i < expected_labels.size(); ++i)
397 {
398 BOOST_TEST(classified_labels[i] == expected_labels[i]);
399 }
400}
steniu01423d88f2017-06-22 10:20:37 +0100401
steniu01960b0842017-06-23 11:44:34 +0100402void validate(float target, float ref, float tolerance_abs_error, float tolerance_relative_error)
steniu01423d88f2017-06-22 10:20:37 +0100403{
404 const bool equal = is_equal(target, ref, tolerance_abs_error, tolerance_relative_error);
405
406 BOOST_TEST_INFO("reference = " << std::setprecision(5) << ref);
407 BOOST_TEST_INFO("target = " << std::setprecision(5) << target);
408 BOOST_TEST(equal);
409}
410
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100411} // namespace validation
412} // namespace test
413} // namespace arm_compute