Compute Library
 21.02
Convolution3d.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017-2021 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 #ifndef ARM_COMPUTE_TEST_VALIDATION_CONVOLUTION_H
25 #define ARM_COMPUTE_TEST_VALIDATION_CONVOLUTION_H
26 
28 #include "support/Requires.h"
31 
32 namespace arm_compute
33 {
34 namespace test
35 {
36 namespace convolution_3d
37 {
38 namespace detail
39 {
40 inline bool is_valid_pixel(int i, int min, int max)
41 {
42  return (i >= min && i < max);
43 }
44 
45 // 3D convolution for floating point type
46 template < typename T, typename TW, typename TB, typename std::enable_if < validation::is_floating_point<T>::value &&validation::is_floating_point<TW>::value
48  int >::type = 0 >
49 inline void convolution3d(const SimpleTensor<T> &in, const SimpleTensor<TW> &weights, const SimpleTensor<TB> &bias, SimpleTensor<T> &out,
50  int i_offset, int w_offset, int b_offset, int o_offset,
51  int xi, int yi, int width_in, int height_in, int depth_in, int width_weights, int height_weights, int dilation_x = 1, int dilation_y = 1, int filter_id = 0)
52 {
53  ARM_COMPUTE_UNUSED(filter_id);
54  const T *in_ptr = in.data() + i_offset;
55  const TW *w_ptr = weights.data() + w_offset;
56  const TB *b_ptr = bias.data() + b_offset;
57  T *out_ptr = out.data() + o_offset;
58 
59  const int half_width_weights_start = width_weights / 2;
60  const int half_width_weights_end = ((width_weights % 2) == 0) ? (half_width_weights_start - 1) : half_width_weights_start;
61  const int half_height_weights_start = height_weights / 2;
62  const int half_height_weights_end = ((height_weights % 2) == 0) ? (half_height_weights_start - 1) : half_height_weights_start;
63 
64  // Reset accumulator
65  T acc(0);
66 
67  // Compute a 2D convolution for each IFM and accumulate the result
68  for(int ifm = 0; ifm < depth_in; ++ifm)
69  {
70  // Compute the offset for the input slice
71  const int offset_slice_in = xi + yi * width_in + ifm * width_in * height_in;
72 
73  // Compute 2D convolution
74  for(int yk = -half_height_weights_start; yk <= half_height_weights_end; ++yk)
75  {
76  for(int xk = -half_width_weights_start; xk <= half_width_weights_end; ++xk)
77  {
78  // Check if the pixel is out-of-bound
79  if(is_valid_pixel(xi + xk * dilation_x, 0, width_in) && is_valid_pixel(yi + yk * dilation_y, 0, height_in))
80  {
81  const int idx = xk + half_width_weights_start;
82  const int idy = yk + half_height_weights_start;
83 
84  const T i_value = in_ptr[offset_slice_in + xk * dilation_x + yk * dilation_y * width_in];
85  const TW w_value = w_ptr[idx + idy * width_weights + ifm * width_weights * height_weights];
86 
87  acc += i_value * w_value;
88  }
89  }
90  }
91  }
92 
93  // Accumulate the bias and store the result
94  *out_ptr = acc + (*b_ptr);
95 }
96 
97 // 3D convolution for QASYMM8 type
98 template < typename T, typename TW, typename TB, ARM_COMPUTE_REQUIRES_TA((std::is_same<T, uint8_t>::value || std::is_same<T, int8_t>::value) &&(std::is_same<TW, uint8_t>::value
99  || std::is_same<TW, int8_t>::value)) >
100 inline void convolution3d(const SimpleTensor<T> &in, const SimpleTensor<TW> &weights, const SimpleTensor<TB> &bias, SimpleTensor<T> &out,
101  int i_offset, int w_offset, int b_offset, int o_offset,
102  int xi, int yi, int width_in, int height_in, int depth_in, int width_weights, int height_weights, int dilation_x = 1, int dilation_y = 1, int filter_id = 0)
103 {
104  const T *in_ptr = in.data() + i_offset;
105  const TW *w_ptr = weights.data() + w_offset;
106  const TB *b_ptr = bias.data() + b_offset;
107  T *out_ptr = out.data() + o_offset;
108 
109  const UniformQuantizationInfo iq_info = in.quantization_info().uniform();
110  const UniformQuantizationInfo wq_info = weights.quantization_info().uniform();
111  const UniformQuantizationInfo oq_info = out.quantization_info().uniform();
112 
113  const int input_offset = -iq_info.offset;
114  const float input_scale = iq_info.scale;
115  int weights_offset = -wq_info.offset;
116  float weights_scale = wq_info.scale;
118  {
120  {
121  weights_offset = weights.quantization_info().offset()[filter_id];
122  }
123  else
124  {
125  weights_offset = 0;
126  }
127  weights_scale = weights.quantization_info().scale()[filter_id];
128  }
129  const int output_offset = oq_info.offset;
130  const float output_scale = oq_info.scale;
131 
132  int output_multiplier = 0;
133  int output_shift = 0;
134  const float multiplier = input_scale * weights_scale / output_scale;
135  arm_compute::quantization::calculate_quantized_multiplier(multiplier, &output_multiplier, &output_shift);
136 
137  const int half_width_weights_start = width_weights / 2;
138  const int half_width_weights_end = ((width_weights % 2) == 0) ? (half_width_weights_start - 1) : half_width_weights_start;
139  const int half_height_weights_start = height_weights / 2;
140  const int half_height_weights_end = ((height_weights % 2) == 0) ? (half_height_weights_start - 1) : half_height_weights_start;
141 
142  // Reset accumulator
143  int32_t acc(0);
144 
145  // Compute a 2D convolution for each IFM and accumulate the result
146  for(int ifm = 0; ifm < depth_in; ++ifm)
147  {
148  // Compute the offset for the input slice
149  const int offset_slice_in = xi + yi * width_in + ifm * width_in * height_in;
150 
151  // Compute 2D convolution
152  for(int yk = -half_height_weights_start; yk <= half_height_weights_end; ++yk)
153  {
154  for(int xk = -half_width_weights_start; xk <= half_width_weights_end; ++xk)
155  {
156  // Check if the pixel is out-of-bound
157  if(is_valid_pixel(xi + xk * dilation_x, 0, width_in) && is_valid_pixel(yi + yk * dilation_y, 0, height_in))
158  {
159  const int idx = xk + half_width_weights_start;
160  const int idy = yk + half_height_weights_start;
161 
162  const int32_t i_value = in_ptr[offset_slice_in + xk * dilation_x + yk * dilation_y * width_in];
163  const int32_t w_value = w_ptr[idx + idy * width_weights + ifm * width_weights * height_weights];
164  acc += (i_value + input_offset) * (w_value + weights_offset);
165  }
166  }
167  }
168  }
169 
170  // Accumulate the bias
171  acc += (*b_ptr);
172 
173  // Quantize down
174  acc = validation::quantize_down_scale_by_fixedpoint(acc, output_multiplier, output_shift, output_offset,
175  std::numeric_limits<T>::lowest(), std::numeric_limits<T>::max());
176 
177  // Store the result
178  *out_ptr = acc;
179 }
180 } // namespace detail
181 } // namespace convolution_3d
182 } // namespace test
183 } // namespace arm_compute
184 #endif /* ARM_COMPUTE_TEST_VALIDATION_CONVOLUTION_H */
DataType data_type() const override
Data type of the tensor.
Definition: SimpleTensor.h:357
Quantization info when assuming per layer quantization.
Status calculate_quantized_multiplier(float multiplier, int32_t *quant_multiplier, int32_t *shift, bool ignore_epsilon=false)
Calculate quantized representation of multiplier.
decltype(strategy::transforms) typedef type
bool is_valid_pixel(int i, int min, int max)
Definition: Convolution3d.h:40
Copyright (c) 2017-2021 Arm Limited.
void convolution3d(const SimpleTensor< T > &in, const SimpleTensor< TW > &weights, const SimpleTensor< TB > &bias, SimpleTensor< T > &out, int i_offset, int w_offset, int b_offset, int o_offset, int xi, int yi, int width_in, int height_in, int depth_in, int width_weights, int height_weights, int dilation_x=1, int dilation_y=1, int filter_id=0)
Definition: Convolution3d.h:49
#define ARM_COMPUTE_UNUSED(...)
To avoid unused variables warnings.
Definition: Error.h:152
bool is_data_type_quantized_per_channel(DataType dt)
Check if a given data type is of per channel type.
Definition: Utils.h:1245
int32_t quantize_down_scale_by_fixedpoint(int32_t val, int32_t result_mult_int, int32_t result_shift, int32_t result_offset_after_shift, int32_t min, int32_t max)
Quantize down the input value in range [min, max].
bool is_data_type_quantized_asymmetric(DataType dt)
Check if a given data type is of asymmetric quantized type.
Definition: Utils.h:1190
Simple tensor object that stores elements in a consecutive chunk of memory.
Definition: SimpleTensor.h:58
QuantizationInfo quantization_info() const override
Quantization info in case of asymmetric quantized type.
Definition: SimpleTensor.h:332
const T * data() const
Constant pointer to the underlying buffer.
Definition: SimpleTensor.h:418