Compute Library  18.05
Validation.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017, 2018 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 
27 #include "arm_compute/core/Error.h"
29 #include "arm_compute/core/Types.h"
31 
32 #include <array>
33 #include <cmath>
34 #include <cstddef>
35 #include <cstdint>
36 
37 namespace arm_compute
38 {
39 namespace test
40 {
41 namespace validation
42 {
43 namespace
44 {
52 double get_double_data(const void *ptr, DataType data_type)
53 {
54  if(ptr == nullptr)
55  {
56  ARM_COMPUTE_ERROR("Can't dereference a null pointer!");
57  }
58 
59  switch(data_type)
60  {
61  case DataType::U8:
62  return *reinterpret_cast<const uint8_t *>(ptr);
63  case DataType::S8:
64  return *reinterpret_cast<const int8_t *>(ptr);
65  case DataType::QS8:
66  return *reinterpret_cast<const qint8_t *>(ptr);
67  case DataType::U16:
68  return *reinterpret_cast<const uint16_t *>(ptr);
69  case DataType::S16:
70  return *reinterpret_cast<const int16_t *>(ptr);
71  case DataType::QS16:
72  return *reinterpret_cast<const qint16_t *>(ptr);
73  case DataType::U32:
74  return *reinterpret_cast<const uint32_t *>(ptr);
75  case DataType::S32:
76  return *reinterpret_cast<const int32_t *>(ptr);
77  case DataType::U64:
78  return *reinterpret_cast<const uint64_t *>(ptr);
79  case DataType::S64:
80  return *reinterpret_cast<const int64_t *>(ptr);
81  case DataType::F16:
82  return *reinterpret_cast<const half *>(ptr);
83  case DataType::F32:
84  return *reinterpret_cast<const float *>(ptr);
85  case DataType::F64:
86  return *reinterpret_cast<const double *>(ptr);
87  case DataType::SIZET:
88  return *reinterpret_cast<const size_t *>(ptr);
89  default:
90  ARM_COMPUTE_ERROR("NOT SUPPORTED!");
91  }
92 }
93 
94 void check_border_element(const IAccessor &tensor, const Coordinates &id,
95  const BorderMode &border_mode, const void *border_value,
96  int64_t &num_elements, int64_t &num_mismatches)
97 {
98  const size_t channel_size = element_size_from_data_type(tensor.data_type());
99  const auto ptr = static_cast<const uint8_t *>(tensor(id));
100 
101  if(border_mode == BorderMode::REPLICATE)
102  {
103  Coordinates border_id{ id };
104 
105  if(id.x() < 0)
106  {
107  border_id.set(0, 0);
108  }
109  else if(static_cast<size_t>(id.x()) >= tensor.shape().x())
110  {
111  border_id.set(0, tensor.shape().x() - 1);
112  }
113 
114  if(id.y() < 0)
115  {
116  border_id.set(1, 0);
117  }
118  else if(static_cast<size_t>(id.y()) >= tensor.shape().y())
119  {
120  border_id.set(1, tensor.shape().y() - 1);
121  }
122 
123  border_value = tensor(border_id);
124  }
125 
126  // Iterate over all channels within one element
127  for(int channel = 0; channel < tensor.num_channels(); ++channel)
128  {
129  const size_t channel_offset = channel * channel_size;
130  const double target = get_double_data(ptr + channel_offset, tensor.data_type());
131  const double reference = get_double_data(static_cast<const uint8_t *>(border_value) + channel_offset, tensor.data_type());
132 
133  if(!compare<AbsoluteTolerance<double>>(target, reference))
134  {
135  ARM_COMPUTE_TEST_INFO("id = " << id);
136  ARM_COMPUTE_TEST_INFO("channel = " << channel);
137  ARM_COMPUTE_TEST_INFO("target = " << std::setprecision(5) << target);
138  ARM_COMPUTE_TEST_INFO("reference = " << std::setprecision(5) << reference);
140 
141  ++num_mismatches;
142  }
143 
144  ++num_elements;
145  }
146 }
147 } // namespace
148 
149 void validate(const arm_compute::ValidRegion &region, const arm_compute::ValidRegion &reference)
150 {
153 
154  for(unsigned int d = 0; d < region.anchor.num_dimensions(); ++d)
155  {
157  }
158 
159  for(unsigned int d = 0; d < region.shape.num_dimensions(); ++d)
160  {
162  }
163 }
164 
165 void validate(const arm_compute::PaddingSize &padding, const arm_compute::PaddingSize &reference)
166 {
171 }
172 
173 void validate(const arm_compute::PaddingSize &padding, const arm_compute::PaddingSize &width_reference, const arm_compute::PaddingSize &height_reference)
174 {
175  ARM_COMPUTE_EXPECT_EQUAL(padding.top, height_reference.top, framework::LogLevel::ERRORS);
179 }
180 
181 void validate(const IAccessor &tensor, const void *reference_value)
182 {
183  ARM_COMPUTE_ASSERT(reference_value != nullptr);
184 
185  int64_t num_mismatches = 0;
186  int64_t num_elements = 0;
187  const size_t channel_size = element_size_from_data_type(tensor.data_type());
188 
189  // Iterate over all elements, e.g. U8, S16, RGB888, ...
190  for(int element_idx = 0; element_idx < tensor.num_elements(); ++element_idx)
191  {
192  const Coordinates id = index2coord(tensor.shape(), element_idx);
193 
194  const auto ptr = static_cast<const uint8_t *>(tensor(id));
195 
196  // Iterate over all channels within one element
197  for(int channel = 0; channel < tensor.num_channels(); ++channel)
198  {
199  const size_t channel_offset = channel * channel_size;
200  const double target = get_double_data(ptr + channel_offset, tensor.data_type());
201  const double reference = get_double_data(reference_value, tensor.data_type());
202 
203  if(!compare<AbsoluteTolerance<double>>(target, reference))
204  {
205  ARM_COMPUTE_TEST_INFO("id = " << id);
206  ARM_COMPUTE_TEST_INFO("channel = " << channel);
207  ARM_COMPUTE_TEST_INFO("target = " << std::setprecision(5) << target);
208  ARM_COMPUTE_TEST_INFO("reference = " << std::setprecision(5) << reference);
210 
211  ++num_mismatches;
212  }
213 
214  ++num_elements;
215  }
216  }
217 
218  if(num_elements > 0)
219  {
220  const float percent_mismatches = static_cast<float>(num_mismatches) / num_elements * 100.f;
221 
222  ARM_COMPUTE_TEST_INFO(num_mismatches << " values (" << std::fixed << std::setprecision(2) << percent_mismatches << "%) mismatched");
224  }
225 }
226 
227 void validate(const IAccessor &tensor, BorderSize border_size, const BorderMode &border_mode, const void *border_value)
228 {
229  if(border_mode == BorderMode::UNDEFINED)
230  {
231  return;
232  }
233  else if(border_mode == BorderMode::CONSTANT)
234  {
235  ARM_COMPUTE_ASSERT(border_value != nullptr);
236  }
237 
238  int64_t num_mismatches = 0;
239  int64_t num_elements = 0;
240  const int slice_size = tensor.shape()[0] * tensor.shape()[1];
241 
242  for(int element_idx = 0; element_idx < tensor.num_elements(); element_idx += slice_size)
243  {
244  Coordinates id = index2coord(tensor.shape(), element_idx);
245 
246  // Top border
247  for(int y = -border_size.top; y < 0; ++y)
248  {
249  id.set(1, y);
250 
251  for(int x = -border_size.left; x < static_cast<int>(tensor.shape()[0]) + static_cast<int>(border_size.right); ++x)
252  {
253  id.set(0, x);
254 
255  check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches);
256  }
257  }
258 
259  // Bottom border
260  for(int y = tensor.shape()[1]; y < static_cast<int>(tensor.shape()[1]) + static_cast<int>(border_size.bottom); ++y)
261  {
262  id.set(1, y);
263 
264  for(int x = -border_size.left; x < static_cast<int>(tensor.shape()[0]) + static_cast<int>(border_size.right); ++x)
265  {
266  id.set(0, x);
267 
268  check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches);
269  }
270  }
271 
272  // Left/right border
273  for(int y = 0; y < static_cast<int>(tensor.shape()[1]); ++y)
274  {
275  id.set(1, y);
276 
277  // Left border
278  for(int x = -border_size.left; x < 0; ++x)
279  {
280  id.set(0, x);
281 
282  check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches);
283  }
284 
285  // Right border
286  for(int x = tensor.shape()[0]; x < static_cast<int>(tensor.shape()[0]) + static_cast<int>(border_size.right); ++x)
287  {
288  id.set(0, x);
289 
290  check_border_element(tensor, id, border_mode, border_value, num_elements, num_mismatches);
291  }
292  }
293  }
294 
295  if(num_elements > 0)
296  {
297  const float percent_mismatches = static_cast<float>(num_mismatches) / num_elements * 100.f;
298 
299  ARM_COMPUTE_TEST_INFO(num_mismatches << " values (" << std::fixed << std::setprecision(2) << percent_mismatches << "%) mismatched");
301  }
302 }
303 
304 void validate(std::vector<unsigned int> classified_labels, std::vector<unsigned int> expected_labels)
305 {
306  ARM_COMPUTE_EXPECT_EQUAL(classified_labels.size(), expected_labels.size(), framework::LogLevel::ERRORS);
307 
308  int64_t num_mismatches = 0;
309  const int num_elements = std::min(classified_labels.size(), expected_labels.size());
310 
311  for(int i = 0; i < num_elements; ++i)
312  {
313  if(classified_labels[i] != expected_labels[i])
314  {
315  ++num_mismatches;
316  ARM_COMPUTE_EXPECT_EQUAL(classified_labels[i], expected_labels[i], framework::LogLevel::DEBUG);
317  }
318  }
319 
320  if(num_elements > 0)
321  {
322  const float percent_mismatches = static_cast<float>(num_mismatches) / num_elements * 100.f;
323 
324  ARM_COMPUTE_TEST_INFO(num_mismatches << " values (" << std::fixed << std::setprecision(2) << percent_mismatches << "%) mismatched");
326  }
327 }
328 } // namespace validation
329 } // namespace test
330 } // namespace arm_compute
BorderMode
Methods available to handle borders.
Definition: Types.h:283
#define ARM_COMPUTE_ERROR(...)
Print the given message then throw an std::runtime_error.
Definition: Error.h:260
unsigned int top
top of the border
Definition: Types.h:371
quantized, symmetric fixed-point 16-bit number
fixed_point< T > min(fixed_point< T > x, fixed_point< T > y)
Definition: FixedPoint.h:897
Class reprensenting an absolute tolerance value.
Definition: Validation.h:51
quantized, symmetric fixed-point 8-bit number
TensorShape shape
Shape of the valid region.
Definition: Types.h:279
Container for 2D border size.
Definition: Types.h:291
1 channel, 1 U8 per channel
size_t element_size_from_data_type(DataType dt)
The size in bytes of the data type.
Definition: Utils.h:182
half_float::half half
16-bit floating point type
Definition: Types.h:44
1 channel, 1 F32 per channel
1 channel, 1 U16 per channel
unsigned int bottom
bottom of the border
Definition: Types.h:373
void set(size_t dimension, T value)
Accessor to set the value of one of the dimensions.
Definition: Dimensions.h:74
#define ARM_COMPUTE_EXPECT_EQUAL(X, Y, LEVEL)
Definition: Asserts.h:104
virtual TensorShape shape() const =0
Shape of the tensor.
#define ARM_COMPUTE_TEST_INFO(INFO)
Definition: Asserts.h:65
This file contains all available output stages for GEMMLowp on OpenCL.
1 channel, 1 F16 per channel
1 channel, 1 S32 per channel
signed 64-bit number
validate(dst.info() ->valid_region(), dst_valid_region)
1 channel, 1 U32 per channel
Coordinates of an item.
Definition: Coordinates.h:37
int16_t qint16_t
16 bit fixed point scalar value
Definition: FixedPoint.h:30
virtual int num_elements() const =0
Number of elements of the tensor.
Coordinates index2coord(const TensorShape &shape, int index)
Convert a linear index into n-dimensional coordinates.
Definition: Utils.h:419
unsigned int left
left of the border
Definition: Types.h:374
unsigned int right
right of the border
Definition: Types.h:372
1 channel, 1 S16 per channel
#define ARM_COMPUTE_ASSERT(X)
Definition: Asserts.h:110
int8_t qint8_t
8 bit fixed point scalar value
Definition: FixedPoint.h:29
unsigned int num_dimensions() const
Returns the effective dimensionality of the tensor.
Definition: Dimensions.h:122
virtual DataType data_type() const =0
Data type of the tensor.
Borders are left undefined.
Pixels outside the image are assumed to have the same value as the closest image pixel.
Common interface to provide information and access to tensor like structures.
Definition: IAccessor.h:37
Pixels outside the image are assumed to have a constant value.
64-bit floating-point number
Container for valid region of a window.
Definition: Types.h:206
virtual int num_channels() const =0
Number of channels of the tensor.
unsigned 64-bit number
DataType
Available data types.
Definition: Types.h:72
signed 8-bit number
Coordinates anchor
Anchor for the start of the valid region.
Definition: Types.h:278