2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
3 * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 #include "kernels/Mul.h"
19 #include "kernels/TestUtils.h"
20 #include "luci_interpreter/TestMemoryManager.h"
22 namespace luci_interpreter
29 using namespace testing;
31 class MulTest : public ::testing::Test
34 void SetUp() override { _memory_manager = std::make_unique<TestMemoryManager>(); }
36 std::unique_ptr<IMemoryManager> _memory_manager;
39 TEST_F(MulTest, Float)
41 Shape base_shape = {2, 3, 1, 2};
42 std::vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
43 std::vector<std::vector<float>> test_outputs = {
44 {0.00f, 0.69f, 0.12f, 1.15f, 0.00f, 2.07f, 0.18f, 0.15f, 0.00f, 0.25f, 0.90f, 0.45f,
45 0.16f, 0.00f, 0.00f, 0.00f, 0.80f, 0.00f, 0.24f, 0.84f, 0.00f, 1.40f, 1.20f, 2.52f,
46 0.00f, 0.00f, 0.64f, 0.00f, 0.00f, 0.00f, 0.14f, 0.00f, 0.00f, 0.00f, 0.70f, 0.00f},
47 {0.00f, 0.69f, 0.00f, 0.25f, 0.80f, 0.00f, 0.24f, 0.84f, 0.64f, 0.00f, 0.70f, 0.00f},
48 {0.00f, 0.46f, 0.00f, 0.69f, 0.12f, 0.00f, 0.18f, 0.10f, 0.27f, 0.15f, 0.00f, 0.00f,
49 0.16f, 0.00f, 0.24f, 0.00f, 0.00f, 0.44f, 0.60f, 1.40f, 1.20f, 2.80f, 1.08f, 2.52f,
50 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.35f, 0.00f, 0.70f, 0.00f, 0.63f, 0.00f},
51 {0.00f, 0.46f, 0.27f, 0.15f, 0.00f, 0.44f, 0.60f, 1.40f, 0.00f, 0.00f, 0.63f, 0.00f}};
52 std::vector<float> input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f,
53 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f};
54 std::vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
55 for (size_t i = 0; i < test_shapes.size(); ++i)
57 Tensor input1_tensor =
58 makeInputTensor<DataType::FLOAT32>(base_shape, input1_data, _memory_manager.get());
59 Tensor input2_tensor =
60 makeInputTensor<DataType::FLOAT32>(test_shapes[i], input2_data, _memory_manager.get());
61 Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
64 params.activation = Activation::RELU;
66 Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
68 _memory_manager->allocate_memory(output_tensor);
71 EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f))
72 << "With shape number " << i;
74 // Re-run with exchanged inputs.
75 for (size_t i = 0; i < test_shapes.size(); ++i)
77 Tensor input1_tensor =
78 makeInputTensor<DataType::FLOAT32>(test_shapes[i], input2_data, _memory_manager.get());
79 Tensor input2_tensor =
80 makeInputTensor<DataType::FLOAT32>(base_shape, input1_data, _memory_manager.get());
81 Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
84 params.activation = Activation::RELU;
86 Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
88 _memory_manager->allocate_memory(output_tensor);
91 EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f))
92 << "With shape number " << i;
96 template <loco::DataType DType> void checkInteger(luci_interpreter::IMemoryManager *memory_manager)
98 using dtype = typename loco::DataTypeImpl<DType>::Type;
99 Shape base_shape = {2, 3, 1, 2};
100 std::vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
102 dtype max_value = std::numeric_limits<dtype>::max();
103 dtype res_max = max_value - max_value % 10;
105 std::vector<std::vector<dtype>> test_outputs = {
106 {8, 0, 20, 0, 4, 30, //
107 16, 0, 40, 3, 8, 0, //
109 4, 0, 10, 9, 2, 0, //
110 40, 0, 100, 0, 20, 150, //
111 28, 0, 70, 0, 14, res_max},
112 {8, 0, 40, 3, 0, 0, 4, 0, 100, 0, 14, res_max},
113 {8, 12, 0, 0, 20, 30, 16, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0,
114 0, 0, 9, 2, 0, 10, 0, 0, 0, 20, 30, 100, 150, 0, 0, 14, max_value / 10 * 2,
116 {8, 12, 0, 0, 0, 0, 0, 9, 20, 30, 70, res_max}};
117 std::vector<dtype> input1_data{2, 3, 4, -1, -3, -2, 1, -3, 10, 15, 7, max_value / 10};
118 std::vector<dtype> input2_data{4, 0, 10, -3, 2, 10};
119 for (size_t i = 0; i < test_shapes.size(); ++i)
121 Tensor input1_tensor = makeInputTensor<DType>(base_shape, input1_data, memory_manager);
122 Tensor input2_tensor = makeInputTensor<DType>(test_shapes[i], input2_data, memory_manager);
123 Tensor output_tensor = makeOutputTensor(DType);
126 params.activation = Activation::RELU;
128 Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
130 memory_manager->allocate_memory(output_tensor);
133 EXPECT_THAT(extractTensorData<dtype>(output_tensor), test_outputs[i])
134 << "With shape number " << i;
136 // Re-run with exchanged inputs.
137 for (size_t i = 0; i < test_shapes.size(); ++i)
139 Tensor input1_tensor = makeInputTensor<DType>(test_shapes[i], input2_data, memory_manager);
140 Tensor input2_tensor = makeInputTensor<DType>(base_shape, input1_data, memory_manager);
141 Tensor output_tensor = makeOutputTensor(DType);
144 params.activation = Activation::RELU;
146 Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
148 memory_manager->allocate_memory(output_tensor);
151 EXPECT_THAT(extractTensorData<dtype>(output_tensor), test_outputs[i])
152 << "With shape number " << i;
156 TEST_F(MulTest, SInt64)
158 checkInteger<loco::DataType::S64>(_memory_manager.get());
162 TEST_F(MulTest, SInt32)
164 checkInteger<loco::DataType::S32>(_memory_manager.get());
168 TEST_F(MulTest, SInt16)
170 Shape base_shape = {2, 3, 1, 2};
171 std::vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
172 std::vector<std::vector<int32_t>> ref_output_shapes{
173 {2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}};
175 std::vector<float> input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f,
176 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f};
177 std::vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
178 std::vector<std::vector<float>> ref_outputs = {
179 {0.00f, 0.69f, 0.12f, 1.15f, 0.00f, 2.07f, 0.18f, 0.15f, 0.00f, 0.25f, 0.90f, 0.45f,
180 0.16f, 0.00f, 0.00f, 0.00f, 0.80f, 0.00f, 0.24f, 0.84f, 0.00f, 1.40f, 1.20f, 2.52f,
181 0.00f, 0.00f, 0.64f, 0.00f, 0.00f, 0.00f, 0.14f, 0.00f, 0.00f, 0.00f, 0.70f, 0.00f},
182 {0.00f, 0.69f, 0.00f, 0.25f, 0.80f, 0.00f, 0.24f, 0.84f, 0.64f, 0.00f, 0.70f, 0.00f},
183 {0.00f, 0.46f, 0.00f, 0.69f, 0.12f, 0.00f, 0.18f, 0.10f, 0.27f, 0.15f, 0.00f, 0.00f,
184 0.16f, 0.00f, 0.24f, 0.00f, 0.00f, 0.44f, 0.60f, 1.40f, 1.20f, 2.80f, 1.08f, 2.52f,
185 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.35f, 0.00f, 0.70f, 0.00f, 0.63f, 0.00f},
186 {0.00f, 0.46f, 0.27f, 0.15f, 0.00f, 0.44f, 0.60f, 1.40f, 0.00f, 0.00f, 0.63f, 0.00f}};
187 for (size_t i = 0; i < test_shapes.size(); ++i)
189 Tensor input1_tensor = makeInputTensor<DataType::S16>(base_shape, 3.0 / 32767, 0, input1_data,
190 _memory_manager.get());
191 Tensor input2_tensor = makeInputTensor<DataType::S16>(test_shapes[i], 1.0 / 32767, 0,
192 input2_data, _memory_manager.get());
193 Tensor output_tensor = makeOutputTensor(DataType::S16, 4.0 / 32767, 0);
194 const float tolerance = output_tensor.scale() * 2;
197 params.activation = Activation::RELU;
199 Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
201 _memory_manager->allocate_memory(output_tensor);
204 EXPECT_THAT(extractTensorShape(output_tensor),
205 ::testing::ElementsAreArray(ref_output_shapes[i]))
206 << "With shape number " << i;
207 EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance))
208 << "With shape number " << i;
210 // Re-run with exchanged inputs and different scales.
211 for (size_t i = 0; i < test_shapes.size(); ++i)
213 Tensor input1_tensor = makeInputTensor<DataType::S16>(test_shapes[i], 2.0 / 32767, 0,
214 input2_data, _memory_manager.get());
215 Tensor input2_tensor = makeInputTensor<DataType::S16>(base_shape, 4.0 / 32767, 0, input1_data,
216 _memory_manager.get());
217 Tensor output_tensor = makeOutputTensor(DataType::S16, 3.0 / 32767, 0);
218 const float tolerance = output_tensor.scale() * 2;
221 params.activation = Activation::RELU;
223 Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
225 _memory_manager->allocate_memory(output_tensor);
228 EXPECT_THAT(extractTensorShape(output_tensor),
229 ::testing::ElementsAreArray(ref_output_shapes[i]))
230 << "With shape number " << i;
231 EXPECT_THAT(dequantizeTensorData(output_tensor), FloatArrayNear(ref_outputs[i], tolerance))
232 << "With shape number " << i;
236 TEST_F(MulTest, Input_Output_Type_NEG)
238 Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f}, _memory_manager.get());
239 Tensor input2_tensor = makeInputTensor<DataType::S32>({1}, {2}, _memory_manager.get());
240 Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
243 params.activation = Activation::RELU;
245 Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
246 EXPECT_ANY_THROW(kernel.configure());
249 TEST_F(MulTest, Invalid_Output_Type_NEG)
251 Tensor input1_tensor = makeInputTensor<DataType::S64>({1}, {1}, _memory_manager.get());
252 Tensor input2_tensor = makeInputTensor<DataType::S64>({1}, {2}, _memory_manager.get());
253 Tensor output_tensor = makeOutputTensor(DataType::S32);
256 params.activation = Activation::RELU;
258 Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
259 EXPECT_ANY_THROW(kernel.configure());
262 TEST_F(MulTest, Invalid_Input_Type_NEG)
264 Tensor input1_tensor = makeInputTensor<DataType::U64>({1}, {1}, _memory_manager.get());
265 Tensor input2_tensor = makeInputTensor<DataType::U64>({1}, {2}, _memory_manager.get());
266 Tensor output_tensor = makeOutputTensor(DataType::U64);
269 params.activation = Activation::RELU;
271 Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
273 _memory_manager->allocate_memory(output_tensor);
274 EXPECT_ANY_THROW(kernel.execute());
277 TEST_F(MulTest, Invalid_Quantization_NEG)
279 Tensor input1_tensor = makeInputTensor<DataType::S16>({1}, {1}, _memory_manager.get());
280 Tensor input2_tensor = makeInputTensor<DataType::S16>({1}, {2}, _memory_manager.get());
281 Tensor output_tensor = makeOutputTensor(DataType::S16);
284 params.activation = Activation::NONE;
286 Mul kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
287 EXPECT_ANY_THROW(kernel.configure());
291 } // namespace kernels
292 } // namespace luci_interpreter