* limitations under the License.
*/
-#include "kernels/Sub.h"
#include "kernels/TestUtils.h"
-#include "luci_interpreter/TestMemoryManager.h"
+#include "luci_interpreter/test_models/sub/FloatSubKernel.h"
+#include "luci_interpreter/test_models/sub/IntSubKernel.h"
+#include "luci_interpreter/test_models/sub/NegSubKernel.h"
-#include <algorithm>
+#include "loader/ModuleLoader.h"
namespace luci_interpreter
{
-namespace kernels
-{
namespace
{
using namespace testing;
-using std::pair;
-using std::vector;
-using std::transform;
-using std::initializer_list;
class SubTest : public ::testing::Test
{
-protected:
- void SetUp() override { _memory_manager = std::make_unique<TestMemoryManager>(); }
-
- std::unique_ptr<IMemoryManager> _memory_manager;
+ // Do nothing
};
-// for quantized Add, the error shouldn't exceed step
-float GetTolerance(float min, float max)
-{
- float kQuantizedStep = (max - min) / 255.0;
- return kQuantizedStep;
-}
-
-TEST_F(SubTest, Uint8)
+template <typename T> std::vector<T> checkSubKernel(test_kernel::TestDataBase<T> *test_data_base)
{
- Shape base_shape = {2, 3, 1, 2};
- vector<float> base_data = {-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f,
- 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f};
- vector<Shape> test_shapes = {{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
- vector<float> test_data = {0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
- vector<vector<int32_t>> output_shapes = {{2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}};
- vector<vector<float>> output_data = {
- {-0.5f, 2.0f, 0.1f, 1.8f, -1.3f, 1.4f, 0.7f, 0.2f, 1.3f, 0.0f, -0.1f, -0.4f,
- 0.6f, -1.4f, 1.2f, -1.6f, -0.2f, -2.0f, 1.0f, 2.5f, 1.6f, 2.3f, 0.2f, 1.9f,
- -1.8f, -0.3f, -1.2f, -0.5f, -2.6f, -0.9f, 0.5f, -2.5f, 1.1f, -2.7f, -0.3f, -3.0f},
- {-0.5f, 2.0f, 1.3f, 0.0f, -0.2f, -2.0f, 1.0f, 2.5f, -1.2f, -0.5f, -0.3f, -3.0f},
- {-0.5f, 2.1f, -0.6f, 2.0f, 0.1f, 2.7f, 0.7f, 0.3f, 0.6f, 0.2f, 1.3f, 0.9f,
- 0.6f, -1.3f, 0.5f, -1.4f, 1.2f, -0.7f, 0.7f, 2.3f, 0.2f, 1.8f, 0.3f, 1.9f,
- -2.1f, -0.5f, -2.6f, -1.0f, -2.5f, -0.9f, 0.2f, -2.7f, -0.3f, -3.0f, -0.2f, -3.0f},
- {-0.5f, 2.1f, 0.6f, 0.2f, 1.2f, -0.7f, 0.7f, 2.3f, -2.6f, -1.0f, -0.2f, -3.0f}};
+ MemoryManager memory_manager{};
+ RuntimeModule runtime_module{};
+ bool dealloc_input = true;
- float kQuantizedTolerance = GetTolerance(-3.f, 3.f);
- pair<float, int32_t> quant_param = quantizationParams<uint8_t>(-3.f, 3.f);
- for (size_t i = 0; i < output_data.size(); ++i)
- {
- Tensor input1_tensor = makeInputTensor<DataType::U8>(
- base_shape, quant_param.first, quant_param.second, base_data, _memory_manager.get());
- Tensor input2_tensor = makeInputTensor<DataType::U8>(
- test_shapes[i], quant_param.first, quant_param.second, test_data, _memory_manager.get());
- Tensor output_tensor =
- makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second);
-
- SubParams params{};
- params.activation = Activation::NONE;
+ // Load model with single op
+ auto *model_data_raw = reinterpret_cast<const char *>(test_data_base->get_model_ptr());
+ ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input);
- Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
- kernel.configure();
- _memory_manager->allocate_memory(output_tensor);
- kernel.execute();
+ auto *main_runtime_graph = runtime_module.getMainGraph();
+ assert(main_runtime_graph->getNumOfInputTensors() == 2);
- EXPECT_THAT(dequantizeTensorData(output_tensor),
- FloatArrayNear(output_data[i], kQuantizedTolerance));
- EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i]));
+ // set left input data
+ {
+ auto *input_tensor_data = reinterpret_cast<T *>(main_runtime_graph->configureGraphInput(0));
+ std::copy(test_data_base->get_input_data_by_index(0).begin(),
+ test_data_base->get_input_data_by_index(0).end(), input_tensor_data);
}
- // Inversion step for output_data, because subtract is not commutative operation
- auto multiply = [](auto &i) {
- transform(i.begin(), i.end(), i.begin(), [](auto &value) { return value * -1.0f; });
- };
- for_each(output_data.begin(), output_data.end(), multiply);
-
- // Re-run with exchanged inputs.
- for (size_t i = 0; i < output_data.size(); ++i)
+ // set right input data
{
- Tensor input1_tensor = makeInputTensor<DataType::U8>(
- test_shapes[i], quant_param.first, quant_param.second, test_data, _memory_manager.get());
- Tensor input2_tensor = makeInputTensor<DataType::U8>(
- base_shape, quant_param.first, quant_param.second, base_data, _memory_manager.get());
- Tensor output_tensor =
- makeOutputTensor(getElementType<uint8_t>(), quant_param.first, quant_param.second);
+ auto *input_tensor_data = reinterpret_cast<T *>(main_runtime_graph->configureGraphInput(1));
+ std::copy(test_data_base->get_input_data_by_index(1).begin(),
+ test_data_base->get_input_data_by_index(1).end(), input_tensor_data);
+ }
- SubParams params{};
- params.activation = Activation::NONE;
+ runtime_module.execute();
- Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
- kernel.configure();
- _memory_manager->allocate_memory(output_tensor);
- kernel.execute();
+ assert(main_runtime_graph->getNumOfOutputTensors() == 1);
- EXPECT_THAT(dequantizeTensorData(output_tensor),
- FloatArrayNear(output_data[i], kQuantizedTolerance));
- EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i]));
- }
+ T *output_data = reinterpret_cast<T *>(main_runtime_graph->getOutputDataByIndex(0));
+ const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T));
+ std::vector<T> output_data_vector(output_data, output_data + num_elements);
+ return output_data_vector;
}
-TEST_F(SubTest, Float)
+TEST_F(SubTest, Float_P)
{
- Shape base_shape = {2, 3, 1, 2};
- vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
- vector<vector<int32_t>> output_shapes{{2, 3, 3, 2}, {2, 3, 1, 2}, {2, 3, 3, 2}, {2, 3, 1, 2}};
- vector<vector<float>> test_outputs = {
- {0.0f, 2.0f, 0.1f, 1.8f, 0.0f, 1.4f, 0.7f, 0.2f, 1.3f, 0.0f, 0.0f, 0.0f,
- 0.6f, 0.0f, 1.2f, 0.0f, 0.0f, 0.0f, 1.0f, 2.5f, 1.6f, 2.3f, 0.2f, 1.9f,
- 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 1.1f, 0.0f, 0.0f, 0.0f},
- {0.0f, 2.0f, 1.3f, 0.0f, 0.0f, 0.0f, 1.0f, 2.5f, 0.0f, 0.0f, 0.0f, 0.0f},
- {0.0f, 2.1f, 0.0f, 2.0f, 0.1f, 2.7f, 0.7f, 0.3f, 0.6f, 0.2f, 1.3f, 0.9f,
- 0.6f, 0.0f, 0.5f, 0.0f, 1.2f, 0.0f, 0.7f, 2.3f, 0.2f, 1.8f, 0.3f, 1.9f,
- 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
- {0.0f, 2.1f, 0.6f, 0.2f, 1.2f, 0.0f, 0.7f, 2.3f, 0.0f, 0.0f, 0.0f, 0.0f}};
-
- vector<float> input1_data{-0.3f, 2.3f, 0.9f, 0.5f, 0.8f, -1.1f,
- 1.2f, 2.8f, -1.6f, 0.0f, 0.7f, -2.2f};
- vector<float> input2_data{0.2f, 0.3f, -0.4f, 0.5f, 1.0f, 0.9f};
- for (size_t i = 0; i < test_shapes.size(); ++i)
+ // No broadcast
{
- Tensor input1_tensor =
- makeInputTensor<DataType::FLOAT32>(base_shape, input1_data, _memory_manager.get());
- Tensor input2_tensor =
- makeInputTensor<DataType::FLOAT32>(test_shapes[i], input2_data, _memory_manager.get());
- Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
-
- SubParams params{};
- params.activation = Activation::RELU;
-
- Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
- kernel.configure();
- _memory_manager->allocate_memory(output_tensor);
- kernel.execute();
-
- EXPECT_THAT(extractTensorData<float>(output_tensor), FloatArrayNear(test_outputs[i], 0.0001f))
- << "With shape number " << i;
-
- EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(output_shapes[i]));
+ const bool is_with_broadcast = false;
+ test_kernel::TestDataFloatSub test_data_float_kernel(is_with_broadcast);
+ std::vector<float> output_data_vector = checkSubKernel(&test_data_float_kernel);
+ EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear(
+ test_data_float_kernel.get_output_data_by_index(0), 0.0001f));
}
-}
-
-template <loco::DataType DType> void CheckInteger(luci_interpreter::IMemoryManager *memory_manager)
-{
- using dtype = typename loco::DataTypeImpl<DType>::Type;
- Shape base_shape = {2, 3, 1, 2};
- std::vector<Shape> test_shapes{{1, 1, 3, 2}, {1, 3, 1, 2}, {2, 1, 3, 1}, {2, 3, 1, 1}};
- std::vector<std::vector<dtype>> test_outputs = {
- {0, 1, 2, 3, 0, 0, 0, 0, 4, 1, 0, 0, 0, 0, 7, 0, 3, 0,
- 0, 2, 4, 4, 0, 0, 3, 0, 10, 0, 6, 0, 3, 0, 10, 2, 6, 0},
- {0, 1, 4, 1, 3, 0, 0, 2, 10, 0, 6, 0},
- {0, 0, 0, 1, 2, 5, 0, 0, 0, 0, 4, 3, 0, 0, 3, 0, 7, 0,
- 2, 4, 0, 2, 0, 0, 8, 0, 6, 0, 1, 0, 8, 2, 6, 0, 1, 0},
- {0, 0, 0, 0, 7, 0, 2, 4, 6, 0, 1, 0}};
- std::vector<dtype> input1_data{-1, 2, 1, 0, 4, -5, 1, 3, 7, -1, 7, 1};
- std::vector<dtype> input2_data{4, 1, -3, -1, 1, 6};
- for (size_t i = 0; i < test_shapes.size(); ++i)
+ // With broadcast
{
- Tensor input1_tensor = makeInputTensor<DType>(base_shape, input1_data, memory_manager);
- Tensor input2_tensor = makeInputTensor<DType>(test_shapes[i], input2_data, memory_manager);
- Tensor output_tensor = makeOutputTensor(DType);
-
- SubParams params{};
- params.activation = Activation::RELU;
-
- Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
- kernel.configure();
- memory_manager->allocate_memory(output_tensor);
- kernel.execute();
-
- EXPECT_THAT(extractTensorData<dtype>(output_tensor), test_outputs[i])
- << "With shape number " << i;
+ const bool is_with_broadcast = true;
+ test_kernel::TestDataFloatSub test_data_float_kernel(is_with_broadcast);
+ std::vector<float> output_data_vector = checkSubKernel(&test_data_float_kernel);
+ EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear(
+ test_data_float_kernel.get_output_data_by_index(0), 0.0001f));
}
-};
-
-TEST_F(SubTest, SInt32)
-{
- CheckInteger<loco::DataType::S32>(_memory_manager.get());
- SUCCEED();
}
-TEST_F(SubTest, SInt64)
+TEST_F(SubTest, INT_P)
{
- CheckInteger<loco::DataType::S64>(_memory_manager.get());
- SUCCEED();
+ // No broadcast
+ {
+ const bool is_with_broadcast = false;
+ test_kernel::TestDataIntSub test_data_kernel(is_with_broadcast);
+ const auto output_data_vector = checkSubKernel<int32_t>(&test_data_kernel);
+ EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0));
+ }
+ // With broadcast
+ {
+ const bool is_with_broadcast = true;
+ test_kernel::TestDataIntSub test_data_kernel(is_with_broadcast);
+ const auto output_data_vector = checkSubKernel<int32_t>(&test_data_kernel);
+ EXPECT_THAT(output_data_vector, test_data_kernel.get_output_data_by_index(0));
+ }
}
-TEST_F(SubTest, Input_Output_Type_NEG)
+TEST_F(SubTest, Inputs_type_mismatch_NEG)
{
- Tensor input1_tensor = makeInputTensor<DataType::FLOAT32>({1}, {1.f}, _memory_manager.get());
- Tensor input2_tensor = makeInputTensor<DataType::S32>({1}, {2}, _memory_manager.get());
- Tensor output_tensor = makeOutputTensor(DataType::FLOAT32);
-
- SubParams params{};
- params.activation = Activation::RELU;
-
- Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
- EXPECT_ANY_THROW(kernel.configure());
+ test_kernel::NegTestDataInputsTypeMismatchSubKernel test_data_kernel;
+ MemoryManager memory_manager{};
+ RuntimeModule runtime_module{};
+ bool dealloc_input = true;
+ // Load model with single op
+ auto *model_data_raw = reinterpret_cast<const char *>(test_data_kernel.get_model_ptr());
+ EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input),
+ "");
}
-TEST_F(SubTest, Invalid_Output_Type_NEG)
+TEST_F(SubTest, Input_output_type_mismatch_NEG)
{
- Tensor input1_tensor = makeInputTensor<DataType::S64>({1}, {1}, _memory_manager.get());
- Tensor input2_tensor = makeInputTensor<DataType::S64>({1}, {2}, _memory_manager.get());
- Tensor output_tensor = makeOutputTensor(DataType::S32);
-
- SubParams params{};
- params.activation = Activation::RELU;
-
- Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
- EXPECT_ANY_THROW(kernel.configure());
+ test_kernel::NegTestDataInputOutputTypeMismatchSubKernel test_data_kernel;
+ MemoryManager memory_manager{};
+ RuntimeModule runtime_module{};
+ bool dealloc_input = true;
+ // Load model with single op
+ auto *model_data_raw = reinterpret_cast<const char *>(test_data_kernel.get_model_ptr());
+ EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input),
+ "");
}
-TEST_F(SubTest, Invalid_Input_Type_NEG)
+TEST_F(SubTest, No_quant_params_NEG)
{
- Tensor input1_tensor = makeInputTensor<DataType::U64>({1}, {1}, _memory_manager.get());
- Tensor input2_tensor = makeInputTensor<DataType::U64>({1}, {2}, _memory_manager.get());
- Tensor output_tensor = makeOutputTensor(DataType::U64);
-
- SubParams params{};
- params.activation = Activation::RELU;
-
- Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
- kernel.configure();
- _memory_manager->allocate_memory(output_tensor);
- EXPECT_ANY_THROW(kernel.execute());
+ test_kernel::NegTestDataNoQuantParamsSubKernel test_data_kernel;
+ MemoryManager memory_manager{};
+ RuntimeModule runtime_module{};
+ bool dealloc_input = true;
+ // Load model with single op
+ auto *model_data_raw = reinterpret_cast<const char *>(test_data_kernel.get_model_ptr());
+ EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input),
+ "");
}
-TEST_F(SubTest, Mismatching_Input_Int_Types_NEG)
-{
- Tensor input1_tensor = makeInputTensor<DataType::S32>({1}, {1}, _memory_manager.get());
- Tensor input2_tensor = makeInputTensor<DataType::S64>({1}, {2}, _memory_manager.get());
- Tensor output_tensor = makeOutputTensor(DataType::S32);
-
- SubParams params{};
- params.activation = Activation::NONE;
-
- Sub kernel(&input1_tensor, &input2_tensor, &output_tensor, params);
- EXPECT_ANY_THROW(kernel.configure());
-}
+// TODO: add tests for U8 and S16
+// TODO: add tests for inplace optimizations for all types
} // namespace
-} // namespace kernels
} // namespace luci_interpreter