Skip to content

Commit

Permalink
[onert-micro] Support Shape kernel (Samsung#10652)
Browse files Browse the repository at this point in the history
This pr supports Shape kernel and adds test for this kernel.

ONE-DCO-1.0-Signed-off-by: Artem Balyshev <[email protected]>

Co-authored-by: Artem Balyshev <[email protected]>
  • Loading branch information
BalyshevArtem and Artem Balyshev authored Apr 17, 2023
1 parent ebc5f6d commit 9cf3915
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 133 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ namespace luci_interpreter
namespace test_kernel
{

template <typename T> class TestDataBase
template <typename T, typename U = T> class TestDataBase
{
public:
virtual ~TestDataBase() = default;

virtual const unsigned char *get_model_ptr() = 0;

virtual const std::vector<T> &get_input_data_by_index(int i) = 0;
virtual const std::vector<T> &get_output_data_by_index(int i) = 0;
virtual const std::vector<U> &get_output_data_by_index(int i) = 0;
};

} // namespace test_kernel
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef LUCI_INTERPRETER_TEST_MODELS_SHAPE_KERNEL_H
#define LUCI_INTERPRETER_TEST_MODELS_SHAPE_KERNEL_H

#include "luci_interpreter/test_models/TestDataBase.h"

namespace luci_interpreter
{
namespace test_kernel
{

namespace shape_kernel
{
/*
* Shape Kernel:
*
* Input(2, 3, 4)
* |
* Shape
* |
* Output(3)
*/
const unsigned char test_kernel_model_circle[] = {
0x18, 0x00, 0x00, 0x00, 0x43, 0x49, 0x52, 0x30, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x2c, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff,
0xfc, 0xff, 0xff, 0xff, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00,
0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x64, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x08, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x69, 0x66, 0x6d, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x4d, 0x11, 0x00, 0x00, 0x00, 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69,
0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x00, 0x00, 0x00};

const std::vector<float> input_data = {
9.817013, -10.169584, -11.175514, 6.3500366, -39.949837, 2.3447914, 14.254675, 20.6128,
8.819141, -10.237312, -5.171467, 4.7246437, 11.657671, 20.094395, 11.213078, -13.8377495,
10.846771, -15.841316, 7.4385757, -6.9196777, 12.076214, 18.011564, -14.684473, 2.7402115};

const std::vector<int32_t> reference_output_data = {2, 3, 4};
} // namespace shape_kernel

template <typename T, typename U> class TestDataShapeKernel : public TestDataBase<T, U>
{
public:
TestDataShapeKernel()
{
_input_data = shape_kernel::input_data;
_reference_output_data = shape_kernel::reference_output_data;
_test_kernel_model_circle = shape_kernel::test_kernel_model_circle;
}

~TestDataShapeKernel() override = default;

const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; }

const std::vector<T> &get_input_data_by_index(int i) override final
{
switch (i)
{
case 0:
return _input_data;
default:
assert(false && "Wrong input index");
}
}

const std::vector<U> &get_output_data_by_index(int i) override final
{
assert(i == 0);
return _reference_output_data;
}

protected:
std::vector<T> _input_data;
std::vector<U> _reference_output_data;
const unsigned char *_test_kernel_model_circle;
};

} // namespace test_kernel
} // namespace luci_interpreter

#endif // LUCI_INTERPRETER_TEST_MODELS_SHAPE_KERNEL_H
1 change: 1 addition & 0 deletions onert-micro/luci-interpreter/pal/mcu/KernelsToBuild.lst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ REGISTER_KERNEL(LESS, Less)
REGISTER_KERNEL(MUL, Mul)
REGISTER_KERNEL(MAX_POOL_2D, MaxPool2D)
REGISTER_KERNEL(CONCATENATION, Concatenation)
REGISTER_KERNEL(SHAPE, Shape)
REGISTER_KERNEL(SLICE, Slice)
REGISTER_KERNEL(SUB, Sub)
REGISTER_KERNEL(SPLIT, Split)
Expand Down
55 changes: 16 additions & 39 deletions onert-micro/luci-interpreter/src/kernels/Shape.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
* Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -14,57 +14,34 @@
* limitations under the License.
*/

#include "kernels/Shape.h"
#include "Builders.h"
#include "SISOKernel.h"
#include "kernels/Utils.h"

namespace luci_interpreter
{
namespace kernels
void configure_kernel_CircleShape(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph)
{

ShapeKernel::ShapeKernel(const Tensor *input, Tensor *output, const ShapeParams &params)
: KernelWithParams<ShapeParams>({input}, {output}, params)
{
}

void ShapeKernel::configure()
{
LUCI_INTERPRETER_CHECK(output()->element_type() == DataType::S32 or
output()->element_type() == DataType::S64);
const auto input_shape = input()->shape();

Shape output_shape(1);
output_shape.dim(0) = input_shape.num_dims();
// TODO: enable it only if kernel with dynamic shapes
output()->resize(output_shape);
kernels::SISOKernel kernel(cur_op, runtime_graph);
LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.output()) == DataType::S32);
}

void ShapeKernel::execute() const
void execute_kernel_CircleShape(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph,
bool)
{
switch (params().out_type)
{
case DataType::S32:
evalInt<int32_t>();
break;
case DataType::S64:
evalInt<int64_t>();
break;
default:
assert(false && "Unsupported type.");
}
}
kernels::SISOKernel kernel(cur_op, runtime_graph);

template <typename T> void ShapeKernel::evalInt() const
{
const auto input_shape = input()->shape();
const circle::Tensor *input = kernel.input();
const circle::Tensor *output = kernel.output();

auto output_data = getTensorData<T>(output());
assert(Tensor::element_type(output) == DataType::S32);
int32_t *output_data = kernels::getTensorData<int32_t>(runtime_graph->getDataByTensor(output));

for (int i = 0; i < input_shape.num_dims(); ++i)
const int rank = Tensor::num_dims(input);
for (int i = 0; i < rank; ++i)
{
output_data[i] = input_shape.dim(i);
output_data[i] = Tensor::dim(input, i);
}
}

} // namespace kernels
} // namespace luci_interpreter
46 changes: 0 additions & 46 deletions onert-micro/luci-interpreter/src/kernels/Shape.h

This file was deleted.

78 changes: 32 additions & 46 deletions onert-micro/luci-interpreter/src/kernels/Shape.test.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
* Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -14,76 +14,62 @@
* limitations under the License.
*/

#include "kernels/Shape.h"
#include "kernels/TestUtils.h"
#include "luci_interpreter/TestMemoryManager.h"
#include "luci_interpreter/test_models/shape/ShapeKernel.h"

#include "loader/ModuleLoader.h"

namespace luci_interpreter
{
namespace kernels
{
namespace
{

using namespace testing;

class ShapeTest : public ::testing::Test
{
protected:
void SetUp() override { _memory_manager = std::make_unique<TestMemoryManager>(); }

std::unique_ptr<IMemoryManager> _memory_manager;
// Do nothing
};

template <typename T> void runShapeKernel(loco::DataType dataType, IMemoryManager *memory_manager)
template <typename T, typename U>
std::vector<U> checkShapeKernel(test_kernel::TestDataBase<T, U> *test_data_base)
{
Shape input_shape{1, 3, 1, 3, 5};
MemoryManager memory_manager{};
RuntimeModule runtime_module{};
bool dealloc_input = true;

Tensor input_tensor = Tensor(loco::DataType::FLOAT32, input_shape, {}, "");
Tensor output_tensor = makeOutputTensor(dataType);
// 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);

ShapeParams params{};
params.out_type = dataType;
auto *main_runtime_graph = runtime_module.getMainGraph();
assert(main_runtime_graph->getNumOfInputTensors() == 1);

ShapeKernel kernel(&input_tensor, &output_tensor, params);
// Set 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);
}

kernel.configure();
memory_manager->allocate_memory(output_tensor);
kernel.execute();
runtime_module.execute();

std::vector<T> ref_output_data{1, 3, 1, 3, 5};
EXPECT_THAT(extractTensorData<T>(output_tensor), ref_output_data);
assert(main_runtime_graph->getNumOfOutputTensors() == 1);

std::vector<int32_t> ref_output_shape{5};
EXPECT_THAT(extractTensorShape(output_tensor), ::testing::ElementsAreArray(ref_output_shape));
U *output_data = reinterpret_cast<U *>(main_runtime_graph->getOutputDataByIndex(0));
const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T));
std::vector<U> output_data_vector(output_data, output_data + num_elements);
return output_data_vector;
}

TEST_F(ShapeTest, OutTypeInt)
TEST_F(ShapeTest, MainTest_P)
{

// Run for int32_t output
runShapeKernel<int32_t>(loco::DataType::S32, _memory_manager.get());
// Run for int64_t output
runShapeKernel<int64_t>(loco::DataType::S64, _memory_manager.get());

SUCCEED();
test_kernel::TestDataShapeKernel<float, int32_t> test_data_shape_kernel;
std::vector<int32_t> output_data_vector = checkShapeKernel(&test_data_shape_kernel);
EXPECT_THAT(output_data_vector, test_data_shape_kernel.get_output_data_by_index(0));
}

TEST_F(ShapeTest, Invalid_Output_Type_NEG)
{
Shape input_shape{1, 3};

Tensor input_tensor = Tensor(loco::DataType::FLOAT32, input_shape, {}, "");
Tensor output_tensor = makeOutputTensor(loco::DataType::FLOAT32);

ShapeParams params{};
params.out_type = loco::DataType::FLOAT32;

ShapeKernel kernel(&input_tensor, &output_tensor, params);

EXPECT_ANY_THROW(kernel.configure());
}
// TODO: add negative tests?

} // namespace
} // namespace kernels
} // namespace luci_interpreter

0 comments on commit 9cf3915

Please sign in to comment.