blob: b99d5346356834d337252c9489290c8440909416 [file] [log] [blame]
Sanghoon Leef47bfb92018-01-23 15:16:47 +00001/*
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 *asymm_int_mult
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, asymm_int_multDAMAGES 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
27#include "arm_compute/core/utils/quantization/AsymmHelpers.h"
28#include "tests/validation/FixedPoint.h"
29#include "tests/validation/Helpers.h"
30#include "tests/validation/reference/UtilsQuantizedAsymm.h"
31
32namespace arm_compute
33{
34namespace test
35{
36namespace convolution_3d
37{
38namespace detail
39{
40inline 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
46template < typename T, typename TB, typename std::enable_if < validation::is_floating_point<T>::value &&validation::is_floating_point<TB>::value, int >::type = 0 >
47inline void convolution3d(const SimpleTensor<T> &in, const SimpleTensor<T> &weights, const SimpleTensor<TB> &bias, SimpleTensor<T> &out,
48 int i_offset, int w_offset, int b_offset, int o_offset,
49 int xi, int yi, int width_in, int height_in, int depth_in, int width_weights, int height_weights)
50{
51 const T *in_ptr = in.data() + i_offset;
52 const T *w_ptr = weights.data() + w_offset;
53 const TB *b_ptr = bias.data() + b_offset;
54 T *out_ptr = out.data() + o_offset;
55
56 const int half_width_weights_start = width_weights / 2;
57 const int half_width_weights_end = ((width_weights % 2) == 0) ? (half_width_weights_start - 1) : half_width_weights_start;
58 const int half_height_weights_start = height_weights / 2;
59 const int half_height_weights_end = ((height_weights % 2) == 0) ? (half_height_weights_start - 1) : half_height_weights_start;
60
61 // Reset accumulator
62 T acc(0);
63
64 // Compute a 2D convolution for each IFM and accumulate the result
65 for(int ifm = 0; ifm < depth_in; ++ifm)
66 {
67 // Compute the offset for the input slice
68 const int offset_slice_in = xi + yi * width_in + ifm * width_in * height_in;
69
70 // Compute 2D convolution
71 for(int yk = -half_height_weights_start; yk <= half_height_weights_end; ++yk)
72 {
73 for(int xk = -half_width_weights_start; xk <= half_width_weights_end; ++xk)
74 {
75 // Check if the pixel is out-of-bound
76 if(is_valid_pixel(xi + xk, 0, width_in) && is_valid_pixel(yi + yk, 0, height_in))
77 {
78 const int idx = xk + half_width_weights_start;
79 const int idy = yk + half_height_weights_start;
80
81 const T i_value = in_ptr[offset_slice_in + xk + yk * width_in];
82 const T w_value = w_ptr[idx + idy * width_weights + ifm * width_weights * height_weights];
83
84 acc += i_value * w_value;
85 }
86 }
87 }
88 }
89
90 // Accumulate the bias and store the result
91 *out_ptr = acc + (*b_ptr);
92}
93
94// 3D convolution for fixed point type
95template < typename T, typename TB, typename std::enable_if < std::is_integral<T>::value &&std::is_integral<TB>::value, int >::type = 0 >
96inline void convolution3d(const SimpleTensor<T> &in, const SimpleTensor<T> &weights, const SimpleTensor<TB> &bias, SimpleTensor<T> &out,
97 int i_offset, int w_offset, int b_offset, int o_offset,
98 int xi, int yi, int width_in, int height_in, int depth_in, int width_weights, int height_weights)
99{
100 const T *in_ptr = in.data() + i_offset;
101 const T *w_ptr = weights.data() + w_offset;
102 const T *b_ptr = bias.data() + b_offset;
103 T *out_ptr = out.data() + o_offset;
104 int fixed_point_position = in.fixed_point_position();
105
106 const int half_width_weights_start = width_weights / 2;
107 const int half_width_weights_end = ((width_weights % 2) == 0) ? (half_width_weights_start - 1) : half_width_weights_start;
108 const int half_height_weights_start = height_weights / 2;
109 const int half_height_weights_end = ((height_weights % 2) == 0) ? (half_height_weights_start - 1) : half_height_weights_start;
110
111 using namespace fixed_point_arithmetic;
112 using promoted_type = fixed_point_arithmetic::traits::promote_t<T>;
113
114 // Reset accumulator
115 fixed_point<promoted_type> acc(0, fixed_point_position);
116
117 // Compute a 2D convolution for each IFM and accumulate the result
118 for(int ifm = 0; ifm < depth_in; ++ifm)
119 {
120 // Compute the offset for the input slice
121 const int offset_slice_in = xi + yi * width_in + ifm * width_in * height_in;
122
123 // Compute 2D convolution
124 for(int yk = -half_height_weights_start; yk <= half_height_weights_end; ++yk)
125 {
126 for(int xk = -half_width_weights_start; xk <= half_width_weights_end; ++xk)
127 {
128 // Check if the pixel is out-of-bound
129 if(is_valid_pixel(xi + xk, 0, width_in) && is_valid_pixel(yi + yk, 0, height_in))
130 {
131 const int idx = xk + half_width_weights_start;
132 const int idy = yk + half_height_weights_start;
133
134 const fixed_point<promoted_type> i_value(in_ptr[offset_slice_in + xk + yk * width_in], fixed_point_position, true);
135 const fixed_point<promoted_type> w_value(w_ptr[idx + idy * width_weights + ifm * width_weights * height_weights], fixed_point_position, true);
136 const fixed_point<promoted_type> iw = i_value * w_value;
137 acc = iw + acc;
138 }
139 }
140 }
141 }
142
143 // Get the bias
144 const fixed_point<promoted_type> b(*b_ptr, fixed_point_position, true);
145
146 // Accumulate the bias and covert back
147 acc = acc + b;
148 fixed_point<T> res(acc);
149 *out_ptr = res.raw();
150}
151
152// 3D convolution for QASYMM8 type
153template <>
154inline void convolution3d(const SimpleTensor<uint8_t> &in, const SimpleTensor<uint8_t> &weights, const SimpleTensor<int32_t> &bias, SimpleTensor<uint8_t> &out,
155 int i_offset, int w_offset, int b_offset, int o_offset,
156 int xi, int yi, int width_in, int height_in, int depth_in, int width_weights, int height_weights)
157{
158 const uint8_t *in_ptr = in.data() + i_offset;
159 const uint8_t *w_ptr = weights.data() + w_offset;
160 const int32_t *b_ptr = bias.data() + b_offset;
161 uint8_t *out_ptr = out.data() + o_offset;
162
163 const int input_offset = -in.quantization_info().offset;
164 const float input_scale = in.quantization_info().scale;
165 const int weights_offset = -weights.quantization_info().offset;
166 const float weights_scale = weights.quantization_info().scale;
167 const int output_offset = out.quantization_info().offset;
168 const float output_scale = out.quantization_info().scale;
169
170 int output_multiplier = 0;
171 int output_shift = 0;
172 const float multiplier = input_scale * weights_scale / output_scale;
173 arm_compute::quantization::calculate_quantized_multiplier_less_than_one(multiplier, &output_multiplier, &output_shift);
174
175 const int half_width_weights_start = width_weights / 2;
176 const int half_width_weights_end = ((width_weights % 2) == 0) ? (half_width_weights_start - 1) : half_width_weights_start;
177 const int half_height_weights_start = height_weights / 2;
178 const int half_height_weights_end = ((height_weights % 2) == 0) ? (half_height_weights_start - 1) : half_height_weights_start;
179
180 // Reset accumulator
181 int32_t acc(0);
182
183 // Compute a 2D convolution for each IFM and accumulate the result
184 for(int ifm = 0; ifm < depth_in; ++ifm)
185 {
186 // Compute the offset for the input slice
187 const int offset_slice_in = xi + yi * width_in + ifm * width_in * height_in;
188
189 // Compute 2D convolution
190 for(int yk = -half_height_weights_start; yk <= half_height_weights_end; ++yk)
191 {
192 for(int xk = -half_width_weights_start; xk <= half_width_weights_end; ++xk)
193 {
194 // Check if the pixel is out-of-bound
195 if(is_valid_pixel(xi + xk, 0, width_in) && is_valid_pixel(yi + yk, 0, height_in))
196 {
197 const int idx = xk + half_width_weights_start;
198 const int idy = yk + half_height_weights_start;
199
200 const uint8_t i_value = in_ptr[offset_slice_in + xk + yk * width_in];
201 const uint8_t w_value = w_ptr[idx + idy * width_weights + ifm * width_weights * height_weights];
202
203 acc += (i_value + input_offset) * (w_value + weights_offset);
204 }
205 }
206 }
207 }
208
209 // Accumulate the bias
210 acc += (*b_ptr);
211
212 acc = validation::asymm_rounding_divide_by_pow2(validation::asymm_int_mult(acc, output_multiplier), output_shift);
213 acc += output_offset;
214 acc = utility::clamp<int32_t>(acc, 0, 255);
215
216 // Store the result
217 *out_ptr = acc;
218}
219} // namespace detail
220} // namespace convolution_3d
221} // namespace test
222} // namespace arm_compute
223#endif /*__ARM_COMPUTE_TEST_VALIDATION_CONVOLUTION_H__ */