From 85d16ca98599c0058afb5b6baf2c77c7b098d486 Mon Sep 17 00:00:00 2001 From: Balyshev Artem <43214667+BalyshevArtem@users.noreply.github.com> Date: Wed, 4 Oct 2023 17:34:18 +0300 Subject: [PATCH] [onert-micro] Add float L2Pool2D kernels (#11636) This commit adds float L2Pool2D kernels for onert-micro. ONE-DCO-1.0-Signed-off-by: Artem Balyshev Co-authored-by: Artem Balyshev --- .../l2_poop_2d/FloatL2Pool2DKernel.h | 101 +++++++ .../l2_poop_2d/NegL2Pool2DKernel.h | 88 ++++++ .../l2_poop_2d/TestDataL2Pool2DBase.h | 60 ++++ .../pal/cmsisnn/KernelsToBuild.lst | 1 + .../luci-interpreter/pal/common/PALL2Pool2D.h | 81 +++++ .../pal/mcu/KernelsToBuild.lst | 1 + .../luci-interpreter/src/kernels/L2Pool2D.cpp | 109 ++++--- .../luci-interpreter/src/kernels/L2Pool2D.h | 49 --- .../src/kernels/L2Pool2D.test.cpp | 285 +++--------------- 9 files changed, 432 insertions(+), 343 deletions(-) create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/l2_poop_2d/FloatL2Pool2DKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/l2_poop_2d/NegL2Pool2DKernel.h create mode 100644 onert-micro/luci-interpreter/include/luci_interpreter/test_models/l2_poop_2d/TestDataL2Pool2DBase.h create mode 100644 onert-micro/luci-interpreter/pal/common/PALL2Pool2D.h delete mode 100644 onert-micro/luci-interpreter/src/kernels/L2Pool2D.h diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/l2_poop_2d/FloatL2Pool2DKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/l2_poop_2d/FloatL2Pool2DKernel.h new file mode 100644 index 00000000000..696948ec8a2 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/l2_poop_2d/FloatL2Pool2DKernel.h @@ -0,0 +1,101 @@ +/* + * 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_FLOAT_L2_POOL_2D_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_FLOAT_L2_POOL_2D_KERNEL_H + +#include "TestDataL2Pool2DBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace l2_pool_2d_float +{ +/* + * L2Pool2D Kernel: + * + * Input(1, 8, 8, 1) + * | + * L2Pool2D + * | + * Output(1, 7, 7, 1) + */ +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, 0x48, 0x01, 0x00, 0x00, 0x64, 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, + 0x7c, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x84, 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, 0x05, 0x1c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x17, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 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, + 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd4, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6f, 0x66, 0x6d, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x01, 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, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 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, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 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 input_data = { + 67.38262, 67.41811, 1.2834148, -98.304276, -79.165695, 15.270965, 3.746103, -54.763, + -42.926304, -23.553911, -19.94737, 47.355026, 31.100943, 8.447245, -75.31512, -16.644464, + 22.489397, -39.29673, -98.54282, -85.136086, 41.03638, -37.152763, -79.57822, -68.96887, + 11.1106205, -10.539243, -8.441398, -52.19395, 6.378709, 9.458231, -77.18643, 99.294, + 76.699104, 45.281494, -19.284706, 65.62093, -66.792015, -1.4548094, -82.337814, -73.516815, + 5.0163054, 80.22257, 15.349068, 19.546398, 24.824991, 16.236868, 92.39976, 76.70845, + -36.775307, -12.240916, 59.4311, 60.621857, 11.79261, -64.71309, -63.46639, -33.302174, + -36.784237, -40.939827, 15.621524, -90.70094, 29.704996, -71.49415, 14.5302105, -17.651545}; +const std::vector reference_output_data = { + 53.57955, 37.07942, 55.465694, 69.174965, 43.413803, 38.700638, 47.33504, 33.34464, 55.244015, + 69.998634, 55.095154, 32.02708, 58.0016, 65.26638, 23.898365, 53.472546, 70.27501, 54.07641, + 28.25977, 58.65199, 82.015915, 45.18763, 25.517868, 43.224625, 53.693985, 33.887444, 56.632225, + 83.667915, 59.987476, 47.6801, 36.385647, 49.41058, 36.54862, 62.415905, 81.55694, 44.618065, + 50.875214, 44.228725, 34.68579, 36.07896, 65.22483, 69.9266, 33.657883, 37.423244, 62.605423, + 56.839954, 50.79529, 58.17711, 37.61556}; + +} // namespace l2_pool_2d_float + +class TestDataFloatL2Pool2D : public TestDataL2Pool2DBase +{ +public: + TestDataFloatL2Pool2D() + { + _input_data = l2_pool_2d_float::input_data; + _reference_output_data = l2_pool_2d_float::reference_output_data; + _test_kernel_model_circle = l2_pool_2d_float::test_kernel_model_circle; + } + + ~TestDataFloatL2Pool2D() override = default; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_FLOAT_L2_POOL_2D_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/l2_poop_2d/NegL2Pool2DKernel.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/l2_poop_2d/NegL2Pool2DKernel.h new file mode 100644 index 00000000000..b8850b4cdc3 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/l2_poop_2d/NegL2Pool2DKernel.h @@ -0,0 +1,88 @@ +/* + * 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_NEG_L2_POOL_2D_KERNEL_H +#define LUCI_INTERPRETER_TEST_MODELS_NEG_L2_POOL_2D_KERNEL_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ +namespace neg_input_output_type_mismatch_l2_pool_2d_kernel +{ +/* + * L2Pool2D Kernel with input output type mismatch: + * + * Input(1, 8, 8, 1) - Float32 + * | + * L2Pool2D + * | + * Output(1, 7, 7, 1) - Int32 + */ +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, 0x58, 0x01, 0x00, 0x00, 0x74, 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, + 0x7c, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x84, 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, 0x05, 0x1c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x17, 0x00, 0x10, 0x00, 0x0c, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 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, + 0x50, 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, + 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x01, 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, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 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, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x00, 0x00, 0x00, + 0x4f, 0x4e, 0x45, 0x2d, 0x74, 0x66, 0x6c, 0x69, 0x74, 0x65, 0x32, 0x63, 0x69, 0x72, 0x63, 0x6c, + 0x65, 0x00, 0x00, 0x00}; +} // namespace neg_input_output_type_mismatch_l2_pool_2d_kernel + +class NegTestDataInputOutputTypeMismatchL2Pool2DKernel : public NegTestDataBase +{ +public: + NegTestDataInputOutputTypeMismatchL2Pool2DKernel() + { + _test_kernel_model_circle = + neg_input_output_type_mismatch_l2_pool_2d_kernel::test_kernel_model_circle; + } + + ~NegTestDataInputOutputTypeMismatchL2Pool2DKernel() override = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + +protected: + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_NEG_L2_POOL_2D_KERNEL_H diff --git a/onert-micro/luci-interpreter/include/luci_interpreter/test_models/l2_poop_2d/TestDataL2Pool2DBase.h b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/l2_poop_2d/TestDataL2Pool2DBase.h new file mode 100644 index 00000000000..ca191721de8 --- /dev/null +++ b/onert-micro/luci-interpreter/include/luci_interpreter/test_models/l2_poop_2d/TestDataL2Pool2DBase.h @@ -0,0 +1,60 @@ +/* + * 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_L2_POOL_2D_KERNEL_BASE_H +#define LUCI_INTERPRETER_TEST_MODELS_L2_POOL_2D_KERNEL_BASE_H + +#include "luci_interpreter/test_models/TestDataBase.h" + +namespace luci_interpreter +{ +namespace test_kernel +{ + +template class TestDataL2Pool2DBase : public TestDataBase +{ +public: + TestDataL2Pool2DBase() = default; + + const unsigned char *get_model_ptr() override final { return _test_kernel_model_circle; } + + const std::vector &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 &get_output_data_by_index(int i) override final + { + assert(i == 0); + return _reference_output_data; + } + +protected: + std::vector _input_data; + std::vector _reference_output_data; + const unsigned char *_test_kernel_model_circle; +}; + +} // namespace test_kernel +} // namespace luci_interpreter + +#endif // LUCI_INTERPRETER_TEST_MODELS_L2_POOL_2D_KERNEL_BASE_H diff --git a/onert-micro/luci-interpreter/pal/cmsisnn/KernelsToBuild.lst b/onert-micro/luci-interpreter/pal/cmsisnn/KernelsToBuild.lst index 8902ff7de3e..41659440974 100644 --- a/onert-micro/luci-interpreter/pal/cmsisnn/KernelsToBuild.lst +++ b/onert-micro/luci-interpreter/pal/cmsisnn/KernelsToBuild.lst @@ -35,6 +35,7 @@ REGISTER_KERNEL(REDUCE_MAX, ReduceMax) REGISTER_KERNEL(ROUND, Round) REGISTER_KERNEL(LESS, Less) REGISTER_KERNEL(L2_NORMALIZATION, L2Normalize) +REGISTER_KERNEL(L2_POOL_2D, L2Pool2D) REGISTER_KERNEL(LESS_EQUAL, LessEqual) REGISTER_KERNEL(LOGICAL_AND, LogicalAnd) REGISTER_KERNEL(LOGICAL_OR, LogicalOr) diff --git a/onert-micro/luci-interpreter/pal/common/PALL2Pool2D.h b/onert-micro/luci-interpreter/pal/common/PALL2Pool2D.h new file mode 100644 index 00000000000..9744dd02f1e --- /dev/null +++ b/onert-micro/luci-interpreter/pal/common/PALL2Pool2D.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright 2020 The TensorFlow Authors. 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_PAL_L2_POOL_2D_COMMON_H +#define LUCI_INTERPRETER_PAL_L2_POOL_2D_COMMON_H + +#include "Params.h" +#include "PALUtils.h" + +namespace luci_interpreter_pal +{ + +inline void L2Pool(const PoolParams ¶ms, const luci_interpreter::RuntimeShape &input_shape, + const float *input_data, const luci_interpreter::RuntimeShape &output_shape, + float *output_data) +{ + const int batches = MatchingDim(input_shape, 0, output_shape, 0); + const int depth = MatchingDim(input_shape, 3, output_shape, 3); + const int input_height = input_shape.dims(1); + const int input_width = input_shape.dims(2); + const int output_height = output_shape.dims(1); + const int output_width = output_shape.dims(2); + const int stride_height = params.stride_height; + const int stride_width = params.stride_width; + for (int batch = 0; batch < batches; ++batch) + { + for (int out_y = 0; out_y < output_height; ++out_y) + { + for (int out_x = 0; out_x < output_width; ++out_x) + { + for (int channel = 0; channel < depth; ++channel) + { + const int in_x_origin = (out_x * stride_width) - params.padding_values.width; + const int in_y_origin = (out_y * stride_height) - params.padding_values.height; + // Compute the boundaries of the filter region clamped so as to + // ensure that the filter window fits in the input array. + const int filter_x_start = std::max(0, -in_x_origin); + const int filter_x_end = std::min(params.filter_width, input_width - in_x_origin); + const int filter_y_start = std::max(0, -in_y_origin); + const int filter_y_end = std::min(params.filter_height, input_height - in_y_origin); + float sum_squares = 0.f; + int filter_count = 0; + for (int filter_y = filter_y_start; filter_y < filter_y_end; ++filter_y) + { + for (int filter_x = filter_x_start; filter_x < filter_x_end; ++filter_x) + { + const int in_x = in_x_origin + filter_x; + const int in_y = in_y_origin + filter_y; + const float val = + input_data[offset(input_shape.dimsData(), batch, in_y, in_x, channel)]; + sum_squares += val * val; + filter_count++; + } + } + const float l2pool_result = std::sqrt(sum_squares / filter_count); + output_data[offset(output_shape.dimsData(), batch, out_y, out_x, channel)] = + activationFunctionWithMinMax(l2pool_result, params.float_activation_min, + params.float_activation_max); + } + } + } + } +} + +} // namespace luci_interpreter_pal + +#endif // LUCI_INTERPRETER_PAL_L2_POOL_2D_COMMON_H diff --git a/onert-micro/luci-interpreter/pal/mcu/KernelsToBuild.lst b/onert-micro/luci-interpreter/pal/mcu/KernelsToBuild.lst index 773aa3260f4..21770f2fbc3 100644 --- a/onert-micro/luci-interpreter/pal/mcu/KernelsToBuild.lst +++ b/onert-micro/luci-interpreter/pal/mcu/KernelsToBuild.lst @@ -40,6 +40,7 @@ REGISTER_KERNEL(REDUCE_MAX, ReduceMax) REGISTER_KERNEL(ROUND, Round) REGISTER_KERNEL(LESS, Less) REGISTER_KERNEL(L2_NORMALIZATION, L2Normalize) +REGISTER_KERNEL(L2_POOL_2D, L2Pool2D) REGISTER_KERNEL(LESS_EQUAL, LessEqual) REGISTER_KERNEL(LOGICAL_AND, LogicalAnd) REGISTER_KERNEL(LOGICAL_OR, LogicalOr) diff --git a/onert-micro/luci-interpreter/src/kernels/L2Pool2D.cpp b/onert-micro/luci-interpreter/src/kernels/L2Pool2D.cpp index e465c220a12..dced2d647f8 100644 --- a/onert-micro/luci-interpreter/src/kernels/L2Pool2D.cpp +++ b/onert-micro/luci-interpreter/src/kernels/L2Pool2D.cpp @@ -15,73 +15,82 @@ * limitations under the License. */ -#include "kernels/L2Pool2D.h" - +#include "Builders.h" +#include "SISOKernel.h" #include "kernels/Utils.h" - #include "PALL2Pool2D.h" namespace luci_interpreter { - -namespace kernels +void configure_kernel_CircleL2Pool2D(const circle::Operator *cur_op, + BaseRuntimeGraph *runtime_graph) { + const kernels::SISOKernel siso_kernel(cur_op, runtime_graph); -L2Pool2D::L2Pool2D(const Tensor *input, Tensor *output, const Pool2DParams ¶ms) - : KernelWithParams({input}, {output}, params) -{ + LUCI_INTERPRETER_CHECK(Tensor::element_type(siso_kernel.input()) == + Tensor::element_type(siso_kernel.output())); + LUCI_INTERPRETER_CHECK(Tensor::num_dims(siso_kernel.input()) == 4); + LUCI_INTERPRETER_CHECK(Tensor::num_dims(siso_kernel.input()) == + Tensor::num_dims(siso_kernel.output())); } -void L2Pool2D::configure() +void execute_kernel_CircleL2Pool2D(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph) { - LUCI_INTERPRETER_CHECK(input()->shape().num_dims() == 4); - LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type()); - - int batches = input()->shape().dim(0); - int height = input()->shape().dim(1); - int width = input()->shape().dim(2); - int channels_out = input()->shape().dim(3); - - // Matching GetWindowedOutputSize in TensorFlow. - auto padding = params().padding; - int out_width, out_height; - out_width = computeOutputSize(padding, width, params().filter_width, params().stride_width, 1); - out_height = - computeOutputSize(padding, height, params().filter_height, params().stride_height, 1); - _padding_width = - computePadding(params().stride_width, 1, width, params().filter_width, out_width); - _padding_height = - computePadding(params().stride_height, 1, height, params().filter_height, out_height); - - LUCI_INTERPRETER_CHECK(input()->element_type() == DataType::FLOAT32); - // TODO: enable it only if kernel with dynamic shapes - output()->resize({batches, out_height, out_width, channels_out}); -} + const kernels::SISOKernel siso_kernel(cur_op, runtime_graph); -void L2Pool2D::execute() const -{ - switch (input()->element_type()) + const auto *options = cur_op->builtin_options_as_Pool2DOptions(); + + const auto input = siso_kernel.input(); + const auto output = siso_kernel.output(); + + const int32_t input_height = Tensor::dim(input, 1); + const int32_t input_width = Tensor::dim(input, 2); + + const int32_t output_height = kernels::computeOutputSize( + luci_padding(options->padding()), input_height, options->filter_height(), options->stride_h()); + const int32_t output_width = kernels::computeOutputSize( + luci_padding(options->padding()), input_width, options->filter_width(), options->stride_w()); + + const auto padding_height = kernels::computePadding(options->stride_h(), 1, input_height, + options->filter_height(), output_height); + const auto padding_width = kernels::computePadding(options->stride_w(), 1, input_width, + options->filter_width(), output_width); + + const auto *input_data = runtime_graph->getDataByTensor(input); + auto *output_data = runtime_graph->getDataByTensor(output); + + const DataType input_type = Tensor::element_type(input); + + float activation_min{}; + float activation_max{}; + +#ifndef DIS_FLOAT + kernels::calculateActivationRange(luci_actfunc(options->fused_activation_function()), + &activation_min, &activation_max); +#endif // DIS_FLOAT + + luci_interpreter_pal::PoolParams params{}; + params.padding_values.height = padding_height; + params.padding_values.width = padding_width; + params.stride_height = options->stride_h(); + params.stride_width = options->stride_w(); + params.filter_height = options->filter_height(); + params.filter_width = options->filter_width(); + params.float_activation_min = activation_min; + params.float_activation_max = activation_max; + + switch (input_type) { +#ifndef DIS_FLOAT case DataType::FLOAT32: - float activation_min, activation_max; - calculateActivationRange(params().activation, &activation_min, &activation_max); - tflite::PoolParams op_params; - op_params.stride_height = params().stride_height; - op_params.stride_width = params().stride_width; - op_params.filter_height = params().filter_height; - op_params.filter_width = params().filter_width; - op_params.padding_values.height = _padding_height; - op_params.padding_values.width = _padding_width; - op_params.float_activation_min = activation_min; - op_params.float_activation_max = activation_max; - luci_interpreter_pal::L2Pool(op_params, getTensorShape(input()), - getTensorData(input()), getTensorShape(output()), - getTensorData(output())); + luci_interpreter_pal::L2Pool( + params, kernels::getTensorShape(input), kernels::getTensorData(input_data), + kernels::getTensorShape(output), kernels::getTensorData(output_data)); break; +#endif // DIS_FLOAT default: assert(false && "Unsupported type."); } } -} // namespace kernels } // namespace luci_interpreter diff --git a/onert-micro/luci-interpreter/src/kernels/L2Pool2D.h b/onert-micro/luci-interpreter/src/kernels/L2Pool2D.h deleted file mode 100644 index d40f5f47827..00000000000 --- a/onert-micro/luci-interpreter/src/kernels/L2Pool2D.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020 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_KERNELS_L2POOL2D_H -#define LUCI_INTERPRETER_KERNELS_L2POOL2D_H - -#include "core/Kernel.h" -#include "core/KernelParams.h" - -#include - -namespace luci_interpreter -{ -namespace kernels -{ - -class L2Pool2D : public KernelWithParams -{ -public: - L2Pool2D(const Tensor *input, Tensor *output, const Pool2DParams ¶ms); - - const Tensor *input() const { return _inputs[0]; } - Tensor *output() const { return _outputs[0]; } - - void configure() override; - void execute() const override; - -private: - int32_t _padding_height = 0; - int32_t _padding_width = 0; -}; - -} // namespace kernels -} // namespace luci_interpreter - -#endif // LUCI_INTERPRETER_KERNELS_L2POOL2D_H diff --git a/onert-micro/luci-interpreter/src/kernels/L2Pool2D.test.cpp b/onert-micro/luci-interpreter/src/kernels/L2Pool2D.test.cpp index 7245456cb65..a0984fb4623 100644 --- a/onert-micro/luci-interpreter/src/kernels/L2Pool2D.test.cpp +++ b/onert-micro/luci-interpreter/src/kernels/L2Pool2D.test.cpp @@ -15,14 +15,14 @@ * limitations under the License. */ -#include "kernels/L2Pool2D.h" #include "kernels/TestUtils.h" -#include "luci_interpreter/TestMemoryManager.h" +#include "luci_interpreter/test_models/l2_poop_2d/FloatL2Pool2DKernel.h" +#include "luci_interpreter/test_models/l2_poop_2d/NegL2Pool2DKernel.h" + +#include "loader/ModuleLoader.h" namespace luci_interpreter { -namespace kernels -{ namespace { @@ -30,262 +30,59 @@ using namespace testing; class L2Pool2DTest : public ::testing::Test { -protected: - void SetUp() override { _memory_manager = std::make_unique(); } - - std::unique_ptr _memory_manager; + // Do nothing }; -TEST_F(L2Pool2DTest, FloatNone) -{ - Shape input_shape{1, 2, 4, 1}; - std::vector input_data{ - 0, 6, 2, 4, // - 3, 2, 10, 7, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Pool2DParams params{}; - params.padding = Padding::VALID; - params.activation = Activation::NONE; - params.filter_height = 2; - params.filter_width = 2; - params.stride_height = 2; - params.stride_width = 2; - - L2Pool2D kernel(&input_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{3.5, 6.5}; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); - // TODO make a Shape checking of output_tensor. -} - -TEST_F(L2Pool2DTest, FloatRelu) -{ - Shape input_shape{1, 2, 4, 1}; - std::vector input_data{ - -1, -6, 2, 4, // - -3, -2, 10, 7, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Pool2DParams params{}; - params.padding = Padding::VALID; - params.activation = Activation::RELU; - params.filter_height = 2; - params.filter_width = 2; - params.stride_height = 2; - params.stride_width = 2; - - L2Pool2D kernel(&input_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{3.53553, 6.5}; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); - // TODO make a Shape checking of output_tensor. -} - -TEST_F(L2Pool2DTest, FloatRelu1) -{ - Shape input_shape{1, 2, 4, 1}; - std::vector input_data{ - -0.1, -0.6, 2, 4, // - -0.3, -0.2, 10, 7, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Pool2DParams params{}; - params.padding = Padding::VALID; - params.activation = Activation::RELU_N1_TO_1; - params.filter_height = 2; - params.filter_width = 2; - params.stride_height = 2; - params.stride_width = 2; - - L2Pool2D kernel(&input_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{0.353553, 1.0}; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); - // TODO make a Shape checking of output_tensor. -} - -TEST_F(L2Pool2DTest, FloatRelu6) -{ - Shape input_shape{1, 2, 4, 1}; - std::vector input_data{ - -0.1, -0.6, 2, 4, // - -0.3, -0.2, 10, 7, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Pool2DParams params{}; - params.padding = Padding::VALID; - params.activation = Activation::RELU6; - params.filter_height = 2; - params.filter_width = 2; - params.stride_height = 2; - params.stride_width = 2; - - L2Pool2D kernel(&input_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{0.353553, 6.0}; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); - // TODO make a Shape checking of output_tensor. -} - -TEST_F(L2Pool2DTest, FloatPaddingSame) -{ - Shape input_shape{1, 2, 4, 1}; - std::vector input_data{ - 0, 6, 2, 4, // - 3, 2, 10, 7, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Pool2DParams params{}; - params.padding = Padding::SAME; - params.activation = Activation::NONE; - params.filter_height = 2; - params.filter_width = 2; - params.stride_height = 2; - params.stride_width = 2; - - L2Pool2D kernel(&input_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{3.5, 6.5}; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); - // TODO make a Shape checking of output_tensor. -} - -TEST_F(L2Pool2DTest, FloatPaddingSameStride) +template +std::vector checkL2Pool2DKernel(test_kernel::TestDataBase *test_data_base) { - Shape input_shape{1, 2, 4, 1}; - std::vector input_data{ - 0, 6, 2, 4, // - 3, 2, 10, 7, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; - Pool2DParams params{}; - params.padding = Padding::SAME; - params.activation = Activation::NONE; - params.filter_height = 2; - params.filter_width = 2; - params.stride_height = 1; - params.stride_width = 1; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_base->get_model_ptr()); + ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input); - L2Pool2D kernel(&input_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); - - std::vector ref_output_data{3.5, 6.0, 6.5, 5.70088, 2.54951, 7.2111, 8.63134, 7.0}; - // NOTE with NEON+ruy, error is #1=-1.14441e-05, #6=-1.81198e-05 - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data, 1.0e-4f)); - // TODO make a Shape checking of output_tensor. -} + auto *main_runtime_graph = runtime_module.getMainGraph(); + assert(main_runtime_graph->getNumOfInputTensors() == 1); -TEST_F(L2Pool2DTest, FloatPaddingValidStride) -{ - Shape input_shape{1, 2, 4, 1}; - std::vector input_data{ - 0, 6, 2, 4, // - 3, 2, 10, 7, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); + // Set input data + { + auto *input_tensor_data = reinterpret_cast(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); + } - Pool2DParams params{}; - params.padding = Padding::VALID; - params.activation = Activation::NONE; - params.filter_height = 2; - params.filter_width = 2; - params.stride_height = 1; - params.stride_width = 1; + runtime_module.execute(); - L2Pool2D kernel(&input_tensor, &output_tensor, params); - kernel.configure(); - _memory_manager->allocate_memory(output_tensor); - kernel.execute(); + assert(main_runtime_graph->getNumOfOutputTensors() == 1); - std::vector ref_output_data{3.5, 6.0, 6.5}; - EXPECT_THAT(extractTensorData(output_tensor), FloatArrayNear(ref_output_data)); - // TODO make a Shape checking of output_tensor. + T *output_data = reinterpret_cast(main_runtime_graph->getOutputDataByIndex(0)); + const size_t num_elements = (main_runtime_graph->getOutputDataSizeByIndex(0) / sizeof(T)); + std::vector output_data_vector(output_data, output_data + num_elements); + return output_data_vector; } -TEST_F(L2Pool2DTest, InvalidInputShape_NEG) +TEST_F(L2Pool2DTest, Float_P) { - Shape input_shape{1, 2, 4}; - std::vector input_data{ - 0, 6, 2, 4, // - 3, 2, 10, 7, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::FLOAT32); - - Pool2DParams params{}; - params.padding = Padding::VALID; - params.activation = Activation::NONE; - params.filter_height = 2; - params.filter_width = 2; - params.stride_height = 1; - params.stride_width = 1; - - L2Pool2D kernel(&input_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::TestDataFloatL2Pool2D test_data_kernel; + std::vector output_data_vector = checkL2Pool2DKernel(&test_data_kernel); + EXPECT_THAT(output_data_vector, kernels::testing::FloatArrayNear( + test_data_kernel.get_output_data_by_index(0), 0.0001f)); } -TEST_F(L2Pool2DTest, InvalidInputOutputType_NEG) +TEST_F(L2Pool2DTest, Input_output_type_mismatch_NEG) { - Shape input_shape{1, 2, 4}; - std::vector input_data{ - 0, 6, 2, 4, // - 3, 2, 10, 7, // - }; - Tensor input_tensor = - makeInputTensor(input_shape, input_data, _memory_manager.get()); - Tensor output_tensor = makeOutputTensor(DataType::U8); - - Pool2DParams params{}; - params.padding = Padding::VALID; - params.activation = Activation::NONE; - params.filter_height = 2; - params.filter_width = 2; - params.stride_height = 1; - params.stride_width = 1; - - L2Pool2D kernel(&input_tensor, &output_tensor, params); - EXPECT_ANY_THROW(kernel.configure()); + test_kernel::NegTestDataInputOutputTypeMismatchL2Pool2DKernel test_data_kernel; + MemoryManager memory_manager{}; + RuntimeModule runtime_module{}; + bool dealloc_input = true; + // Load model with single op + auto *model_data_raw = reinterpret_cast(test_data_kernel.get_model_ptr()); + EXPECT_DEATH(ModuleLoader::load(&runtime_module, &memory_manager, model_data_raw, dealloc_input), + ""); } } // namespace -} // namespace kernels } // namespace luci_interpreter