blob: 0f6908a468888866d3b37da821e43013b1b58510 [file] [log] [blame]
Pablo Tello299025a2017-09-29 11:30:12 +01001/*
SiCong Li11ab4512023-11-07 12:04:59 +00002 * Copyright (c) 2017-2024 Arm Limited.
Pablo Tello299025a2017-09-29 11:30:12 +01003 *
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 */
SiCong Li11ab4512023-11-07 12:04:59 +000024#ifndef ACL_TESTS_VALIDATION_FIXTURES_GEMMLOWPFIXTURE_H
25#define ACL_TESTS_VALIDATION_FIXTURES_GEMMLOWPFIXTURE_H
Pablo Tello299025a2017-09-29 11:30:12 +010026
Vidhya Sudhan Loganathan951b8a42019-11-04 14:42:08 +000027#include "arm_compute/core/utils/quantization/AsymmHelpers.h"
SiCong Li11ab4512023-11-07 12:04:59 +000028#include "src/core/utils/quantization/AsymmHelpers.h"
29#include "tests/validation/Helpers.h"
Pablo Tello299025a2017-09-29 11:30:12 +010030#include "tests/framework/Fixture.h"
Freddie Liardete572dff2022-05-16 14:09:10 +010031#include "tests/validation/Validation.h"
Ramy Elgammala77c6d72022-09-08 11:30:08 +010032#include "tests/validation/reference/GEMMLowp.h"
Radu Salavatf1f1f872024-02-27 18:32:26 +000033#include "tests/validation/reference/ArithmeticOperations.h"
Pablo Tello299025a2017-09-29 11:30:12 +010034
SiCong Li11ab4512023-11-07 12:04:59 +000035#include <cstdint>
36#include <vector>
37
Pablo Tello299025a2017-09-29 11:30:12 +010038namespace arm_compute
39{
40namespace test
41{
42namespace validation
43{
George Wort2d7e6832019-02-22 16:37:41 +000044namespace
45{
46template <typename U>
47void fill(U &&tensor, int i)
48{
Radu Salavatf1f1f872024-02-27 18:32:26 +000049 library->fill_tensor_uniform(tensor, i);
50}
51
52template <typename U>
53void fill_quantized(U &&tensor, int i)
54{
SiCong Li11ab4512023-11-07 12:04:59 +000055 ARM_COMPUTE_ASSERT(is_data_type_quantized(tensor.data_type()));
56 library->fill_tensor_uniform(tensor, i);
George Wort2d7e6832019-02-22 16:37:41 +000057}
58
SiCong Li11ab4512023-11-07 12:04:59 +000059template <typename U>
Radu Salavatf1f1f872024-02-27 18:32:26 +000060void fill_s32(U &&tensor, int i, int32_t min, int32_t max)
George Wort2d7e6832019-02-22 16:37:41 +000061{
SiCong Li11ab4512023-11-07 12:04:59 +000062 ARM_COMPUTE_ASSERT(tensor.data_type() == DataType::S32);
63 std::uniform_int_distribution<int32_t> distribution(min, max);
64 library->fill(tensor, distribution, i);
65}
66
67/** Information about how to fill tensors */
68struct TensorFillInfo
69{
70 // Bias fill range. Default values are arbitrary
71 int32_t min_bias {-20000};
72 int32_t max_bias {20000};
Radu Salavatf1f1f872024-02-27 18:32:26 +000073
74 // Output fill range. Default values are arbitrary
75 int32_t min_output {-20000};
76 int32_t max_output {20000};
77
SiCong Li11ab4512023-11-07 12:04:59 +000078 // Optional extra hash to randomize tensor filling
79 int32_t hash {0};
80};
81
82template <typename TensorType, typename AccessorType, typename FunctionType, bool reinterpret_input_as_3d, bool reinterpret_output_as_3d, typename OutputType, bool is_fused = false, bool run_twice = false>
83TensorType compute_gemmlowp_target(const TensorShape &shape_a, const TensorShape &shape_b, const TensorShape &shape_output, const QuantizationInfo& a_qinfo, const QuantizationInfo& b_qinfo,
84 const QuantizationInfo& output_qinfo, DataType data_type_a = DataType::QASYMM8, DataType data_type_b = DataType::QASYMM8,
Radu Salavatf1f1f872024-02-27 18:32:26 +000085 GEMMLowpOutputStageInfo output_stage = GEMMLowpOutputStageInfo(), bool reshape_b_only_on_first_run = false, const TensorFillInfo& finfo = TensorFillInfo(),
86 bool accumulate = false)
SiCong Li11ab4512023-11-07 12:04:59 +000087{
88 ARM_COMPUTE_ASSERT(is_data_type_quantized_asymmetric(data_type_a));
89 ARM_COMPUTE_ASSERT(data_type_a == data_type_b);
George Wort2d7e6832019-02-22 16:37:41 +000090 // Create tensors
SiCong Li11ab4512023-11-07 12:04:59 +000091 const DataType data_type_output = output_stage.type == GEMMLowpOutputStageType::NONE ? DataType::S32 : data_type_a;
Manuel Bottini959c26d2019-12-02 16:22:35 +000092
SiCong Li11ab4512023-11-07 12:04:59 +000093 TensorType a = create_tensor<TensorType>(shape_a, data_type_a, 1, a_qinfo);
94 TensorType b = create_tensor<TensorType>(shape_b, data_type_b, 1, b_qinfo); // gemm output before output stage mismatch if i pass data_layout_output here. to be investigated
95 TensorType output = create_tensor<TensorType>(shape_output, data_type_output, 1, output_qinfo /* output_qinfo will be ignored when output stage type is None */);
George Wort2d7e6832019-02-22 16:37:41 +000096
George Wort2d7e6832019-02-22 16:37:41 +000097 TensorType bias;
98 if(is_fused)
99 {
100 TensorShape bias_shape(shape_b[0]);
101 bias = create_tensor<TensorType>(bias_shape, DataType::S32, 1);
102 }
103
104 // Create and configure function
105 // The GEMMinfo includes the values of the depth in case of reinterpreted 3d input/output
106 FunctionType gemmlowp;
Giorgio Arena5f6fdc12021-06-09 15:23:06 +0100107 gemmlowp.configure(&a, &b, is_fused ? &bias : nullptr, &output, GEMMInfo(false, false, reshape_b_only_on_first_run, (reinterpret_output_as_3d ? shape_output[2] : 0), reinterpret_input_as_3d, false,
Radu Salavatf1f1f872024-02-27 18:32:26 +0000108 output_stage, false /*fp_mixed_precision*/, false /*fast_math*/, false /*broadcast_bias*/,
109 arm_compute::ActivationLayerInfo(), false /* fixed_format */, arm_compute::WeightFormat::UNSPECIFIED,
110 false /* pretranspose_B */, accumulate));
George Wort2d7e6832019-02-22 16:37:41 +0000111
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100112 ARM_COMPUTE_ASSERT(a.info()->is_resizable());
113 ARM_COMPUTE_ASSERT(b.info()->is_resizable());
114 ARM_COMPUTE_ASSERT(output.info()->is_resizable());
George Wort2d7e6832019-02-22 16:37:41 +0000115
Giorgio Arena63825e82021-03-25 14:54:50 +0000116 add_padding_x({ &a, &b, &output });
117
George Wort2d7e6832019-02-22 16:37:41 +0000118 // Allocate tensors
119 a.allocator()->allocate();
120 b.allocator()->allocate();
121 output.allocator()->allocate();
122
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100123 ARM_COMPUTE_ASSERT(!a.info()->is_resizable());
124 ARM_COMPUTE_ASSERT(!b.info()->is_resizable());
125 ARM_COMPUTE_ASSERT(!output.info()->is_resizable());
George Wort2d7e6832019-02-22 16:37:41 +0000126
127 // Fill tensors
Radu Salavatf1f1f872024-02-27 18:32:26 +0000128 fill_quantized(AccessorType(a), 0 + finfo.hash);
129 fill_quantized(AccessorType(b), 1 + finfo.hash);
130
131 if (accumulate)
132 {
133 ARM_COMPUTE_ASSERT(accumulate != run_twice);
134 fill_s32(AccessorType(output), 6 + finfo.hash, finfo.min_output, finfo.max_output);
135 }
George Wort2d7e6832019-02-22 16:37:41 +0000136
137 if(is_fused)
138 {
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100139 ARM_COMPUTE_ASSERT(bias.info()->is_resizable());
George Wort2d7e6832019-02-22 16:37:41 +0000140 bias.allocator()->allocate();
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100141 ARM_COMPUTE_ASSERT(!bias.info()->is_resizable());
Radu Salavatf1f1f872024-02-27 18:32:26 +0000142 fill_s32(AccessorType(bias), 2 + finfo.hash, finfo.min_bias, finfo.max_bias);
George Wort2d7e6832019-02-22 16:37:41 +0000143 }
Ramy Elgammala77c6d72022-09-08 11:30:08 +0100144
145 // Run with variable inputs.
146 if(run_twice)
147 {
148 gemmlowp.run();
Radu Salavatf1f1f872024-02-27 18:32:26 +0000149 fill_quantized(AccessorType(a), 3 + finfo.hash); // Fill tensors with new seed after run
150 fill_quantized(AccessorType(b), 4 + finfo.hash);
Ramy Elgammala77c6d72022-09-08 11:30:08 +0100151 if(is_fused)
152 {
Radu Salavatf1f1f872024-02-27 18:32:26 +0000153 fill_s32(AccessorType(bias), 5 + finfo.hash, finfo.min_bias, finfo.max_bias);
Ramy Elgammala77c6d72022-09-08 11:30:08 +0100154 }
155 }
156
George Wort2d7e6832019-02-22 16:37:41 +0000157 // Compute GEMM function
158 gemmlowp.run();
159 return output;
160}
161
Ramy Elgammala77c6d72022-09-08 11:30:08 +0100162template <bool reinterpret_input_as_3d, typename TI = uint8_t, typename TW = uint8_t, bool pretranspose_A = false, bool pretranspose_B = false, bool run_twice = false>
SiCong Li11ab4512023-11-07 12:04:59 +0000163SimpleTensor<int32_t> compute_gemmlowp_reference(const TensorShape &shape_a, const TensorShape &shape_b, const TensorShape &shape_output, const QuantizationInfo& a_qinfo, const QuantizationInfo& b_qinfo,
164 DataType data_type_a = DataType::QASYMM8, DataType data_type_b = DataType::QASYMM8, const TensorFillInfo& finfo = TensorFillInfo())
George Wort2d7e6832019-02-22 16:37:41 +0000165{
SiCong Li11ab4512023-11-07 12:04:59 +0000166 ARM_COMPUTE_ASSERT(is_data_type_quantized_asymmetric(data_type_a));
167 ARM_COMPUTE_ASSERT(data_type_a == data_type_b);
George Wort2d7e6832019-02-22 16:37:41 +0000168 TensorShape shape_a_to_use = shape_a;
169 if(reinterpret_input_as_3d)
170 {
171 // Collapse the second and third dimension if the input is 3D
172 shape_a_to_use.collapse(2U, 1U);
173 }
174
175 // Create reference
SiCong Li11ab4512023-11-07 12:04:59 +0000176 SimpleTensor<TI> a{ shape_a_to_use, data_type_a, 1, a_qinfo };
177 SimpleTensor<TW> b{ shape_b, data_type_b, 1, b_qinfo };
George Wort2d7e6832019-02-22 16:37:41 +0000178
Adnan AlSinanc5849582022-05-05 11:13:19 +0100179 TensorShape shape_a_to_use_transposed{ shape_a_to_use };
180 TensorShape shape_b_transposed{ shape_b };
181
182 shape_a_to_use_transposed.set(0, shape_a_to_use[1]);
183 shape_a_to_use_transposed.set(1, shape_a_to_use[0]);
184 shape_b_transposed.set(0, shape_b[1]);
185 shape_b_transposed.set(1, shape_b[0]);
186
SiCong Li11ab4512023-11-07 12:04:59 +0000187 SimpleTensor<TI> a_transposed{ shape_a_to_use_transposed, data_type_a, 1, a_qinfo };
188 SimpleTensor<TW> b_transposed{ shape_b_transposed, data_type_b, 1, b_qinfo };
Adnan AlSinanc5849582022-05-05 11:13:19 +0100189
George Wort2d7e6832019-02-22 16:37:41 +0000190 // Fill reference
Radu Salavatf1f1f872024-02-27 18:32:26 +0000191 fill_quantized(a, 0 + finfo.hash);
192 fill_quantized(b, 1 + finfo.hash);
Adnan AlSinanc5849582022-05-05 11:13:19 +0100193
194 // Transpose reference if required
Adnan AlSinan3bb72b62022-05-06 12:10:11 +0100195 /* Note: Assuming the usual batch matmul dimensions A = (B x M x K), B = (B x K x N), if pretranspose_A is set to true, then A is assumed to be (B x K x M),
196 therefore, A must be pre-transposed before passing it to the fixture. And, we transpose A again in the fixture to make it (B x M x K)
197 in order to be able to call reference implementation that works with (B x M x K) input.
198 Similarly, if pretranspose_B is set to true, then B is assumed to be (B x N x K), B must be pre-transposed before passing it to the fixture. */
Adnan AlSinanc5849582022-05-05 11:13:19 +0100199 if(pretranspose_A)
200 {
201 transpose_matrix<TI>(a, a_transposed);
202 }
203
204 if(pretranspose_B)
205 {
206 transpose_matrix<TW>(b, b_transposed);
207 }
208
Ramy Elgammala77c6d72022-09-08 11:30:08 +0100209 // Run with variable inputs.
SiCong Li11ab4512023-11-07 12:04:59 +0000210 const int32_t a_offset = a_qinfo.uniform().offset;
211 const int32_t b_offset = b_qinfo.uniform().offset;
Radu Salavatf1f1f872024-02-27 18:32:26 +0000212
Ramy Elgammala77c6d72022-09-08 11:30:08 +0100213 if(run_twice)
214 {
215 reference::gemmlowp_matrix_multiply_core<int32_t, TI, TW>((pretranspose_A ? a_transposed : a), (pretranspose_B ? b_transposed : b), shape_output, a_offset, b_offset);
Radu Salavatf1f1f872024-02-27 18:32:26 +0000216 fill_quantized((pretranspose_A) ? a_transposed : a, 3 + finfo.hash);
217 fill_quantized((pretranspose_B) ? b_transposed : b, 4 + finfo.hash);
Ramy Elgammala77c6d72022-09-08 11:30:08 +0100218 }
219
Adnan AlSinanc5849582022-05-05 11:13:19 +0100220 return reference::gemmlowp_matrix_multiply_core<int32_t, TI, TW>((pretranspose_A ? a_transposed : a), (pretranspose_B ? b_transposed : b), shape_output, a_offset, b_offset);
George Wort2d7e6832019-02-22 16:37:41 +0000221}
SiCong Li11ab4512023-11-07 12:04:59 +0000222} // namespace
George Wort2d7e6832019-02-22 16:37:41 +0000223
Ramy Elgammala77c6d72022-09-08 11:30:08 +0100224template <typename TensorType, typename AccessorType, typename FunctionType, bool reinterpret_input_as_3d = false, bool reinterpret_output_as_3d = false, bool run_twice = false>
Radu Salavatf1f1f872024-02-27 18:32:26 +0000225class GEMMLowpGenericMatrixMultiplyCoreValidationFixture : public framework::Fixture
Pablo Tello299025a2017-09-29 11:30:12 +0100226{
227public:
Radu Salavatf1f1f872024-02-27 18:32:26 +0000228 void setup(TensorShape shape_a, TensorShape shape_b, TensorShape shape_output, int32_t a_offset, int32_t b_offset, bool accumulate=false)
Pablo Tello299025a2017-09-29 11:30:12 +0100229 {
SiCong Li11ab4512023-11-07 12:04:59 +0000230 const auto a_qinfo = QuantizationInfo(1.0f / 255, a_offset);
231 const auto b_qinfo = QuantizationInfo(1.0f / 255, b_offset);
Radu Salavatf1f1f872024-02-27 18:32:26 +0000232 TensorFillInfo finfo;
233 _target = compute_target(shape_a, shape_b, shape_output, a_qinfo, b_qinfo, finfo, accumulate);
234 _reference = compute_reference(shape_a, shape_b, shape_output, a_qinfo, b_qinfo, finfo, accumulate);
Pablo Tello299025a2017-09-29 11:30:12 +0100235 }
236
237protected:
Radu Salavatf1f1f872024-02-27 18:32:26 +0000238 TensorType compute_target(const TensorShape &shape_a, const TensorShape &shape_b, const TensorShape &shape_output, const QuantizationInfo& a_qinfo, const QuantizationInfo& b_qinfo, const TensorFillInfo& finfo, const bool accumulate)
Pablo Tello299025a2017-09-29 11:30:12 +0100239 {
SiCong Li11ab4512023-11-07 12:04:59 +0000240 const auto output_qinfo = QuantizationInfo(); // No output stage
Radu Salavatf1f1f872024-02-27 18:32:26 +0000241 return compute_gemmlowp_target<TensorType, AccessorType, FunctionType, reinterpret_input_as_3d, reinterpret_output_as_3d, int32_t, false, run_twice>(shape_a, shape_b, shape_output, a_qinfo, b_qinfo, output_qinfo,
242 DataType::QASYMM8, DataType::QASYMM8, GEMMLowpOutputStageInfo(), false, finfo, accumulate);
Pablo Tello299025a2017-09-29 11:30:12 +0100243 }
244
Radu Salavatf1f1f872024-02-27 18:32:26 +0000245 SimpleTensor<int32_t> compute_reference(const TensorShape &shape_a, const TensorShape &shape_b, const TensorShape &shape_output, const QuantizationInfo& a_qinfo, const QuantizationInfo& b_qinfo, const TensorFillInfo& finfo, bool accumulate)
Pablo Tello299025a2017-09-29 11:30:12 +0100246 {
Radu Salavatf1f1f872024-02-27 18:32:26 +0000247 SimpleTensor<int32_t> ref_output = compute_gemmlowp_reference<reinterpret_input_as_3d, uint8_t, uint8_t, false, false, run_twice>(shape_a, shape_b, shape_output, a_qinfo, b_qinfo,
248 DataType::QASYMM8, DataType::QASYMM8, finfo);
249
250 if (accumulate)
251 {
252 SimpleTensor<int32_t> output{ shape_output, DataType::S32, 1 };
253 fill_s32(output, 6 + finfo.hash, finfo.min_output, finfo.max_output);
254 reference::arithmetic_operation<int32_t>(reference::ArithmeticOperation::ADD, output, ref_output, output, ConvertPolicy::SATURATE);
255 return output;
256 }
257
258 return ref_output;
Pablo Tellobf2fb952017-09-29 16:43:25 +0100259 }
260
Pablo Tello6ff12a02017-11-02 16:09:35 +0000261 TensorType _target{};
262 SimpleTensor<int32_t> _reference{};
Pablo Tellobf2fb952017-09-29 16:43:25 +0100263};
264
Radu Salavatf1f1f872024-02-27 18:32:26 +0000265template <typename TensorType, typename AccessorType, typename FunctionType, bool reinterpret_input_as_3d = false, bool reinterpret_output_as_3d = false, bool run_twice = false>
266class GEMMLowpMatrixMultiplyCoreValidationFixture : protected GEMMLowpGenericMatrixMultiplyCoreValidationFixture<TensorType, AccessorType, FunctionType, reinterpret_input_as_3d, reinterpret_output_as_3d, run_twice>
267{
268public:
269 void setup(TensorShape shape_a, TensorShape shape_b, TensorShape shape_output, int32_t a_offset, int32_t b_offset)
270 {
271 GEMMLowpGenericMatrixMultiplyCoreValidationFixture<TensorType, AccessorType, FunctionType, reinterpret_input_as_3d, reinterpret_output_as_3d, run_twice>::setup(shape_a, shape_b, shape_output, a_offset, b_offset, false /* accumulate */);
272 }
273};
274
275template <typename TensorType, typename AccessorType, typename FunctionType, bool reinterpret_input_as_3d = false, bool reinterpret_output_as_3d = false, bool run_twice = false>
276class GEMMLowpMatrixMultiplyAccumulateValidationFixture : protected GEMMLowpGenericMatrixMultiplyCoreValidationFixture<TensorType, AccessorType, FunctionType, reinterpret_input_as_3d, reinterpret_output_as_3d, run_twice>
277{
278public:
279 void setup(TensorShape shape_a, TensorShape shape_b, TensorShape shape_output, int32_t a_offset, int32_t b_offset)
280 {
281 GEMMLowpGenericMatrixMultiplyCoreValidationFixture<TensorType, AccessorType, FunctionType, reinterpret_input_as_3d, reinterpret_output_as_3d, run_twice>::setup(shape_a, shape_b, shape_output, a_offset, b_offset, true /* accumulate */);
282 }
283};
284
Mohammed Suhail Munshi97a609b2022-10-21 11:15:54 +0100285template <typename TensorType, typename AccessorType, typename FunctionType, bool reinterpret_input_as_3d = false, bool reinterpret_output_as_3d = false, typename TI = uint8_t, typename TW = uint8_t, bool run_twice = false>
Radu Salavatf1f1f872024-02-27 18:32:26 +0000286class GEMMLowpGenericMatrixMultiplyCoreFusedOffsetOutputValidationFixture : public framework::Fixture
George Wort2d7e6832019-02-22 16:37:41 +0000287{
288public:
SiCong Li11ab4512023-11-07 12:04:59 +0000289 /** Dynamically initialize the quantization info with saturation awareness
290 */
291 template <typename T>
292 static void setup_quantization(DataType data_type, const TensorShape& shape_a, const TensorShape& shape_b, QuantizationInfo& a_qinfo, QuantizationInfo& b_qinfo, QuantizationInfo& output_qinfo, TensorFillInfo& finfo)
293 {
294 // This hash is used by random generators. There may be hash collisions but
295 // this is intentional as it's a very easy way to make the the current
296 // random generation process almost different for many test configurations,
297 // which were using the same set of values before.
298 finfo.hash = shape_a[0] + shape_a[1] + shape_b[0] + shape_b[1];
299
300 const int32_t t_max = static_cast<int32_t>(std::numeric_limits<T>::max());
301 const int32_t t_min = static_cast<int32_t>(std::numeric_limits<T>::min());
302
303 std::mt19937 generator(library->seed() + finfo.hash);
304 std::uniform_real_distribution<float> distribution_float(-5.0f, 3.0f);
305 std::uniform_int_distribution<int32_t> distribution_t(t_min, t_max);
306
307 const float scale_lhs = pow(2, distribution_float(generator)); // [2^-5, 2^3]
308 const float scale_rhs = pow(2, distribution_float(generator)); // [2^-5, 2^3]
309
310 const int32_t offset_lhs = distribution_t(generator);
311 const int32_t offset_rhs = distribution_t(generator);
312
313 a_qinfo = QuantizationInfo(scale_lhs, offset_lhs);
314 b_qinfo = QuantizationInfo(scale_rhs, offset_rhs);
315
316 // reinterpret_input_as_3d or reinterpret_output_as_3d can be ignored, as the underlying gemm / matmul computation
317 // is equivalent to a standard 2D one with m-n-k dimensions
318 const int m = shape_a.y();
319 const int n = shape_b.x();
320 const int k = shape_a.x();
321
322 const float bias_fraction = 0.5f; // We enabled is_fused in compute_gemmlowp_target below, thus bias is included
323
324 QuantizationHint q_hint = suggest_matmul_dst_q_info_and_bias(a_qinfo, b_qinfo, m, n, k, data_type, bias_fraction);
325 output_qinfo = q_hint.q_info;
326 finfo.min_bias = q_hint.bias_min;
327 finfo.max_bias = q_hint.bias_max;
328
329 // Both target and reference implementations use negated offsets, i.e.
330 // float_val = (int_val + offset) * scale
331 // instead of
332 // float_val = (int_val - offset) * scale
333 // as usual. Therefore, after calculating the output quantization above, we
334 // negate the offsets of inputs' offsets.
335 a_qinfo = QuantizationInfo(scale_lhs, -offset_lhs);
336 b_qinfo = QuantizationInfo(scale_rhs, -offset_rhs);
337 }
338
339 /** Initialize output stage info from quantization info */
340 static Status init_gemmlowp_output_stage_info(
341 DataType data_type,
342 const QuantizationInfo& a_qinfo,
343 const QuantizationInfo& b_qinfo,
344 const QuantizationInfo& output_qinfo,
345 GEMMLowpOutputStageType type,
346 GEMMLowpOutputStageInfo &gemmlowp_output_stage_info)
347 {
348 ARM_COMPUTE_RETURN_ERROR_ON(!is_data_type_quantized_asymmetric(data_type));
349
350 const UniformQuantizationInfo aq_unif = a_qinfo.uniform();
351 const UniformQuantizationInfo bq_unif = b_qinfo.uniform();
352 const UniformQuantizationInfo oq_unif = output_qinfo.uniform();
353
354 float multiplier = (aq_unif.scale * bq_unif.scale) / oq_unif.scale;
355 int32_t int_multiplier;
356 int32_t shift;
357
358 ARM_COMPUTE_RETURN_ON_ERROR(
359 quantization::calculate_quantized_multiplier(multiplier, &int_multiplier, &shift));
360
361 int32_t type_min = 0;
362 int32_t type_max = 0;
363 std::tie(type_min, type_max) = quantization::get_quantized_asymmetric_output_min_max(output_qinfo, ActivationLayerInfo(), data_type);
364
365 gemmlowp_output_stage_info.gemmlowp_real_multiplier = multiplier;
366 gemmlowp_output_stage_info.gemmlowp_multiplier = int_multiplier;
367 gemmlowp_output_stage_info.gemmlowp_multipliers = { int_multiplier };
368 gemmlowp_output_stage_info.gemmlowp_shift = shift;
369 gemmlowp_output_stage_info.gemmlowp_shifts = { shift };
370 gemmlowp_output_stage_info.gemmlowp_offset = oq_unif.offset;
371 gemmlowp_output_stage_info.type = type;
372 gemmlowp_output_stage_info.gemmlowp_min_bound = type_min;
373 gemmlowp_output_stage_info.gemmlowp_max_bound = type_max;
374
375 return Status{};
376 }
377
378 /** Currently this fixture only tests the following data type configurations:
379 *
380 * 1. a and b are of the same data type
381 * 2. The data type is quantized asymmetric
382 *
383 */
384 void setup(TensorShape shape_a, TensorShape shape_b, TensorShape shape_output, GEMMLowpOutputStageType output_stage_type, DataType data_type,
Giorgio Arena5f6fdc12021-06-09 15:23:06 +0100385 bool reshape_b_only_on_first_run)
George Wort2d7e6832019-02-22 16:37:41 +0000386 {
SiCong Li11ab4512023-11-07 12:04:59 +0000387 ARM_COMPUTE_ASSERT(output_stage_type != GEMMLowpOutputStageType::NONE);
388 ARM_COMPUTE_ASSERT(is_data_type_quantized_asymmetric(data_type));
Manuel Bottini959c26d2019-12-02 16:22:35 +0000389
SiCong Li11ab4512023-11-07 12:04:59 +0000390 // Randomized dynamic quantization: randomize quantization info in a way that ensures no result saturation
391 // most of the time
392 QuantizationInfo a_qinfo;
393 QuantizationInfo b_qinfo;
394 QuantizationInfo output_qinfo;
395 TensorFillInfo finfo;
396 setup_quantization<TI>(data_type, shape_a, shape_b, a_qinfo, b_qinfo, output_qinfo, finfo);
Vidhya Sudhan Loganathan951b8a42019-11-04 14:42:08 +0000397
SiCong Li11ab4512023-11-07 12:04:59 +0000398 GEMMLowpOutputStageInfo output_stage;
399 init_gemmlowp_output_stage_info(data_type, a_qinfo, b_qinfo, output_qinfo, output_stage_type, output_stage);
400
401 _reference = compute_reference(shape_a, shape_b, shape_output, a_qinfo, b_qinfo, data_type, data_type, output_stage, finfo);
402 _target = compute_target(shape_a, shape_b, shape_output, a_qinfo, b_qinfo, output_qinfo, data_type, data_type, output_stage, reshape_b_only_on_first_run, finfo);
George Wort2d7e6832019-02-22 16:37:41 +0000403 }
404
405protected:
SiCong Li11ab4512023-11-07 12:04:59 +0000406 TensorType compute_target(const TensorShape &shape_a, const TensorShape &shape_b, const TensorShape &shape_output, const QuantizationInfo& a_qinfo, const QuantizationInfo& b_qinfo, const QuantizationInfo& output_qinfo,
407 DataType data_type_a, DataType data_type_b, const GEMMLowpOutputStageInfo& output_stage, bool reshape_b_only_on_first_run = false, const TensorFillInfo& finfo = TensorFillInfo())
George Wort2d7e6832019-02-22 16:37:41 +0000408 {
SiCong Li11ab4512023-11-07 12:04:59 +0000409 return compute_gemmlowp_target<TensorType, AccessorType, FunctionType, reinterpret_input_as_3d, reinterpret_output_as_3d, qasymm8_t, true, run_twice>(shape_a, shape_b, shape_output, a_qinfo,
410 b_qinfo, output_qinfo, data_type_a, data_type_b, output_stage, reshape_b_only_on_first_run, finfo);
George Wort2d7e6832019-02-22 16:37:41 +0000411 }
412
SiCong Li11ab4512023-11-07 12:04:59 +0000413 SimpleTensor<TI> compute_reference(const TensorShape &shape_a, const TensorShape &shape_b, const TensorShape &shape_output, const QuantizationInfo& a_qinfo, const QuantizationInfo& b_qinfo,
414 DataType data_type_a, DataType data_type_b, const GEMMLowpOutputStageInfo& output_stage, const TensorFillInfo& finfo = TensorFillInfo())
George Wort2d7e6832019-02-22 16:37:41 +0000415 {
SiCong Li11ab4512023-11-07 12:04:59 +0000416 SimpleTensor<int32_t> output = compute_gemmlowp_reference<reinterpret_input_as_3d, TI, TW, false, false, run_twice>(shape_a, shape_b, shape_output, a_qinfo, b_qinfo, data_type_a, data_type_b, finfo);
George Wort2d7e6832019-02-22 16:37:41 +0000417
418 TensorShape bias_shape(shape_b[0]);
419 SimpleTensor<int32_t> bias{ bias_shape, DataType::S32, 1 };
Radu Salavatf1f1f872024-02-27 18:32:26 +0000420 (run_twice) ? fill_s32(bias, 5 + finfo.hash, finfo.min_bias, finfo.max_bias) : fill_s32(bias, 2 + finfo.hash, finfo.min_bias, finfo.max_bias); // Fill bias with same seed as last run of gemmlowp_target
George Wort2d7e6832019-02-22 16:37:41 +0000421
422 switch(output_stage.type)
423 {
424 case GEMMLowpOutputStageType::QUANTIZE_DOWN:
Radu Salavatf1f1f872024-02-27 18:32:26 +0000425 return reference::gemmlowp_quantize_down_scale<int32_t, TI>(output, bias,
Manuel Bottini959c26d2019-12-02 16:22:35 +0000426 output_stage.gemmlowp_offset, output_stage.gemmlowp_multipliers, output_stage.gemmlowp_shifts, output_stage.gemmlowp_min_bound, output_stage.gemmlowp_max_bound);
George Wort2d7e6832019-02-22 16:37:41 +0000427 break;
428 case GEMMLowpOutputStageType::QUANTIZE_DOWN_FIXEDPOINT:
Radu Salavatf1f1f872024-02-27 18:32:26 +0000429 return reference::gemmlowp_quantize_down_scale_by_fixedpoint<int32_t, TI>(output, bias,
Manuel Bottini959c26d2019-12-02 16:22:35 +0000430 output_stage.gemmlowp_multipliers, output_stage.gemmlowp_shifts, output_stage.gemmlowp_offset, output_stage.gemmlowp_min_bound, output_stage.gemmlowp_max_bound);
George Wort2d7e6832019-02-22 16:37:41 +0000431 break;
432 default:
433 ARM_COMPUTE_ERROR("Not Supported!");
434 }
435 }
436
Manuel Bottini959c26d2019-12-02 16:22:35 +0000437 TensorType _target{};
438 SimpleTensor<TI> _reference{};
George Wort2d7e6832019-02-22 16:37:41 +0000439};
440
Radu Salavatf1f1f872024-02-27 18:32:26 +0000441template <typename TensorType, typename AccessorType, typename FunctionType, bool reinterpret_input_as_3d = false, bool reinterpret_output_as_3d = false, typename TI = uint8_t, typename TW = uint8_t, bool run_twice = false>
442class GEMMLowpMatrixMultiplyCoreFusedOffsetOutputValidationFixture : public GEMMLowpGenericMatrixMultiplyCoreFusedOffsetOutputValidationFixture<TensorType, AccessorType, FunctionType, reinterpret_input_as_3d, reinterpret_output_as_3d, TI, TW, run_twice>
Giorgio Arena5f6fdc12021-06-09 15:23:06 +0100443{
444public:
Radu Salavatf1f1f872024-02-27 18:32:26 +0000445 void setup(TensorShape shape_a, TensorShape shape_b, TensorShape shape_output, GEMMLowpOutputStageType output_stage_type, DataType data_type, bool reshape_b_only_on_first_run)
Giorgio Arena5f6fdc12021-06-09 15:23:06 +0100446 {
Radu Salavatf1f1f872024-02-27 18:32:26 +0000447 GEMMLowpGenericMatrixMultiplyCoreFusedOffsetOutputValidationFixture<TensorType, AccessorType, FunctionType, reinterpret_input_as_3d, reinterpret_output_as_3d, TI, TW, run_twice>::setup(shape_a, shape_b,
448 shape_output, output_stage_type, data_type, reshape_b_only_on_first_run);
449 }
450};
451
452template <typename TensorType, typename AccessorType, typename FunctionType, bool reinterpret_input_as_3d = false, bool reinterpret_output_as_3d = false, typename TI = uint8_t, typename TW = uint8_t, bool run_twice = false>
453class GEMMLowpBatchedMatrixMultiplyCoreFusedOffsetOutputFixture : public GEMMLowpGenericMatrixMultiplyCoreFusedOffsetOutputValidationFixture<TensorType, AccessorType, FunctionType, reinterpret_input_as_3d, reinterpret_output_as_3d, TI, TW, run_twice>
454{
455public:
456 void setup(TensorShape shape_a, TensorShape shape_b, TensorShape shape_output, GEMMLowpOutputStageType output_stage_type, DataType data_type, bool reshape_b_only_on_first_run)
457 {
458 GEMMLowpGenericMatrixMultiplyCoreFusedOffsetOutputValidationFixture<TensorType, AccessorType, FunctionType, reinterpret_input_as_3d, reinterpret_output_as_3d, TI, TW, run_twice>::setup(shape_a, shape_b, shape_output, output_stage_type, data_type, reshape_b_only_on_first_run);
Giorgio Arena5f6fdc12021-06-09 15:23:06 +0100459 }
460};
461
Gian Marcoe75a02b2017-11-08 12:24:09 +0000462template <typename TensorType, typename AccessorType, typename FunctionType>
463class GEMMLowpQuantizeDownInt32ToUint8ScaleValidationFixture : public framework::Fixture
464{
465public:
Gian Marco6b77e912017-11-17 09:27:57 +0000466 void setup(TensorShape shape, int32_t result_offset, int32_t result_mult_int, int32_t result_shift, int32_t min, int32_t max, bool add_bias)
Gian Marcoe75a02b2017-11-08 12:24:09 +0000467 {
Gian Marco6b77e912017-11-17 09:27:57 +0000468 _target = compute_target(shape, result_offset, result_mult_int, result_shift, min, max, add_bias);
469 _reference = compute_reference(shape, result_offset, result_mult_int, result_shift, min, max, add_bias);
Gian Marcoe75a02b2017-11-08 12:24:09 +0000470 }
471
472protected:
473 template <typename U>
474 void fill(U &&tensor, int i)
475 {
476 std::uniform_int_distribution<> distribution(-6000, 6000);
477 library->fill(tensor, distribution, i);
478 }
479
Gian Marco6b77e912017-11-17 09:27:57 +0000480 TensorType compute_target(const TensorShape &shape, int32_t result_offset, int32_t result_mult_int, int32_t result_shift, int32_t min, int32_t max, bool add_bias)
Gian Marcoe75a02b2017-11-08 12:24:09 +0000481 {
Gian Marco6b77e912017-11-17 09:27:57 +0000482 TensorShape shape_bias(shape[0]);
483
Gian Marcoe75a02b2017-11-08 12:24:09 +0000484 // Create tensors
485 TensorType a = create_tensor<TensorType>(shape, DataType::S32, 1);
Gian Marco6b77e912017-11-17 09:27:57 +0000486 TensorType b = create_tensor<TensorType>(shape_bias, DataType::S32, 1);
487 TensorType c = create_tensor<TensorType>(shape, DataType::QASYMM8, 1);
Gian Marcoe75a02b2017-11-08 12:24:09 +0000488
489 // Create and configure function
Luca Foschiani4b869532020-02-13 15:07:36 +0000490 FunctionType output_stage;
491 GEMMLowpOutputStageInfo output_stage_info = GEMMLowpOutputStageInfo();
492 output_stage_info.type = GEMMLowpOutputStageType::QUANTIZE_DOWN;
493 output_stage_info.gemmlowp_offset = result_offset;
494 output_stage_info.gemmlowp_multiplier = result_mult_int;
495 output_stage_info.gemmlowp_shift = result_shift;
496 output_stage_info.gemmlowp_min_bound = min;
497 output_stage_info.gemmlowp_max_bound = max;
498 output_stage_info.output_data_type = DataType::QASYMM8;
499 output_stage.configure(&a, add_bias ? &b : nullptr, &c, output_stage_info);
Gian Marcoe75a02b2017-11-08 12:24:09 +0000500
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100501 ARM_COMPUTE_ASSERT(a.info()->is_resizable());
502 ARM_COMPUTE_ASSERT(c.info()->is_resizable());
Gian Marcoe75a02b2017-11-08 12:24:09 +0000503
504 // Allocate tensors
505 a.allocator()->allocate();
Gian Marco6b77e912017-11-17 09:27:57 +0000506 c.allocator()->allocate();
Gian Marcoe75a02b2017-11-08 12:24:09 +0000507
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100508 ARM_COMPUTE_ASSERT(!a.info()->is_resizable());
509 ARM_COMPUTE_ASSERT(!c.info()->is_resizable());
Gian Marcoe75a02b2017-11-08 12:24:09 +0000510
Gian Marco6b77e912017-11-17 09:27:57 +0000511 // Fill tensor
Gian Marcoe75a02b2017-11-08 12:24:09 +0000512 fill(AccessorType(a), 0);
513
Gian Marco6b77e912017-11-17 09:27:57 +0000514 if(add_bias)
515 {
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100516 ARM_COMPUTE_ASSERT(b.info()->is_resizable());
Gian Marco6b77e912017-11-17 09:27:57 +0000517
518 // Allocate bias tensor
519 b.allocator()->allocate();
520
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100521 ARM_COMPUTE_ASSERT(!b.info()->is_resizable());
Gian Marco6b77e912017-11-17 09:27:57 +0000522
523 // Fill tensor
524 fill(AccessorType(b), 1);
525 }
526
Gian Marcoe75a02b2017-11-08 12:24:09 +0000527 // Compute GEMM function
528 output_stage.run();
Gian Marco6b77e912017-11-17 09:27:57 +0000529 return c;
Gian Marcoe75a02b2017-11-08 12:24:09 +0000530 }
531
Gian Marco6b77e912017-11-17 09:27:57 +0000532 SimpleTensor<uint8_t> compute_reference(const TensorShape &shape, int32_t result_offset, int32_t result_mult_int, int32_t result_shift, int32_t min, int32_t max, bool add_bias)
Gian Marcoe75a02b2017-11-08 12:24:09 +0000533 {
534 // Create reference
Gian Marco6b77e912017-11-17 09:27:57 +0000535 TensorShape shape_bias(shape[0]);
536
Gian Marcoe75a02b2017-11-08 12:24:09 +0000537 SimpleTensor<int32_t> a{ shape, DataType::S32, 1 };
Gian Marco6b77e912017-11-17 09:27:57 +0000538 SimpleTensor<int32_t> b{ shape_bias, DataType::S32, 1 };
Gian Marcoe75a02b2017-11-08 12:24:09 +0000539
540 // Fill reference
541 fill(a, 0);
542
Vidhya Sudhan Loganathan951b8a42019-11-04 14:42:08 +0000543 const std::vector<int32_t> result_mult_int_vec = { result_mult_int };
544 const std::vector<int32_t> result_shift_vec = { result_shift };
545
Gian Marco6b77e912017-11-17 09:27:57 +0000546 if(add_bias)
547 {
548 // Fill bias
549 fill(b, 1);
550
Manuel Bottini959c26d2019-12-02 16:22:35 +0000551 return reference::gemmlowp_quantize_down_scale<int32_t, uint8_t>(a, b, result_offset, result_mult_int_vec, result_shift_vec, min, max);
Gian Marco6b77e912017-11-17 09:27:57 +0000552 }
553 else
554 {
Manuel Bottini959c26d2019-12-02 16:22:35 +0000555 return reference::gemmlowp_quantize_down_scale<int32_t, uint8_t>(a, result_offset, result_mult_int_vec, result_shift_vec, min, max);
Gian Marco6b77e912017-11-17 09:27:57 +0000556 }
Gian Marcoe75a02b2017-11-08 12:24:09 +0000557 }
558
559 TensorType _target{};
560 SimpleTensor<uint8_t> _reference{};
561};
Gian Marco58c57942017-11-28 09:10:03 +0000562
563template <typename TensorType, typename AccessorType, typename FunctionType>
Luca Foschiani4b869532020-02-13 15:07:36 +0000564class GEMMLowpQuantizeDownInt32ToInt8ScaleValidationFixture : public framework::Fixture
565{
566public:
Luca Foschiani4b869532020-02-13 15:07:36 +0000567 void setup(TensorShape shape, int32_t result_offset, int32_t result_mult_int, int32_t result_shift, int32_t min, int32_t max, bool add_bias)
568 {
569 _target = compute_target(shape, result_offset, result_mult_int, result_shift, min, max, add_bias);
570 _reference = compute_reference(shape, result_offset, result_mult_int, result_shift, min, max, add_bias);
571 }
572
573protected:
574 template <typename U>
575 void fill(U &&tensor, int i)
576 {
577 std::uniform_int_distribution<> distribution(-6000, 6000);
578 library->fill(tensor, distribution, i);
579 }
580
581 TensorType compute_target(const TensorShape &shape, int32_t result_offset, int32_t result_mult_int, int32_t result_shift, int32_t min, int32_t max, bool add_bias)
582 {
583 TensorShape shape_bias(shape[0]);
584
585 // Create tensors
586 TensorType a = create_tensor<TensorType>(shape, DataType::S32, 1);
587 TensorType b = create_tensor<TensorType>(shape_bias, DataType::S32, 1);
588 TensorType c = create_tensor<TensorType>(shape, DataType::QASYMM8_SIGNED, 1);
589
590 // Create and configure function
591 FunctionType output_stage;
592 GEMMLowpOutputStageInfo output_stage_info = GEMMLowpOutputStageInfo();
593 output_stage_info.type = GEMMLowpOutputStageType::QUANTIZE_DOWN;
594 output_stage_info.gemmlowp_offset = result_offset;
595 output_stage_info.gemmlowp_multiplier = result_mult_int;
596 output_stage_info.gemmlowp_shift = result_shift;
597 output_stage_info.gemmlowp_min_bound = min;
598 output_stage_info.gemmlowp_max_bound = max;
599 output_stage_info.output_data_type = DataType::QASYMM8_SIGNED;
600 output_stage.configure(&a, add_bias ? &b : nullptr, &c, output_stage_info);
601
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100602 ARM_COMPUTE_ASSERT(a.info()->is_resizable());
603 ARM_COMPUTE_ASSERT(c.info()->is_resizable());
Luca Foschiani4b869532020-02-13 15:07:36 +0000604
605 // Allocate tensors
606 a.allocator()->allocate();
607 c.allocator()->allocate();
608
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100609 ARM_COMPUTE_ASSERT(!a.info()->is_resizable());
610 ARM_COMPUTE_ASSERT(!c.info()->is_resizable());
Luca Foschiani4b869532020-02-13 15:07:36 +0000611
612 // Fill tensor
613 fill(AccessorType(a), 0);
614
615 if(add_bias)
616 {
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100617 ARM_COMPUTE_ASSERT(b.info()->is_resizable());
Luca Foschiani4b869532020-02-13 15:07:36 +0000618
619 // Allocate bias tensor
620 b.allocator()->allocate();
621
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100622 ARM_COMPUTE_ASSERT(!b.info()->is_resizable());
Luca Foschiani4b869532020-02-13 15:07:36 +0000623
624 // Fill tensor
625 fill(AccessorType(b), 1);
626 }
627
628 // Compute GEMM function
629 output_stage.run();
630 return c;
631 }
632
633 SimpleTensor<int8_t> compute_reference(const TensorShape &shape, int32_t result_offset, int32_t result_mult_int, int32_t result_shift, int32_t min, int32_t max, bool add_bias)
634 {
635 // Create reference
636 TensorShape shape_bias(shape[0]);
637
638 SimpleTensor<int32_t> a{ shape, DataType::S32, 1 };
639 SimpleTensor<int32_t> b{ shape_bias, DataType::S32, 1 };
640
641 // Fill reference
642 fill(a, 0);
643
644 const std::vector<int32_t> result_mult_int_vec = { result_mult_int };
645 const std::vector<int32_t> result_shift_vec = { result_shift };
646
647 if(add_bias)
648 {
649 // Fill bias
650 fill(b, 1);
651
652 return reference::gemmlowp_quantize_down_scale<int32_t, int8_t>(a, b, result_offset, result_mult_int_vec, result_shift_vec, min, max);
653 }
654 else
655 {
656 return reference::gemmlowp_quantize_down_scale<int32_t, int8_t>(a, result_offset, result_mult_int_vec, result_shift_vec, min, max);
657 }
658 }
659
660 TensorType _target{};
661 SimpleTensor<int8_t> _reference{};
662};
663
664template <typename TensorType, typename AccessorType, typename FunctionType>
Georgios Pinitas448a81f2019-11-21 14:10:25 +0000665class GEMMLowpQuantizeDownInt32ToInt8ScaleByFixedPointValidationFixture : public framework::Fixture
666{
667public:
Georgios Pinitas448a81f2019-11-21 14:10:25 +0000668 void setup(TensorShape shape, int32_t result_fixedpoint_multiplier, int32_t result_shift, int32_t result_offset_after_shift, int32_t min, int32_t max, bool add_bias)
669 {
670 _target = compute_target(shape, result_fixedpoint_multiplier, result_shift, result_offset_after_shift, min, max, add_bias);
671 _reference = compute_reference(shape, result_fixedpoint_multiplier, result_shift, result_offset_after_shift, min, max, add_bias);
672 }
673
674protected:
675 template <typename U>
676 void fill(U &&tensor, int i)
677 {
678 std::uniform_int_distribution<> distribution(-6000, 6000);
679 library->fill(tensor, distribution, i);
680 }
681
682 TensorType compute_target(const TensorShape &shape, int32_t result_fixedpoint_multiplier, int32_t result_shift, int32_t result_offset_after_shift, int32_t min, int32_t max, bool add_bias)
683 {
684 TensorShape shape_bias(shape[0]);
685
686 // Create tensors
687 TensorType a = create_tensor<TensorType>(shape, DataType::S32, 1);
688 TensorType b = create_tensor<TensorType>(shape_bias, DataType::S32, 1);
689 TensorType c = create_tensor<TensorType>(shape, DataType::QASYMM8_SIGNED, 1);
690
691 // Create and configure function
692 FunctionType output_stage;
693 output_stage.configure(&a, add_bias ? &b : nullptr, &c, result_fixedpoint_multiplier, result_shift, result_offset_after_shift, min, max);
694
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100695 ARM_COMPUTE_ASSERT(a.info()->is_resizable());
696 ARM_COMPUTE_ASSERT(c.info()->is_resizable());
Georgios Pinitas448a81f2019-11-21 14:10:25 +0000697
698 // Allocate tensors
699 a.allocator()->allocate();
700 c.allocator()->allocate();
701
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100702 ARM_COMPUTE_ASSERT(!a.info()->is_resizable());
703 ARM_COMPUTE_ASSERT(!c.info()->is_resizable());
Georgios Pinitas448a81f2019-11-21 14:10:25 +0000704
705 // Fill tensor
706 fill(AccessorType(a), 0);
707
708 if(add_bias)
709 {
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100710 ARM_COMPUTE_ASSERT(b.info()->is_resizable());
Georgios Pinitas448a81f2019-11-21 14:10:25 +0000711
712 // Allocate bias tensor
713 b.allocator()->allocate();
714
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100715 ARM_COMPUTE_ASSERT(!b.info()->is_resizable());
Georgios Pinitas448a81f2019-11-21 14:10:25 +0000716
717 // Fill tensor
718 fill(AccessorType(b), 1);
719 }
720
721 // Compute GEMM function
722 output_stage.run();
723 return c;
724 }
725
726 SimpleTensor<int8_t> compute_reference(const TensorShape &shape, int32_t result_fixed_point_multiplier, int32_t result_shift, int32_t result_offset_after_shift, int32_t min, int32_t max,
727 bool add_bias)
728 {
729 // Create reference
730 TensorShape shape_bias(shape[0]);
731
732 SimpleTensor<int32_t> a{ shape, DataType::S32, 1 };
733 SimpleTensor<int32_t> b{ shape_bias, DataType::S32, 1 };
734
735 // Fill reference
736 fill(a, 0);
737
738 const std::vector<int32_t> result_fixed_point_multiplier_vec = { result_fixed_point_multiplier };
739 const std::vector<int32_t> result_shift_vec = { result_shift };
740
741 if(add_bias)
742 {
743 // Fill bias
744 fill(b, 1);
745
746 return reference::gemmlowp_quantize_down_scale_by_fixedpoint<int32_t, int8_t>(a, b, result_fixed_point_multiplier_vec, result_shift_vec, result_offset_after_shift, min, max);
747 }
748 else
749 {
750 return reference::gemmlowp_quantize_down_scale_by_fixedpoint<int32_t, int8_t>(a, result_fixed_point_multiplier_vec, result_shift_vec, result_offset_after_shift, min, max);
751 }
752 }
753
754 TensorType _target{};
755 SimpleTensor<int8_t> _reference{};
756};
757
758template <typename TensorType, typename AccessorType, typename FunctionType>
Gian Marco58c57942017-11-28 09:10:03 +0000759class GEMMLowpQuantizeDownInt32ToUint8ScaleByFixedPointValidationFixture : public framework::Fixture
760{
761public:
Gian Marco58c57942017-11-28 09:10:03 +0000762 void setup(TensorShape shape, int32_t result_fixedpoint_multiplier, int32_t result_shift, int32_t result_offset_after_shift, int32_t min, int32_t max, bool add_bias)
763 {
764 _target = compute_target(shape, result_fixedpoint_multiplier, result_shift, result_offset_after_shift, min, max, add_bias);
765 _reference = compute_reference(shape, result_fixedpoint_multiplier, result_shift, result_offset_after_shift, min, max, add_bias);
766 }
767
768protected:
769 template <typename U>
770 void fill(U &&tensor, int i)
771 {
772 std::uniform_int_distribution<> distribution(-6000, 6000);
773 library->fill(tensor, distribution, i);
774 }
775
776 TensorType compute_target(const TensorShape &shape, int32_t result_fixedpoint_multiplier, int32_t result_shift, int32_t result_offset_after_shift, int32_t min, int32_t max, bool add_bias)
777 {
778 TensorShape shape_bias(shape[0]);
779
780 // Create tensors
781 TensorType a = create_tensor<TensorType>(shape, DataType::S32, 1);
782 TensorType b = create_tensor<TensorType>(shape_bias, DataType::S32, 1);
783 TensorType c = create_tensor<TensorType>(shape, DataType::QASYMM8, 1);
784
785 // Create and configure function
786 FunctionType output_stage;
787 output_stage.configure(&a, add_bias ? &b : nullptr, &c, result_fixedpoint_multiplier, result_shift, result_offset_after_shift, min, max);
788
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100789 ARM_COMPUTE_ASSERT(a.info()->is_resizable());
790 ARM_COMPUTE_ASSERT(c.info()->is_resizable());
Gian Marco58c57942017-11-28 09:10:03 +0000791
792 // Allocate tensors
793 a.allocator()->allocate();
794 c.allocator()->allocate();
795
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100796 ARM_COMPUTE_ASSERT(!a.info()->is_resizable());
797 ARM_COMPUTE_ASSERT(!c.info()->is_resizable());
Gian Marco58c57942017-11-28 09:10:03 +0000798
799 // Fill tensor
800 fill(AccessorType(a), 0);
801
802 if(add_bias)
803 {
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100804 ARM_COMPUTE_ASSERT(b.info()->is_resizable());
Gian Marco58c57942017-11-28 09:10:03 +0000805
806 // Allocate bias tensor
807 b.allocator()->allocate();
808
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100809 ARM_COMPUTE_ASSERT(!b.info()->is_resizable());
Gian Marco58c57942017-11-28 09:10:03 +0000810
811 // Fill tensor
812 fill(AccessorType(b), 1);
813 }
814
815 // Compute GEMM function
816 output_stage.run();
817 return c;
818 }
819
820 SimpleTensor<uint8_t> compute_reference(const TensorShape &shape, int32_t result_fixed_point_multiplier, int32_t result_shift, int32_t result_offset_after_shift, int32_t min, int32_t max,
821 bool add_bias)
822 {
823 // Create reference
824 TensorShape shape_bias(shape[0]);
825
826 SimpleTensor<int32_t> a{ shape, DataType::S32, 1 };
827 SimpleTensor<int32_t> b{ shape_bias, DataType::S32, 1 };
828
829 // Fill reference
830 fill(a, 0);
831
Vidhya Sudhan Loganathan951b8a42019-11-04 14:42:08 +0000832 const std::vector<int32_t> result_fixed_point_multiplier_vec = { result_fixed_point_multiplier };
833 const std::vector<int32_t> result_shift_vec = { result_shift };
834
Gian Marco58c57942017-11-28 09:10:03 +0000835 if(add_bias)
836 {
837 // Fill bias
838 fill(b, 1);
839
Georgios Pinitas448a81f2019-11-21 14:10:25 +0000840 return reference::gemmlowp_quantize_down_scale_by_fixedpoint<int32_t, uint8_t>(a, b, result_fixed_point_multiplier_vec, result_shift_vec, result_offset_after_shift, min, max);
Gian Marco58c57942017-11-28 09:10:03 +0000841 }
842 else
843 {
Georgios Pinitas448a81f2019-11-21 14:10:25 +0000844 return reference::gemmlowp_quantize_down_scale_by_fixedpoint<int32_t, uint8_t>(a, result_fixed_point_multiplier_vec, result_shift_vec, result_offset_after_shift, min, max);
Gian Marco58c57942017-11-28 09:10:03 +0000845 }
846 }
847
848 TensorType _target{};
849 SimpleTensor<uint8_t> _reference{};
850};
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +0000851
Sheri Zhang1b14c752020-03-09 14:29:52 +0000852template <typename TensorType, typename AccessorType, typename FunctionType, typename T>
853class GEMMLowpQuantizeDownInt32ScaleByFloatValidationFixture : public framework::Fixture
854{
855public:
Sheri Zhang1b14c752020-03-09 14:29:52 +0000856 void setup(DataType data_type, TensorShape shape, float result_real_multiplier, int32_t result_offset, int32_t min, int32_t max, bool add_bias)
857 {
858 _target = compute_target(data_type, shape, result_real_multiplier, result_offset, min, max, add_bias);
859 _reference = compute_reference(shape, result_real_multiplier, result_offset, min, max, add_bias);
860 }
861
862protected:
863 template <typename U>
864 void fill(U &&tensor, int i)
865 {
866 // To avoid data all being clampped
867 std::uniform_int_distribution<> distribution(-500, 500);
868 library->fill(tensor, distribution, i);
869 }
870
871 TensorType compute_target(DataType data_type, const TensorShape &shape, float result_multiplier, int32_t result_offset, int32_t min, int32_t max, bool add_bias)
872 {
873 TensorShape shape_bias(shape[0]);
874
875 // Create tensors
876 TensorType a = create_tensor<TensorType>(shape, DataType::S32, 1);
877 TensorType b = create_tensor<TensorType>(shape_bias, DataType::S32, 1);
878 TensorType c = create_tensor<TensorType>(shape, data_type, 1);
879
880 // create output stage info
881 GEMMLowpOutputStageInfo info;
882 info.gemmlowp_max_bound = max;
883 info.gemmlowp_min_bound = min;
884 info.gemmlowp_real_multiplier = result_multiplier;
885 info.gemmlowp_offset = result_offset;
886 info.type = GEMMLowpOutputStageType::QUANTIZE_DOWN_FLOAT;
887 info.output_data_type = data_type;
888
889 // Create and configure function
890 FunctionType output_stage;
891 output_stage.configure(&a, add_bias ? &b : nullptr, &c, info);
892
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100893 ARM_COMPUTE_ASSERT(a.info()->is_resizable());
894 ARM_COMPUTE_ASSERT(c.info()->is_resizable());
Sheri Zhang1b14c752020-03-09 14:29:52 +0000895
896 // Allocate tensors
897 a.allocator()->allocate();
898 c.allocator()->allocate();
899
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100900 ARM_COMPUTE_ASSERT(!a.info()->is_resizable());
901 ARM_COMPUTE_ASSERT(!c.info()->is_resizable());
Sheri Zhang1b14c752020-03-09 14:29:52 +0000902
903 // Fill tensor
904 fill(AccessorType(a), 0);
905
906 if(add_bias)
907 {
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100908 ARM_COMPUTE_ASSERT(b.info()->is_resizable());
Sheri Zhang1b14c752020-03-09 14:29:52 +0000909
910 // Allocate bias tensor
911 b.allocator()->allocate();
912
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100913 ARM_COMPUTE_ASSERT(!b.info()->is_resizable());
Sheri Zhang1b14c752020-03-09 14:29:52 +0000914
915 // Fill tensor
916 fill(AccessorType(b), 1);
917 }
918
919 // Compute GEMM function
920 output_stage.run();
921 return c;
922 }
923
924 SimpleTensor<T> compute_reference(const TensorShape &shape, float_t result_real_multiplier, int32_t result_offset, int32_t min, int32_t max, bool add_bias)
925 {
926 // Create reference
927 TensorShape shape_bias(shape[0]);
928
929 SimpleTensor<int32_t> a{ shape, DataType::S32, 1 };
930 SimpleTensor<int32_t> b{ shape_bias, DataType::S32, 1 };
931
932 // Fill reference
933 fill(a, 0);
934
935 const std::vector<float_t> result_float_multiplier_vec = { result_real_multiplier };
936
937 if(add_bias)
938 {
939 // Fill bias
940 fill(b, 1);
941
942 return reference::gemmlowp_quantize_down_scale_by_float<int32_t, T>(a, b, result_float_multiplier_vec, result_offset, min, max);
943 }
944 else
945 {
946 return reference::gemmlowp_quantize_down_scale_by_float<int32_t, T>(a, result_float_multiplier_vec, result_offset, min, max);
947 }
948 }
949
950 TensorType _target{};
951 SimpleTensor<T> _reference{};
952};
953
Gian Marco Iodicebc415af2019-06-13 15:58:32 +0100954template <typename TensorType, typename AccessorType, typename FunctionType>
955class GEMMLowpQuantizeDownInt32ToInt16ScaleByFixedPointValidationFixture : public framework::Fixture
956{
957public:
Gian Marco Iodicebc415af2019-06-13 15:58:32 +0100958 void setup(TensorShape shape, int32_t result_fixedpoint_multiplier, int32_t result_shift, int32_t min, int32_t max, bool add_bias)
959 {
960 _target = compute_target(shape, result_fixedpoint_multiplier, result_shift, min, max, add_bias);
961 _reference = compute_reference(shape, result_fixedpoint_multiplier, result_shift, min, max, add_bias);
962 }
963
964protected:
965 template <typename U>
966 void fill(U &&tensor, int i)
967 {
968 std::uniform_int_distribution<> distribution(-6000, 6000);
969 library->fill(tensor, distribution, i);
970 }
971
972 TensorType compute_target(const TensorShape &shape, int32_t result_fixedpoint_multiplier, int32_t result_shift, int32_t min, int32_t max, bool add_bias)
973 {
974 TensorShape shape_bias(shape[0]);
975
976 // Create tensors
977 TensorType a = create_tensor<TensorType>(shape, DataType::S32, 1);
978 TensorType b = create_tensor<TensorType>(shape_bias, DataType::S32, 1);
979 TensorType c = create_tensor<TensorType>(shape, DataType::QSYMM16, 1);
980
981 // Create and configure function
982 FunctionType output_stage;
983 output_stage.configure(&a, add_bias ? &b : nullptr, &c, result_fixedpoint_multiplier, result_shift, min, max);
984
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100985 ARM_COMPUTE_ASSERT(a.info()->is_resizable());
986 ARM_COMPUTE_ASSERT(c.info()->is_resizable());
Gian Marco Iodicebc415af2019-06-13 15:58:32 +0100987
988 // Allocate tensors
989 a.allocator()->allocate();
990 c.allocator()->allocate();
991
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +0100992 ARM_COMPUTE_ASSERT(!a.info()->is_resizable());
993 ARM_COMPUTE_ASSERT(!c.info()->is_resizable());
Gian Marco Iodicebc415af2019-06-13 15:58:32 +0100994
995 // Fill tensor
996 fill(AccessorType(a), 0);
997
998 if(add_bias)
999 {
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001000 ARM_COMPUTE_ASSERT(b.info()->is_resizable());
Gian Marco Iodicebc415af2019-06-13 15:58:32 +01001001
1002 // Allocate bias tensor
1003 b.allocator()->allocate();
1004
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001005 ARM_COMPUTE_ASSERT(!b.info()->is_resizable());
Gian Marco Iodicebc415af2019-06-13 15:58:32 +01001006
1007 // Fill tensor
1008 fill(AccessorType(b), 1);
1009 }
1010
1011 // Compute GEMM function
1012 output_stage.run();
1013 return c;
1014 }
1015
1016 SimpleTensor<int16_t> compute_reference(const TensorShape &shape, int32_t result_fixed_point_multiplier, int32_t result_shift, int32_t min, int32_t max,
1017 bool add_bias)
1018 {
1019 // Create reference
1020 TensorShape shape_bias(shape[0]);
1021
1022 SimpleTensor<int32_t> a{ shape, DataType::S32, 1 };
1023 SimpleTensor<int32_t> b{ shape_bias, DataType::S32, 1 };
1024
1025 // Fill reference
1026 fill(a, 0);
1027
Georgios Pinitas448a81f2019-11-21 14:10:25 +00001028 const std::vector<int32_t> result_fixed_point_multiplier_vec = { result_fixed_point_multiplier };
1029 const std::vector<int32_t> result_shift_vec = { result_shift };
1030
Gian Marco Iodicebc415af2019-06-13 15:58:32 +01001031 if(add_bias)
1032 {
1033 // Fill bias
1034 fill(b, 1);
1035
Georgios Pinitas448a81f2019-11-21 14:10:25 +00001036 return reference::gemmlowp_quantize_down_scale_by_fixedpoint<int32_t, int16_t>(a, b, result_fixed_point_multiplier_vec, result_shift_vec, 0, min, max);
Gian Marco Iodicebc415af2019-06-13 15:58:32 +01001037 }
1038 else
1039 {
Georgios Pinitas448a81f2019-11-21 14:10:25 +00001040 return reference::gemmlowp_quantize_down_scale_by_fixedpoint<int32_t, int16_t>(a, result_fixed_point_multiplier_vec, result_shift_vec, 0, min, max);
Gian Marco Iodicebc415af2019-06-13 15:58:32 +01001041 }
1042 }
1043
1044 TensorType _target{};
1045 SimpleTensor<int16_t> _reference{};
1046};
1047
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001048template <typename TensorType, typename AccessorType, typename ReshapeLHSOperatorType, typename ReshapeRHSOperatorType, typename GEMMFunctionType>
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001049class GEMMLowpMatrixMultiplyReshapedValidationFixture : public framework::Fixture
1050{
1051public:
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001052 void setup(unsigned int m, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0, unsigned int k0, unsigned int v0, unsigned int h0, bool interleave_lhs,
Sheri Zhang28287af2020-02-25 14:13:54 +00001053 bool interleave_rhs, DataType data_type)
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001054 {
1055 GEMMLHSMatrixInfo lhs_info;
1056 lhs_info.m0 = m0;
1057 lhs_info.k0 = k0;
1058 lhs_info.v0 = v0;
1059 lhs_info.interleave = interleave_lhs;
1060 lhs_info.transpose = false;
1061
1062 GEMMRHSMatrixInfo rhs_info;
1063 rhs_info.n0 = n0;
1064 rhs_info.k0 = k0;
1065 rhs_info.h0 = h0;
1066 rhs_info.interleave = interleave_rhs;
1067 rhs_info.transpose = true;
1068
1069 // Set the tensor shapes for LHS and RHS matrices
1070 const TensorShape lhs_shape(k, m, batch_size);
1071 const TensorShape rhs_shape(n, k, batch_size);
1072
Sheri Zhang28287af2020-02-25 14:13:54 +00001073 _target = compute_target(lhs_shape, rhs_shape, lhs_info, rhs_info, data_type);
1074 _reference = compute_reference(lhs_shape, rhs_shape, data_type);
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001075 }
1076
1077protected:
1078 template <typename U>
1079 void fill(U &&tensor, int i)
1080 {
Sheri Zhang28287af2020-02-25 14:13:54 +00001081 switch(tensor.data_type())
1082 {
1083 case DataType::QASYMM8:
1084 {
1085 // Between 1 and 254 in order to avoid having -128 and 128 for the DOT product path
1086 std::uniform_int_distribution<> distribution(1, 254);
1087 library->fill(tensor, distribution, i);
1088 }
1089 break;
1090 case DataType::QASYMM8_SIGNED:
1091 {
1092 std::uniform_int_distribution<> distribution(-127, 126);
1093 library->fill(tensor, distribution, i);
1094 }
1095 break;
1096 default:
1097 ARM_COMPUTE_ERROR("Unsupported data type");
1098 }
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001099 }
1100
Sheri Zhang28287af2020-02-25 14:13:54 +00001101 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info, DataType data_type)
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001102 {
1103 // Create tensors
Sheri Zhang28287af2020-02-25 14:13:54 +00001104 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
1105 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001106 TensorType lhs_reshaped;
1107 TensorType rhs_reshaped;
1108 TensorType dst;
1109
1110 const unsigned int M = lhs_shape[1];
1111 const unsigned int N = rhs_shape[0];
1112 const unsigned int K = lhs_shape[0];
1113
1114 // The output tensor will be auto-initialized within the function
1115
1116 // Create and configure function
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001117 ReshapeLHSOperatorType reshape_lhs;
1118 ReshapeRHSOperatorType reshape_rhs;
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001119 GEMMFunctionType gemm;
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001120 reshape_lhs.configure(lhs.info(), lhs_reshaped.info(), lhs_info);
1121 reshape_rhs.configure(rhs.info(), rhs_reshaped.info(), rhs_info);
Georgios Pinitas4a578b92021-06-25 12:13:49 +01001122 gemm.configure(lhs_reshaped.info(), rhs_reshaped.info(), dst.info(), lhs_info, rhs_info, GEMMReshapeInfo(M, N, K));
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001123
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001124 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
1125 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001126
Giorgio Arena63825e82021-03-25 14:54:50 +00001127 add_padding_x({ &lhs, &rhs, &lhs_reshaped, &rhs_reshaped, &dst });
1128
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001129 // Allocate tensors
1130 lhs.allocator()->allocate();
1131 rhs.allocator()->allocate();
1132 lhs_reshaped.allocator()->allocate();
1133 rhs_reshaped.allocator()->allocate();
1134 dst.allocator()->allocate();
1135
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001136 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
1137 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
1138 ARM_COMPUTE_ASSERT(!lhs_reshaped.info()->is_resizable());
1139 ARM_COMPUTE_ASSERT(!rhs_reshaped.info()->is_resizable());
1140 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001141
1142 // Fill tensors
1143 fill(AccessorType(lhs), 0);
1144 fill(AccessorType(rhs), 1);
1145
1146 // Compute GEMM
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001147 ITensorPack reshape_lhs_pack = { { ACL_SRC, &lhs }, { ACL_DST, &lhs_reshaped } };
1148 reshape_lhs.run(reshape_lhs_pack);
1149 ITensorPack reshape_rhs_pack = { { ACL_SRC, &rhs }, { ACL_DST, &rhs_reshaped } };
1150 reshape_rhs.run(reshape_rhs_pack);
Georgios Pinitas4a578b92021-06-25 12:13:49 +01001151 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs_reshaped }, { ACL_SRC_1, &rhs_reshaped }, { ACL_DST, &dst } });
1152 gemm.run(gemm_pack);
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001153
1154 return dst;
1155 }
1156
Sheri Zhang28287af2020-02-25 14:13:54 +00001157 SimpleTensor<int32_t> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type)
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001158 {
1159 TensorShape dst_shape = lhs_shape;
1160 dst_shape[0] = rhs_shape[0];
1161 dst_shape[1] = lhs_shape[1];
1162
Sheri Zhang28287af2020-02-25 14:13:54 +00001163 switch(data_type)
1164 {
1165 case DataType::QASYMM8:
1166 {
1167 // Create reference
1168 SimpleTensor<uint8_t> lhs{ lhs_shape, data_type, 1 };
1169 SimpleTensor<uint8_t> rhs{ rhs_shape, data_type, 1 };
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001170
Sheri Zhang28287af2020-02-25 14:13:54 +00001171 // Fill reference
1172 fill(lhs, 0);
1173 fill(rhs, 1);
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001174
Sheri Zhang28287af2020-02-25 14:13:54 +00001175 return reference::gemmlowp_matrix_multiply_core<int32_t, uint8_t>(lhs, rhs, dst_shape, 0, 0);
1176 }
1177 case DataType::QASYMM8_SIGNED:
1178 {
1179 // Create reference
1180 SimpleTensor<int8_t> lhs{ lhs_shape, data_type, 1 };
1181 SimpleTensor<int8_t> rhs{ rhs_shape, data_type, 1 };
1182
1183 // Fill reference
1184 fill(lhs, 0);
1185 fill(rhs, 1);
1186
1187 return reference::gemmlowp_matrix_multiply_core<int32_t, int8_t>(lhs, rhs, dst_shape, 0, 0);
1188 }
1189 default:
1190 ARM_COMPUTE_ERROR("Unsupported data type");
1191 }
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001192 }
1193
1194 TensorType _target{};
1195 SimpleTensor<int32_t> _reference{};
1196};
1197
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001198template <typename TensorType, typename AccessorType, typename ReshapeLHSOperatorType, typename ReshapeRHSOperatorType, typename GEMMFunctionType>
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001199class GEMMLowpMatrixMultiplyReshaped3DValidationFixture : public framework::Fixture
1200{
1201public:
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001202 void setup(unsigned int m_w, unsigned int m_h, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0, unsigned int k0, unsigned int v0, unsigned int h0,
Sheri Zhang28287af2020-02-25 14:13:54 +00001203 bool interleave_lhs, bool interleave_rhs, DataType data_type)
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001204 {
1205 GEMMLHSMatrixInfo lhs_info;
1206 lhs_info.m0 = m0;
1207 lhs_info.k0 = k0;
1208 lhs_info.v0 = v0;
1209 lhs_info.interleave = interleave_lhs;
1210 lhs_info.transpose = false;
1211
1212 GEMMRHSMatrixInfo rhs_info;
1213 rhs_info.n0 = n0;
1214 rhs_info.k0 = k0;
1215 rhs_info.h0 = h0;
1216 rhs_info.interleave = interleave_rhs;
1217 rhs_info.transpose = true;
1218
1219 // In case of GEMM3D, m is the product between m_w and m_h
1220 const unsigned int m = m_w * m_h;
1221
1222 // Set the tensor shapes for LHS and RHS matrices
1223 const TensorShape lhs_shape(k, m, batch_size);
1224 const TensorShape rhs_shape(n, k, batch_size);
1225
Sheri Zhang28287af2020-02-25 14:13:54 +00001226 _target = compute_target(lhs_shape, rhs_shape, lhs_info, rhs_info, m_h, data_type);
1227 _reference = compute_reference(lhs_shape, rhs_shape, m_h, data_type);
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001228 }
1229
1230protected:
1231 template <typename U>
1232 void fill(U &&tensor, int i)
1233 {
Sheri Zhang28287af2020-02-25 14:13:54 +00001234 switch(tensor.data_type())
1235 {
1236 case DataType::QASYMM8:
1237 {
1238 // Between 1 and 254 in order to avoid having -128 and 128 for the DOT product path
1239 std::uniform_int_distribution<> distribution(1, 254);
1240 library->fill(tensor, distribution, i);
1241 }
1242 break;
1243 case DataType::QASYMM8_SIGNED:
1244 {
1245 std::uniform_int_distribution<> distribution(-127, 126);
1246 library->fill(tensor, distribution, i);
1247 }
1248 break;
1249 default:
1250 ARM_COMPUTE_ERROR("Unsupported data type");
1251 }
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001252 }
1253
Sheri Zhang28287af2020-02-25 14:13:54 +00001254 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info, unsigned int m_h,
1255 DataType data_type)
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001256 {
1257 // Create tensors
Sheri Zhang28287af2020-02-25 14:13:54 +00001258 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
1259 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001260 TensorType lhs_reshaped;
1261 TensorType rhs_reshaped;
1262 TensorType dst;
1263
1264 const unsigned int M = lhs_shape[1];
1265 const unsigned int N = rhs_shape[0];
1266 const unsigned int K = lhs_shape[0];
1267
1268 // The output tensor will be auto-initialized within the function
1269
1270 // Create and configure function
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001271 ReshapeLHSOperatorType reshape_lhs;
1272 ReshapeRHSOperatorType reshape_rhs;
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001273 GEMMFunctionType gemm;
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001274 reshape_lhs.configure(lhs.info(), lhs_reshaped.info(), lhs_info);
1275 reshape_rhs.configure(rhs.info(), rhs_reshaped.info(), rhs_info);
Georgios Pinitas4a578b92021-06-25 12:13:49 +01001276 gemm.configure(lhs_reshaped.info(), rhs_reshaped.info(), dst.info(), lhs_info, rhs_info, GEMMReshapeInfo(M, N, K, 1, 1, m_h));
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001277
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001278 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
1279 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001280
Giorgio Arena63825e82021-03-25 14:54:50 +00001281 add_padding_x({ &lhs, &rhs, &lhs_reshaped, &rhs_reshaped, &dst });
1282
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001283 // Allocate tensors
1284 lhs.allocator()->allocate();
1285 rhs.allocator()->allocate();
1286 lhs_reshaped.allocator()->allocate();
1287 rhs_reshaped.allocator()->allocate();
1288 dst.allocator()->allocate();
1289
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001290 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
1291 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
1292 ARM_COMPUTE_ASSERT(!lhs_reshaped.info()->is_resizable());
1293 ARM_COMPUTE_ASSERT(!rhs_reshaped.info()->is_resizable());
1294 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001295
1296 // Fill tensors
1297 fill(AccessorType(lhs), 0);
1298 fill(AccessorType(rhs), 1);
1299
1300 // Compute GEMM
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001301 ITensorPack reshape_lhs_pack = { { ACL_SRC, &lhs }, { ACL_DST, &lhs_reshaped } };
1302 reshape_lhs.run(reshape_lhs_pack);
1303 ITensorPack reshape_rhs_pack = { { ACL_SRC, &rhs }, { ACL_DST, &rhs_reshaped } };
1304 reshape_rhs.run(reshape_rhs_pack);
Georgios Pinitas4a578b92021-06-25 12:13:49 +01001305 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs_reshaped }, { ACL_SRC_1, &rhs_reshaped }, { ACL_DST, &dst } });
1306 gemm.run(gemm_pack);
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001307
1308 return dst;
1309 }
1310
Sheri Zhang28287af2020-02-25 14:13:54 +00001311 SimpleTensor<int32_t> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, unsigned int m_h, DataType data_type)
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001312 {
1313 TensorShape dst_shape = lhs_shape;
1314 dst_shape.set(0, rhs_shape[0]);
1315 dst_shape.set(1, lhs_shape[1] / m_h);
1316 dst_shape.set(2, m_h);
1317 dst_shape.set(3, lhs_shape[2]);
1318
Sheri Zhang28287af2020-02-25 14:13:54 +00001319 switch(data_type)
1320 {
1321 case DataType::QASYMM8:
1322 {
1323 // Create reference
1324 SimpleTensor<uint8_t> lhs{ lhs_shape, data_type, 1 };
1325 SimpleTensor<uint8_t> rhs{ rhs_shape, data_type, 1 };
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001326
Sheri Zhang28287af2020-02-25 14:13:54 +00001327 // Fill reference
1328 fill(lhs, 0);
1329 fill(rhs, 1);
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001330
Sheri Zhang28287af2020-02-25 14:13:54 +00001331 return reference::gemmlowp_matrix_multiply_core<int32_t, uint8_t>(lhs, rhs, dst_shape, 0, 0);
1332 }
1333 case DataType::QASYMM8_SIGNED:
1334 {
1335 // Create reference
1336 SimpleTensor<int8_t> lhs{ lhs_shape, data_type, 1 };
1337 SimpleTensor<int8_t> rhs{ rhs_shape, data_type, 1 };
1338
1339 // Fill reference
1340 fill(lhs, 0);
1341 fill(rhs, 1);
1342
1343 return reference::gemmlowp_matrix_multiply_core<int32_t, int8_t>(lhs, rhs, dst_shape, 0, 0);
1344 }
1345 default:
1346 ARM_COMPUTE_ERROR("Unsupported data type");
1347 }
Gian Marco Iodicedb63b9c2019-01-17 09:47:04 +00001348 }
1349
1350 TensorType _target{};
1351 SimpleTensor<int32_t> _reference{};
1352};
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001353
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001354template <typename TensorType, typename AccessorType, typename ReshapeRHSOperatorType, typename GEMMFunctionType>
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001355class GEMMLowpMatrixMultiplyReshapedOnlyRHSValidationFixture : public framework::Fixture
1356{
1357public:
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001358 void setup(unsigned int m, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0,
1359 unsigned int k0, unsigned int h0, bool interleave_rhs, bool transpose_rhs, DataType data_type)
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001360 {
1361 GEMMLHSMatrixInfo lhs_info;
1362 lhs_info.m0 = m0;
1363 lhs_info.k0 = k0;
1364
1365 GEMMRHSMatrixInfo rhs_info;
1366 rhs_info.n0 = n0;
1367 rhs_info.k0 = k0;
1368 rhs_info.h0 = h0;
1369 rhs_info.interleave = interleave_rhs;
1370 rhs_info.transpose = transpose_rhs;
1371
1372 // Set the tensor shapes for LHS and RHS matrices
1373 const TensorShape lhs_shape(k, m, batch_size);
1374 const TensorShape rhs_shape(n, k, batch_size);
1375
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001376 _target = compute_target(lhs_shape, rhs_shape, lhs_info, rhs_info, data_type);
1377 _reference = compute_reference(lhs_shape, rhs_shape, data_type);
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001378 }
1379
1380protected:
1381 template <typename U>
1382 void fill(U &&tensor, int i)
1383 {
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001384 switch(tensor.data_type())
1385 {
1386 case DataType::QASYMM8:
1387 {
1388 // Between 1 and 254 in order to avoid having -128 and 128 for the DOT product path
1389 std::uniform_int_distribution<> distribution(1, 254);
1390 library->fill(tensor, distribution, i);
1391 }
1392 break;
1393 case DataType::QASYMM8_SIGNED:
1394 {
1395 std::uniform_int_distribution<> distribution(-127, 126);
1396 library->fill(tensor, distribution, i);
1397 }
1398 break;
1399 default:
1400 ARM_COMPUTE_ERROR("Unsupported data type");
1401 }
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001402 }
1403
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001404 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const GEMMLHSMatrixInfo &lhs_info,
1405 const GEMMRHSMatrixInfo &rhs_info, DataType data_type)
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001406 {
1407 // Create tensors
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001408 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
1409 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001410 TensorType rhs_reshaped;
1411 TensorType dst;
1412
1413 const unsigned int M = lhs_shape[1];
1414 const unsigned int N = rhs_shape[0];
1415 const unsigned int K = lhs_shape[0];
1416
Michele Di Giorgiob54ba282020-01-14 15:31:55 +00001417 GEMMKernelInfo gemm_info;
1418 gemm_info.m = M;
1419 gemm_info.n = N;
1420 gemm_info.k = K;
1421 gemm_info.lhs_info = lhs_info;
1422 gemm_info.rhs_info = rhs_info;
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001423 // The output tensor will be auto-initialized within the function
1424
1425 // Create and configure function
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001426 ReshapeRHSOperatorType reshape_rhs;
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001427 GEMMFunctionType gemm;
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001428 reshape_rhs.configure(rhs.info(), rhs_reshaped.info(), rhs_info);
Georgios Pinitas4a578b92021-06-25 12:13:49 +01001429 gemm.configure(lhs.info(), rhs_reshaped.info(), dst.info(), gemm_info);
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001430
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001431 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
1432 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001433
Giorgio Arena63825e82021-03-25 14:54:50 +00001434 add_padding_x({ &lhs, &rhs, &rhs_reshaped, &dst });
1435
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001436 // Allocate tensors
1437 lhs.allocator()->allocate();
1438 rhs.allocator()->allocate();
1439 rhs_reshaped.allocator()->allocate();
1440 dst.allocator()->allocate();
1441
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001442 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
1443 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
1444 ARM_COMPUTE_ASSERT(!rhs_reshaped.info()->is_resizable());
1445 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001446
1447 // Fill tensors
1448 fill(AccessorType(lhs), 0);
1449 fill(AccessorType(rhs), 1);
1450
1451 // Compute GEMM
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001452 ITensorPack reshape_rhs_pack = { { ACL_SRC, &rhs }, { ACL_DST, &rhs_reshaped } };
1453 reshape_rhs.run(reshape_rhs_pack);
Georgios Pinitas4a578b92021-06-25 12:13:49 +01001454 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs }, { ACL_SRC_1, &rhs_reshaped }, { ACL_DST, &dst } });
1455 gemm.run(gemm_pack);
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001456
1457 return dst;
1458 }
1459
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001460 SimpleTensor<int32_t> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type)
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001461 {
1462 TensorShape dst_shape = lhs_shape;
1463 dst_shape[0] = rhs_shape[0];
1464 dst_shape[1] = lhs_shape[1];
1465
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001466 if(data_type == DataType::QASYMM8)
1467 {
1468 // Create reference
1469 SimpleTensor<uint8_t> lhs{ lhs_shape, data_type, 1 };
1470 SimpleTensor<uint8_t> rhs{ rhs_shape, data_type, 1 };
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001471
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001472 // Fill reference
1473 fill(lhs, 0);
1474 fill(rhs, 1);
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001475
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001476 return reference::gemmlowp_matrix_multiply_core<int32_t, uint8_t>(lhs, rhs, dst_shape, 0, 0);
1477 }
1478 else
1479 {
1480 // Create reference
1481 SimpleTensor<int8_t> lhs{ lhs_shape, data_type, 1 };
1482 SimpleTensor<int8_t> rhs{ rhs_shape, data_type, 1 };
1483
1484 // Fill reference
1485 fill(lhs, 0);
1486 fill(rhs, 1);
1487
1488 return reference::gemmlowp_matrix_multiply_core<int32_t, int8_t>(lhs, rhs, dst_shape, 0, 0);
1489 }
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001490 }
1491
1492 TensorType _target{};
1493 SimpleTensor<int32_t> _reference{};
1494};
1495
Freddie Liardete572dff2022-05-16 14:09:10 +01001496template <typename T, typename TensorType, typename AccessorType, typename ReshapeRHSOperatorType, typename GEMMFunctionType, typename ReduceOperation, typename CastOperation>
1497class GEMMLowpMatrixMultiplyReshapedOnlyRHSMMULOutputStageValidationFixture : public framework::Fixture
1498{
1499public:
Freddie Liardete572dff2022-05-16 14:09:10 +01001500 void setup(unsigned int m, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0,
1501 unsigned int k0, unsigned int h0, bool interleave_rhs, bool transpose_rhs, bool broadcast_bias, DataType data_type)
1502 {
1503 GEMMLowpOutputStageInfo output_stage;
1504 output_stage.type = GEMMLowpOutputStageType::QUANTIZE_DOWN_FIXEDPOINT;
1505 output_stage.output_data_type = data_type;
1506 output_stage.gemmlowp_multipliers = std::vector<int32_t> { 1 };
1507 output_stage.gemmlowp_shifts = std::vector<int32_t> { 1 };
1508 output_stage.gemmlowp_multipliers[0] = 1;
1509 output_stage.gemmlowp_shifts[0] = 1;
1510 output_stage.gemmlowp_offset = 0;
1511 constexpr float scale = 0.001f;
1512 quantization::calculate_quantized_multiplier(scale, &output_stage.gemmlowp_multipliers[0], &output_stage.gemmlowp_shifts[0]);
1513 output_stage.gemmlowp_min_bound = -100;
1514 output_stage.gemmlowp_max_bound = 100;
1515
1516 GEMMLHSMatrixInfo lhs_info;
1517 lhs_info.m0 = m0;
1518 lhs_info.k0 = k0;
1519
1520 GEMMRHSMatrixInfo rhs_info;
1521 rhs_info.n0 = n0;
1522 rhs_info.k0 = k0;
1523 rhs_info.h0 = h0;
1524 rhs_info.interleave = interleave_rhs;
1525 rhs_info.transpose = transpose_rhs;
1526
1527 int a_offset = 1;
1528 int b_offset = 1;
1529
1530 // Set the tensor shapes for LHS and RHS matrices
1531 const TensorShape lhs_shape(k, m, batch_size);
1532 const TensorShape rhs_shape(n, k, batch_size);
1533 const TensorShape bias_shape(n,
1534 broadcast_bias ? 1 : m,
1535 broadcast_bias ? 1 : batch_size);
1536
Ramy Elgammala77c6d72022-09-08 11:30:08 +01001537 _target = compute_target(lhs_shape, rhs_shape, bias_shape, lhs_info, rhs_info, data_type, output_stage, a_offset, b_offset);
Freddie Liardete572dff2022-05-16 14:09:10 +01001538 if(gemm_validated == true)
1539 {
1540 _reference = compute_reference(lhs_shape, rhs_shape, bias_shape, data_type, output_stage, a_offset, b_offset);
1541 }
1542 }
1543
1544protected:
1545 template <typename U>
1546 void fill(U &&tensor, int i)
1547 {
1548 switch(tensor.data_type())
1549 {
1550 case DataType::QASYMM8:
1551 {
1552 // Between 1 and 254 in order to avoid having -128 and 128 for the DOT product path
1553 std::uniform_int_distribution<> distribution(1, 254);
1554 library->fill(tensor, distribution, i);
1555 }
1556 break;
1557 case DataType::QASYMM8_SIGNED:
1558 {
1559 std::uniform_int_distribution<> distribution(-127, 126);
1560 library->fill(tensor, distribution, i);
1561 }
1562 break;
1563 case DataType::S32:
1564 {
1565 std::uniform_int_distribution<> distribution(-10000, 10000);
1566 library->fill(tensor, distribution, i);
1567 }
1568 break;
1569 default:
1570 ARM_COMPUTE_ERROR("Unsupported data type");
1571 }
1572 }
1573
1574 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const TensorShape &bias_shape, const GEMMLHSMatrixInfo &lhs_info,
1575 const GEMMRHSMatrixInfo &rhs_info, DataType data_type, GEMMLowpOutputStageInfo output_stage, const int a_offset, const int b_offset)
1576 {
1577 // Create tensors
1578 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1, QuantizationInfo(1.0f / 255, a_offset));
1579 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1, QuantizationInfo(1.0f / 255, b_offset));
1580 TensorType bias = create_tensor<TensorType>(bias_shape, DataType::S32, 1);
1581 TensorType dst;
1582 TensorType rhs_reshaped;
1583
1584 const unsigned int M = lhs_shape[1];
1585 const unsigned int N = rhs_shape[0];
1586 const unsigned int K = lhs_shape[0];
1587
1588 // Tensors for precomputing sum of lhs rows / rhs columns
1589 TensorType vec_sum_rows = create_tensor<TensorType>(TensorShape(M, 1, lhs_shape[2]), DataType::S32, 1);
1590 TensorType vec_sum_cols = create_tensor<TensorType>(TensorShape(N, 1, rhs_shape[2]), DataType::S32, 1);
1591
1592 GEMMKernelInfo gemm_info;
1593 gemm_info.m = M;
1594 gemm_info.n = N;
1595 gemm_info.k = K;
1596 gemm_info.lhs_info = lhs_info;
1597 gemm_info.rhs_info = rhs_info;
1598 gemm_info.output_stage = output_stage;
1599 gemm_info.a_offset = a_offset;
1600 gemm_info.b_offset = b_offset;
1601 // The output tensor will be auto-initialized within the function
1602
1603 // Create and configure function
1604 ReshapeRHSOperatorType reshape_rhs;
1605 GEMMFunctionType gemm;
1606 reshape_rhs.configure(rhs.info(), rhs_reshaped.info(), rhs_info);
1607
1608 // If GEMM is not validated, do not try to run. The validation will check
1609 // if the technology supports this extension. If not, the test will be skipped.
1610 // If it supports, the test will fail anyway because target and reference
1611 // will not match.
1612 gemm_validated = bool(gemm.validate(lhs.info(), rhs_reshaped.info(), dst.info(), gemm_info, vec_sum_cols.info(), vec_sum_rows.info(), bias.info()));
1613 if(gemm_validated == true)
1614 {
1615 gemm.configure(lhs.info(), rhs_reshaped.info(), dst.info(), gemm_info, vec_sum_cols.info(), vec_sum_rows.info(), bias.info());
1616
1617 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
1618 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
1619 ARM_COMPUTE_ASSERT(bias.info()->is_resizable());
1620
1621 // Allocate tensors
1622 lhs.allocator()->allocate();
1623 rhs.allocator()->allocate();
1624 rhs_reshaped.allocator()->allocate();
1625 bias.allocator()->allocate();
1626 vec_sum_cols.allocator()->allocate();
1627 vec_sum_rows.allocator()->allocate();
1628 dst.allocator()->allocate();
1629
1630 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
1631 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
1632 ARM_COMPUTE_ASSERT(!rhs_reshaped.info()->is_resizable());
1633 ARM_COMPUTE_ASSERT(!bias.info()->is_resizable());
1634 ARM_COMPUTE_ASSERT(!vec_sum_cols.info()->is_resizable());
1635 ARM_COMPUTE_ASSERT(!vec_sum_rows.info()->is_resizable());
1636 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
1637
1638 // Fill tensors
1639 fill(AccessorType(lhs), 0);
1640 fill(AccessorType(rhs), 1);
1641 fill(AccessorType(bias), 2);
1642
1643 TensorType lhs_32 = create_tensor<TensorType>(lhs_shape, DataType::S32, 1);
1644 TensorType rhs_32 = create_tensor<TensorType>(rhs_shape, DataType::S32, 1);
1645 CastOperation cast_lhs;
1646 CastOperation cast_rhs;
1647 cast_lhs.configure(&lhs, &lhs_32, ConvertPolicy::SATURATE);
1648 cast_rhs.configure(&rhs, &rhs_32, ConvertPolicy::SATURATE);
1649 lhs_32.allocator()->allocate();
1650 rhs_32.allocator()->allocate();
1651 cast_lhs.run();
1652 cast_rhs.run();
1653
1654 ReduceOperation lhs_sum_rows;
1655 ReduceOperation rhs_sum_cols;
1656
1657 lhs_sum_rows.configure(&lhs_32, &vec_sum_rows, 0, ReductionOperation::SUM, false);
1658 rhs_sum_cols.configure(&rhs_32, &vec_sum_cols, 1, ReductionOperation::SUM);
1659
1660 lhs_sum_rows.run();
1661 rhs_sum_cols.run();
1662
1663 // Compute GEMM
1664 ITensorPack reshape_rhs_pack = { { ACL_SRC, &rhs }, { ACL_DST, &rhs_reshaped } };
1665 reshape_rhs.run(reshape_rhs_pack);
1666 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs }, { ACL_SRC_1, &rhs_reshaped }, { ACL_SRC_2, &bias }, { ACL_DST, &dst }, { ACL_VEC_COL_SUM, &vec_sum_cols }, { ACL_VEC_ROW_SUM, &vec_sum_rows } });
1667 gemm.run(gemm_pack);
1668 }
1669
1670 return dst;
1671 }
1672
1673 SimpleTensor<T> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const TensorShape &bias_shape, DataType data_type, GEMMLowpOutputStageInfo output_stage,
1674 const int a_offset, const int b_offset)
1675 {
1676 TensorShape dst_shape = lhs_shape;
1677 dst_shape[0] = rhs_shape[0];
1678 dst_shape[1] = lhs_shape[1];
1679
1680 // Create reference
1681 SimpleTensor<T> lhs{ lhs_shape, data_type, 1, QuantizationInfo(1.0f / 255, a_offset) };
1682 SimpleTensor<T> rhs{ rhs_shape, data_type, 1, QuantizationInfo(1.0f / 255, b_offset) };
1683 SimpleTensor<int32_t> bias{ bias_shape, DataType::S32, 1 };
1684 SimpleTensor<int32_t> dst{ dst_shape, DataType::S32, 1 };
1685 SimpleTensor<T> dst_final{ dst_shape, data_type, 1 };
1686
1687 // Fill reference
1688 fill(lhs, 0);
1689 fill(rhs, 1);
1690 fill(bias, 2);
1691
1692 dst = reference::gemmlowp_matrix_multiply_core<int32_t, T>(lhs, rhs, dst_shape, a_offset, b_offset);
1693 dst_final = reference::gemmlowp_quantize_down_scale_by_fixedpoint<int32_t, T>(dst, bias,
1694 output_stage.gemmlowp_multipliers, output_stage.gemmlowp_shifts, output_stage.gemmlowp_offset, output_stage.gemmlowp_min_bound, output_stage.gemmlowp_max_bound);
1695 return dst_final;
1696 }
1697
1698 bool gemm_validated = true;
1699 TensorType _target{};
1700 SimpleTensor<T> _reference{};
1701};
1702
1703template <typename TensorType, typename AccessorType, typename ReshapeRHSOperatorType, typename GEMMFunctionType>
1704class GEMMLowpMatrixMultiplyReshapedOnlyRHSMMULValidationFixture : public framework::Fixture
1705{
1706public:
Freddie Liardete572dff2022-05-16 14:09:10 +01001707 void setup(unsigned int m, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0,
1708 unsigned int k0, unsigned int h0, bool interleave_rhs, bool transpose_rhs, DataType data_type)
1709 {
1710 GEMMLHSMatrixInfo lhs_info;
1711 lhs_info.m0 = m0;
1712 lhs_info.k0 = k0;
1713
1714 GEMMRHSMatrixInfo rhs_info;
1715 rhs_info.n0 = n0;
1716 rhs_info.k0 = k0;
1717 rhs_info.h0 = h0;
1718 rhs_info.interleave = interleave_rhs;
1719 rhs_info.transpose = transpose_rhs;
1720
1721 // Set the tensor shapes for LHS and RHS matrices
1722 const TensorShape lhs_shape(k, m, batch_size);
1723 const TensorShape rhs_shape(n, k, batch_size);
1724
Ramy Elgammala77c6d72022-09-08 11:30:08 +01001725 _target = compute_target(lhs_shape, rhs_shape, lhs_info, rhs_info, data_type);
Freddie Liardete572dff2022-05-16 14:09:10 +01001726 if(gemm_validated == true)
1727 {
1728 _reference = compute_reference(lhs_shape, rhs_shape, data_type);
1729 }
1730 }
1731
1732protected:
1733 template <typename U>
1734 void fill(U &&tensor, int i)
1735 {
1736 switch(tensor.data_type())
1737 {
1738 case DataType::QASYMM8:
1739 {
1740 // Between 1 and 254 in order to avoid having -128 and 128 for the DOT product path
1741 std::uniform_int_distribution<> distribution(1, 254);
1742 library->fill(tensor, distribution, i);
1743 }
1744 break;
1745 case DataType::QASYMM8_SIGNED:
1746 {
1747 std::uniform_int_distribution<> distribution(-127, 126);
1748 library->fill(tensor, distribution, i);
1749 }
1750 break;
1751 default:
1752 ARM_COMPUTE_ERROR("Unsupported data type");
1753 }
1754 }
1755
1756 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const GEMMLHSMatrixInfo &lhs_info,
1757 const GEMMRHSMatrixInfo &rhs_info, DataType data_type)
1758 {
1759 // Create tensors
1760 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
1761 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
1762 TensorType rhs_reshaped;
1763 TensorType dst;
1764
1765 const unsigned int M = lhs_shape[1];
1766 const unsigned int N = rhs_shape[0];
1767 const unsigned int K = lhs_shape[0];
1768
1769 GEMMKernelInfo gemm_info;
1770 gemm_info.m = M;
1771 gemm_info.n = N;
1772 gemm_info.k = K;
1773 gemm_info.lhs_info = lhs_info;
1774 gemm_info.rhs_info = rhs_info;
1775 // The output tensor will be auto-initialized within the function
1776
1777 // Create and configure function
1778 ReshapeRHSOperatorType reshape_rhs;
1779 GEMMFunctionType gemm;
1780 reshape_rhs.configure(rhs.info(), rhs_reshaped.info(), rhs_info);
1781
1782 // If GEMM is not validated, do not try to run. The validation will check
1783 // if the technology supports this extension. If not, the test will be skipped.
1784 // If it supports, the test will fail anyway because target and reference
1785 // will not match.
1786 gemm_validated = bool(gemm.validate(lhs.info(), rhs_reshaped.info(), dst.info(), gemm_info, nullptr, nullptr, nullptr));
1787 if(gemm_validated == true)
1788 {
1789 gemm.configure(lhs.info(), rhs_reshaped.info(), dst.info(), gemm_info, nullptr, nullptr, nullptr);
1790
1791 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
1792 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
1793
1794 // Allocate tensors
1795 lhs.allocator()->allocate();
1796 rhs.allocator()->allocate();
1797 rhs_reshaped.allocator()->allocate();
1798 dst.allocator()->allocate();
1799
1800 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
1801 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
1802 ARM_COMPUTE_ASSERT(!rhs_reshaped.info()->is_resizable());
1803 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
1804
1805 // Fill tensors
1806 fill(AccessorType(lhs), 0);
1807 fill(AccessorType(rhs), 1);
1808
1809 // Compute GEMM
1810 ITensorPack reshape_rhs_pack = { { ACL_SRC, &rhs }, { ACL_DST, &rhs_reshaped } };
1811 reshape_rhs.run(reshape_rhs_pack);
1812 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs }, { ACL_SRC_1, &rhs_reshaped }, { ACL_DST, &dst } });
1813 gemm.run(gemm_pack);
1814 }
1815
1816 return dst;
1817 }
1818
1819 SimpleTensor<int32_t> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, DataType data_type)
1820 {
1821 TensorShape dst_shape = lhs_shape;
1822 dst_shape[0] = rhs_shape[0];
1823 dst_shape[1] = lhs_shape[1];
1824
1825 if(data_type == DataType::QASYMM8)
1826 {
1827 // Create reference
1828 SimpleTensor<uint8_t> lhs{ lhs_shape, data_type, 1 };
1829 SimpleTensor<uint8_t> rhs{ rhs_shape, data_type, 1 };
1830 SimpleTensor<int32_t> dst{ dst_shape, DataType::S32, 1 };
1831
1832 // Fill reference
1833 fill(lhs, 0);
1834 fill(rhs, 1);
1835
1836 return reference::gemmlowp_matrix_multiply_core<int32_t, uint8_t>(lhs, rhs, dst_shape, 0, 0);
1837 }
1838 else
1839 {
1840 // Create reference
1841 SimpleTensor<int8_t> lhs{ lhs_shape, data_type, 1 };
1842 SimpleTensor<int8_t> rhs{ rhs_shape, data_type, 1 };
1843 SimpleTensor<int32_t> dst{ dst_shape, DataType::S32, 1 };
1844
1845 // Fill reference
1846 fill(lhs, 0);
1847 fill(rhs, 1);
1848
1849 return reference::gemmlowp_matrix_multiply_core<int32_t, int8_t>(lhs, rhs, dst_shape, 0, 0);
1850 }
1851 }
1852
1853 bool gemm_validated = true;
1854 TensorType _target{};
1855 SimpleTensor<int32_t> _reference{};
1856};
1857
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001858template <typename TensorType, typename AccessorType, typename ReshapeRHSOperatorType, typename GEMMFunctionType>
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001859class GEMMLowpMatrixMultiplyReshapedOnlyRHS3DValidationFixture : public framework::Fixture
1860{
1861public:
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001862 void setup(unsigned int m_w, unsigned int m_h, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0,
1863 unsigned int k0, unsigned int h0, bool interleave_rhs, bool transpose_rhs, DataType data_type)
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001864 {
1865 GEMMLHSMatrixInfo lhs_info;
1866 lhs_info.m0 = m0;
1867 lhs_info.k0 = k0;
1868
1869 GEMMRHSMatrixInfo rhs_info;
1870 rhs_info.n0 = n0;
1871 rhs_info.k0 = k0;
1872 rhs_info.h0 = h0;
1873 rhs_info.interleave = interleave_rhs;
1874 rhs_info.transpose = transpose_rhs;
1875
1876 // In case of GEMM3D, m is the product between m_w and m_h
1877 const unsigned int m = m_w * m_h;
1878
1879 // Set the tensor shapes for LHS and RHS matrices
1880 const TensorShape lhs_shape(k, m, batch_size);
1881 const TensorShape rhs_shape(n, k, batch_size);
1882
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001883 _target = compute_target(lhs_shape, rhs_shape, lhs_info, rhs_info, m_h, data_type);
1884 _reference = compute_reference(lhs_shape, rhs_shape, m_h, data_type);
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001885 }
1886
1887protected:
1888 template <typename U>
1889 void fill(U &&tensor, int i)
1890 {
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001891 switch(tensor.data_type())
1892 {
1893 case DataType::QASYMM8:
1894 {
1895 // Between 1 and 254 in order to avoid having -128 and 128 for the DOT product path
1896 std::uniform_int_distribution<> distribution(1, 254);
1897 library->fill(tensor, distribution, i);
1898 }
1899 break;
1900 case DataType::QASYMM8_SIGNED:
1901 {
1902 std::uniform_int_distribution<> distribution(-127, 126);
1903 library->fill(tensor, distribution, i);
1904 }
1905 break;
1906 default:
1907 ARM_COMPUTE_ERROR("Unsupported data type");
1908 }
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001909 }
1910
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001911 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const GEMMLHSMatrixInfo &lhs_info,
1912 const GEMMRHSMatrixInfo &rhs_info, unsigned int m_h, DataType data_type)
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001913 {
1914 // Create tensors
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001915 TensorType lhs = create_tensor<TensorType>(lhs_shape, data_type, 1);
1916 TensorType rhs = create_tensor<TensorType>(rhs_shape, data_type, 1);
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001917 TensorType rhs_reshaped;
1918 TensorType dst;
1919
1920 const unsigned int M = lhs_shape[1];
1921 const unsigned int N = rhs_shape[0];
1922 const unsigned int K = lhs_shape[0];
1923
Michele Di Giorgiob54ba282020-01-14 15:31:55 +00001924 GEMMKernelInfo gemm_info;
1925 gemm_info.m = M;
1926 gemm_info.n = N;
1927 gemm_info.k = K;
1928 gemm_info.depth_output_gemm3d = m_h;
1929 gemm_info.lhs_info = lhs_info;
1930 gemm_info.rhs_info = rhs_info;
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001931 // The output tensor will be auto-initialized within the function
1932
1933 // Create and configure function
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001934 ReshapeRHSOperatorType reshape_rhs;
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001935 GEMMFunctionType gemm;
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001936 reshape_rhs.configure(rhs.info(), rhs_reshaped.info(), rhs_info);
Georgios Pinitas4a578b92021-06-25 12:13:49 +01001937 gemm.configure(lhs.info(), rhs_reshaped.info(), dst.info(), gemm_info);
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001938
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001939 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
1940 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001941
Giorgio Arena63825e82021-03-25 14:54:50 +00001942 add_padding_x({ &lhs, &rhs, &rhs_reshaped, &dst });
1943
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001944 // Allocate tensors
1945 lhs.allocator()->allocate();
1946 rhs.allocator()->allocate();
1947 rhs_reshaped.allocator()->allocate();
1948 dst.allocator()->allocate();
1949
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01001950 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
1951 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
1952 ARM_COMPUTE_ASSERT(!rhs_reshaped.info()->is_resizable());
1953 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001954
1955 // Fill tensors
1956 fill(AccessorType(lhs), 0);
1957 fill(AccessorType(rhs), 1);
1958
1959 // Compute GEMM
Georgios Pinitas856f66e2021-04-22 21:13:21 +01001960 ITensorPack reshape_rhs_pack = { { ACL_SRC, &rhs }, { ACL_DST, &rhs_reshaped } };
1961 reshape_rhs.run(reshape_rhs_pack);
Georgios Pinitas4a578b92021-06-25 12:13:49 +01001962 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs }, { ACL_SRC_1, &rhs_reshaped }, { ACL_DST, &dst } });
1963 gemm.run(gemm_pack);
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001964
1965 return dst;
1966 }
1967
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001968 SimpleTensor<int32_t> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, unsigned int m_h, DataType data_type)
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001969 {
1970 TensorShape dst_shape = lhs_shape;
1971 dst_shape.set(0, rhs_shape[0]);
1972 dst_shape.set(1, lhs_shape[1] / m_h);
1973 dst_shape.set(2, m_h);
1974 dst_shape.set(3, lhs_shape[2]);
1975
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001976 if(data_type == DataType::QASYMM8)
1977 {
1978 // Create reference
1979 SimpleTensor<uint8_t> lhs{ lhs_shape, data_type, 1 };
1980 SimpleTensor<uint8_t> rhs{ rhs_shape, data_type, 1 };
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001981
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001982 // Fill reference
1983 fill(lhs, 0);
1984 fill(rhs, 1);
Gian Marco Iodice62251f72019-03-11 16:07:12 +00001985
Michele Di Giorgiof9179d32019-11-27 16:17:30 +00001986 return reference::gemmlowp_matrix_multiply_core<int32_t, uint8_t>(lhs, rhs, dst_shape, 0, 0);
1987 }
1988 else
1989 {
1990 // Create reference
1991 SimpleTensor<int8_t> lhs{ lhs_shape, data_type, 1 };
1992 SimpleTensor<int8_t> rhs{ rhs_shape, data_type, 1 };
1993
1994 // Fill reference
1995 fill(lhs, 0);
1996 fill(rhs, 1);
1997
1998 return reference::gemmlowp_matrix_multiply_core<int32_t, int8_t>(lhs, rhs, dst_shape, 0, 0);
1999 }
Gian Marco Iodice62251f72019-03-11 16:07:12 +00002000 }
2001
2002 TensorType _target{};
2003 SimpleTensor<int32_t> _reference{};
2004};
Gian Marco Iodicee7510622019-06-03 17:28:17 +01002005
2006template <typename TensorType, typename AccessorType, typename GEMMFunctionType>
2007class GEMMLowpMatrixMultiplyNativeValidationFixture : public framework::Fixture
2008{
2009public:
Gian Marco Iodicee7510622019-06-03 17:28:17 +01002010 void setup(unsigned int m, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0, unsigned int k0)
2011 {
2012 GEMMLHSMatrixInfo lhs_info;
2013 lhs_info.m0 = m0;
2014 lhs_info.k0 = k0;
2015
2016 GEMMRHSMatrixInfo rhs_info;
2017 rhs_info.n0 = n0;
2018 rhs_info.k0 = k0;
2019
2020 // Set the tensor shapes for LHS and RHS matrices
2021 const TensorShape lhs_shape(k, m, batch_size);
2022 const TensorShape rhs_shape(n, k, batch_size);
2023
2024 _target = compute_target(lhs_shape, rhs_shape, lhs_info, rhs_info);
2025 _reference = compute_reference(lhs_shape, rhs_shape);
2026 }
2027
2028protected:
2029 template <typename U>
2030 void fill(U &&tensor, int i)
2031 {
2032 // Between 1 and 254 in order to avoid having -128 and 128 for the DOT product path
2033 std::uniform_int_distribution<> distribution(1, 254);
2034 library->fill(tensor, distribution, i);
2035 }
2036
2037 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info)
2038 {
2039 // Create tensors
2040 TensorType lhs = create_tensor<TensorType>(lhs_shape, DataType::QASYMM8, 1);
2041 TensorType rhs = create_tensor<TensorType>(rhs_shape, DataType::QASYMM8, 1);
2042 TensorType dst;
2043
2044 const unsigned int M = lhs_shape[1];
2045 const unsigned int N = rhs_shape[0];
2046 const unsigned int K = lhs_shape[0];
2047
2048 // The output tensor will be auto-initialized within the function
2049
2050 // Create and configure function
2051 GEMMFunctionType gemm;
Georgios Pinitas4a578b92021-06-25 12:13:49 +01002052 gemm.configure(lhs.info(), rhs.info(), dst.info(), lhs_info, rhs_info, GEMMReshapeInfo(M, N, K));
Gian Marco Iodicee7510622019-06-03 17:28:17 +01002053
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01002054 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
2055 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
Gian Marco Iodicee7510622019-06-03 17:28:17 +01002056
Giorgio Arena63825e82021-03-25 14:54:50 +00002057 add_padding_x({ &lhs, &rhs, &dst });
2058
Gian Marco Iodicee7510622019-06-03 17:28:17 +01002059 // Allocate tensors
2060 lhs.allocator()->allocate();
2061 rhs.allocator()->allocate();
2062 dst.allocator()->allocate();
2063
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01002064 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
2065 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
2066 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Gian Marco Iodicee7510622019-06-03 17:28:17 +01002067
2068 // Fill tensors
2069 fill(AccessorType(lhs), 0);
2070 fill(AccessorType(rhs), 1);
2071
2072 // Compute GEMM
Georgios Pinitas4a578b92021-06-25 12:13:49 +01002073 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs }, { ACL_SRC_1, &rhs }, { ACL_DST, &dst } });
2074 gemm.run(gemm_pack);
Gian Marco Iodicee7510622019-06-03 17:28:17 +01002075
2076 return dst;
2077 }
2078
2079 SimpleTensor<int32_t> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape)
2080 {
2081 TensorShape dst_shape = lhs_shape;
2082 dst_shape[0] = rhs_shape[0];
2083 dst_shape[1] = lhs_shape[1];
2084
2085 // Create reference
2086 SimpleTensor<uint8_t> lhs{ lhs_shape, DataType::QASYMM8, 1 };
2087 SimpleTensor<uint8_t> rhs{ rhs_shape, DataType::QASYMM8, 1 };
2088
2089 // Fill reference
2090 fill(lhs, 0);
2091 fill(rhs, 1);
2092
2093 return reference::gemmlowp_matrix_multiply_core<int32_t, uint8_t>(lhs, rhs, dst_shape, 0, 0);
2094 }
2095
2096 TensorType _target{};
2097 SimpleTensor<int32_t> _reference{};
2098};
2099
2100template <typename TensorType, typename AccessorType, typename GEMMFunctionType>
2101class GEMMLowpMatrixMultiplyNative3DValidationFixture : public framework::Fixture
2102{
2103public:
Gian Marco Iodicee7510622019-06-03 17:28:17 +01002104 void setup(unsigned int m_w, unsigned int m_h, unsigned int n, unsigned int k, unsigned int batch_size, unsigned int m0, unsigned int n0, unsigned int k0)
2105 {
2106 GEMMLHSMatrixInfo lhs_info;
2107 lhs_info.m0 = m0;
2108 lhs_info.k0 = k0;
2109
2110 GEMMRHSMatrixInfo rhs_info;
2111 rhs_info.n0 = n0;
2112 rhs_info.k0 = k0;
2113
2114 // In case of GEMM3D, m is the product between m_w and m_h
2115 const unsigned int m = m_w * m_h;
2116
2117 // Set the tensor shapes for LHS and RHS matrices
2118 const TensorShape lhs_shape(k, m, batch_size);
2119 const TensorShape rhs_shape(n, k, batch_size);
2120
2121 _target = compute_target(lhs_shape, rhs_shape, lhs_info, rhs_info, m_h);
2122 _reference = compute_reference(lhs_shape, rhs_shape, m_h);
2123 }
2124
2125protected:
2126 template <typename U>
2127 void fill(U &&tensor, int i)
2128 {
2129 // Between 1 and 254 in order to avoid having -128 and 128 for the DOT product path
2130 std::uniform_int_distribution<> distribution(1, 254);
2131 library->fill(tensor, distribution, i);
2132 }
2133
2134 TensorType compute_target(const TensorShape &lhs_shape, const TensorShape &rhs_shape, const GEMMLHSMatrixInfo &lhs_info, const GEMMRHSMatrixInfo &rhs_info, unsigned int m_h)
2135 {
2136 // Create tensors
2137 TensorType lhs = create_tensor<TensorType>(lhs_shape, DataType::QASYMM8, 1);
2138 TensorType rhs = create_tensor<TensorType>(rhs_shape, DataType::QASYMM8, 1);
2139 TensorType dst;
2140
2141 const unsigned int M = lhs_shape[1];
2142 const unsigned int N = rhs_shape[0];
2143 const unsigned int K = lhs_shape[0];
2144
2145 // The output tensor will be auto-initialized within the function
2146
2147 // Create and configure function
2148 GEMMFunctionType gemm;
Georgios Pinitas4a578b92021-06-25 12:13:49 +01002149 gemm.configure(lhs.info(), rhs.info(), dst.info(), lhs_info, rhs_info, GEMMReshapeInfo(M, N, K, 1, 1, m_h));
Gian Marco Iodicee7510622019-06-03 17:28:17 +01002150
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01002151 ARM_COMPUTE_ASSERT(lhs.info()->is_resizable());
2152 ARM_COMPUTE_ASSERT(rhs.info()->is_resizable());
Gian Marco Iodicee7510622019-06-03 17:28:17 +01002153
Giorgio Arena63825e82021-03-25 14:54:50 +00002154 add_padding_x({ &lhs, &rhs, &dst });
2155
Gian Marco Iodicee7510622019-06-03 17:28:17 +01002156 // Allocate tensors
2157 lhs.allocator()->allocate();
2158 rhs.allocator()->allocate();
2159 dst.allocator()->allocate();
2160
Michele Di Giorgio4fc10b32021-04-30 18:30:41 +01002161 ARM_COMPUTE_ASSERT(!lhs.info()->is_resizable());
2162 ARM_COMPUTE_ASSERT(!rhs.info()->is_resizable());
2163 ARM_COMPUTE_ASSERT(!dst.info()->is_resizable());
Gian Marco Iodicee7510622019-06-03 17:28:17 +01002164
2165 // Fill tensors
2166 fill(AccessorType(lhs), 0);
2167 fill(AccessorType(rhs), 1);
2168
2169 // Compute GEMM
Georgios Pinitas4a578b92021-06-25 12:13:49 +01002170 ITensorPack gemm_pack({ { ACL_SRC_0, &lhs }, { ACL_SRC_1, &rhs }, { ACL_DST, &dst } });
2171 gemm.run(gemm_pack);
Gian Marco Iodicee7510622019-06-03 17:28:17 +01002172
2173 return dst;
2174 }
2175
2176 SimpleTensor<int32_t> compute_reference(const TensorShape &lhs_shape, const TensorShape &rhs_shape, unsigned int m_h)
2177 {
2178 TensorShape dst_shape = lhs_shape;
2179 dst_shape.set(0, rhs_shape[0]);
2180 dst_shape.set(1, lhs_shape[1] / m_h);
2181 dst_shape.set(2, m_h);
2182 dst_shape.set(3, lhs_shape[2]);
2183
2184 // Create reference
2185 SimpleTensor<uint8_t> lhs{ lhs_shape, DataType::QASYMM8, 1 };
2186 SimpleTensor<uint8_t> rhs{ rhs_shape, DataType::QASYMM8, 1 };
2187
2188 // Fill reference
2189 fill(lhs, 0);
2190 fill(rhs, 1);
2191
2192 return reference::gemmlowp_matrix_multiply_core<int32_t, uint8_t>(lhs, rhs, dst_shape, 0, 0);
2193 }
2194
2195 TensorType _target{};
2196 SimpleTensor<int32_t> _reference{};
2197};
Pablo Tello299025a2017-09-29 11:30:12 +01002198} // namespace validation
2199} // namespace test
2200} // namespace arm_compute
SiCong Li11ab4512023-11-07 12:04:59 +00002201#endif // ACL_TESTS_VALIDATION_FIXTURES_GEMMLOWPFIXTURE_H