From bdcb4c148ee2fdeaaddf4cf1e57bbb0de02bb894 Mon Sep 17 00:00:00 2001 From: Adnan AlSinan Date: Mon, 18 Sep 2023 14:49:45 +0100 Subject: Implement tflite compliant reverse for CPU - Add support for negative axis values. - Add option to use opposite ACL convention for dimension addressing. - Add validation tests for the mentioned additions. Resolves COMPMID-6497 Change-Id: I9174b201c3adc070766cc6cffcbe4ec1fe5ec1c3 Signed-off-by: Adnan AlSinan Reviewed-on: https://review.mlplatform.org/c/ml/ComputeLibrary/+/10335 Comments-Addressed: Arm Jenkins Tested-by: Arm Jenkins Reviewed-by: SiCong Li Benchmark: Arm Jenkins --- arm_compute/runtime/NEON/functions/NEReverse.h | 34 ++++++---- docs/user_guide/operator_list.dox | 2 +- docs/user_guide/release_version_and_change_log.dox | 2 + src/core/NEON/kernels/NEReverseKernel.cpp | 58 ++++++++++++----- src/core/NEON/kernels/NEReverseKernel.h | 27 ++++---- src/runtime/NEON/functions/NEReverse.cpp | 10 +-- tests/validation/CL/Reverse.cpp | 47 ++++++++++---- tests/validation/NEON/Reverse.cpp | 50 +++++++++++---- tests/validation/fixtures/ReverseFixture.h | 74 +++++++++++++++++----- tests/validation/reference/DFT.cpp | 8 +-- tests/validation/reference/Reverse.cpp | 35 ++++++++-- tests/validation/reference/Reverse.h | 10 +-- 12 files changed, 255 insertions(+), 102 deletions(-) diff --git a/arm_compute/runtime/NEON/functions/NEReverse.h b/arm_compute/runtime/NEON/functions/NEReverse.h index c02fff54a5..f58eb2373f 100644 --- a/arm_compute/runtime/NEON/functions/NEReverse.h +++ b/arm_compute/runtime/NEON/functions/NEReverse.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2021 Arm Limited. + * Copyright (c) 2018-2021, 2023 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -21,8 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#ifndef ARM_COMPUTE_NEREVERSE_H -#define ARM_COMPUTE_NEREVERSE_H +#ifndef ACL_ARM_COMPUTE_RUNTIME_NEON_FUNCTIONS_NEREVERSE_H +#define ACL_ARM_COMPUTE_RUNTIME_NEON_FUNCTIONS_NEREVERSE_H #include "arm_compute/runtime/NEON/INESimpleFunctionNoBorder.h" @@ -45,22 +45,30 @@ public: * Valid data type configurations: * |src0 |src1 |dst | * |:--------------|:--------------|:--------------| - * |All |U32 |All | + * |All |U32, S32 |All | + * + * @param[in] input Input tensor. Data types supported: All + * @param[out] output Output tensor. Data type supported: Same as @p input + * @param[in] axis Axis tensor. Contains the indices of the dimensions to reverse. Data type supported: U32/S32 + * @param[in] use_inverted_axis Reverse ACL axis indices convention, if true, (inverted)axis = (tensor_rank - 1) - axis + * + * @note The value of each axis should be between [-rank, rank) + * @note If there are duplicate values in the tensor, the subsequent axis values are ignored. e.g. an array of [2, 2] has the same effects as [2]. + * + * @deprecated Support for U32 in axis tensor will be removed in 24.02 release * - * @param[in] input Input tensor. Data types supported: All - * @param[out] output Output tensor. Data type supported: Same as @p input - * @param[in] axis Axis tensor. Contains the indices of the dimensions to reverse. Data type supported: U32 */ - void configure(const ITensor *input, ITensor *output, const ITensor *axis); + void configure(const ITensor *input, ITensor *output, const ITensor *axis, const bool use_inverted_axis = false); /** Static function to check if given info will lead to a valid configuration of @ref NEReverseKernel * - * @param[in] input Input tensor info. Data types supported: All - * @param[in] output Output tensor info. Data type supported: Same as @p input - * @param[in] axis Axis tensor info. Contains the indices of the dimensions to reverse. Data type supported: U32 + * @param[in] input Input tensor info. Data types supported: All + * @param[in] output Output tensor info. Data type supported: Same as @p input + * @param[in] axis Axis tensor info. Contains the indices of the dimensions to reverse. Data type supported: U32/S32 + * @param[in] use_inverted_axis Reverse ACL axis indices convention, if true, (inverted)axis = (tensor_rank - 1) - axis * * @return a status */ - static Status validate(const ITensorInfo *input, const ITensorInfo *output, const ITensorInfo *axis); + static Status validate(const ITensorInfo *input, const ITensorInfo *output, const ITensorInfo *axis, const bool use_inverted_axis = false); }; } // namespace arm_compute -#endif /* ARM_COMPUTE_NEREVERSE_H */ +#endif // ACL_ARM_COMPUTE_RUNTIME_NEON_FUNCTIONS_NEREVERSE_H diff --git a/docs/user_guide/operator_list.dox b/docs/user_guide/operator_list.dox index e0b45419eb..498b925cd3 100644 --- a/docs/user_guide/operator_list.dox +++ b/docs/user_guide/operator_list.dox @@ -2655,7 +2655,7 @@ where N = batches, C = channels, H = height, W = width, D = depth
src0src1dst -
AllU32All +
AllU32, S32All
CLReverse diff --git a/docs/user_guide/release_version_and_change_log.dox b/docs/user_guide/release_version_and_change_log.dox index 5d8ca2beaa..04ecc80a19 100644 --- a/docs/user_guide/release_version_and_change_log.dox +++ b/docs/user_guide/release_version_and_change_log.dox @@ -57,6 +57,8 @@ v23.11 Public major release - Add new OpenCLâ„¢ kernels: - @ref opencl::kernels::ClMatMulLowpNativeMMULKernel support for QASYMM8 and QASYMM8_SIGNED, with batch support - Deprecate support for Bfloat16 in @ref cpu::CpuCast. + - Add support for negative axis values and inverted axis values in @ref arm_compute::NEReverse. + - Support for U32 axis in @ref arm_compute::NEReverse will be deprecated in 24.02. v23.08 Public major release - Deprecate the legacy 'libarm_compute_core' library. This library is an artifact of Compute Library's legacy library architecture and no longer serves any purpose. diff --git a/src/core/NEON/kernels/NEReverseKernel.cpp b/src/core/NEON/kernels/NEReverseKernel.cpp index 758433f89f..ca6c117882 100644 --- a/src/core/NEON/kernels/NEReverseKernel.cpp +++ b/src/core/NEON/kernels/NEReverseKernel.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2021 Arm Limited. + * Copyright (c) 2018-2021, 2023 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -34,13 +34,15 @@ namespace arm_compute { namespace { -Status validate_arguments(const ITensorInfo *input, const ITensorInfo *output, const ITensorInfo *axis) +Status validate_arguments(const ITensorInfo *input, const ITensorInfo *output, const ITensorInfo *axis, bool use_inverted_axis) { + ARM_COMPUTE_UNUSED(use_inverted_axis); ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, output, axis); //Note: ARM_COMPUTE_RETURN_ERROR_ON_CPU_F16_UNSUPPORTED(input) is not needed here as this kernel doesn't use CPU FP16 instructions. ARM_COMPUTE_RETURN_ERROR_ON(input->data_type() == DataType::UNKNOWN); - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(axis, 1, DataType::U32); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(axis, 1, DataType::U32, DataType::S32); ARM_COMPUTE_RETURN_ERROR_ON_MSG(axis->num_dimensions() > 1, "Axis must be a 1D tensor"); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(input->num_dimensions() > 4, "Current implementation only supports up to 4 dimensions."); ARM_COMPUTE_RETURN_ERROR_ON_MSG(axis->dimension(0) > 4, "Only up to 4 dimensions can be reversed"); // Checks performed when output is configured @@ -56,41 +58,63 @@ Status validate_arguments(const ITensorInfo *input, const ITensorInfo *output, c } // namespace NEReverseKernel::NEReverseKernel() - : _input(nullptr), _output(nullptr), _axis(nullptr) + : _input(nullptr), _output(nullptr), _axis(nullptr), _use_inverted_axis(false) { } -void NEReverseKernel::configure(const ITensor *input, ITensor *output, const ITensor *axis) +void NEReverseKernel::configure(const ITensor *input, ITensor *output, const ITensor *axis, bool use_inverted_axis) { ARM_COMPUTE_ERROR_ON_NULLPTR(input, output, axis); - _input = input; - _output = output; - _axis = axis; + _input = input; + _output = output; + _axis = axis; + _use_inverted_axis = use_inverted_axis; // Output tensor auto initialization if not yet initialized auto_init_if_empty(*output->info(), *input->info()->clone()); - ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(input->info(), output->info(), axis->info())); + ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(input->info(), output->info(), axis->info(), use_inverted_axis)); // Configure kernel window INEKernel::configure(calculate_max_window(*output->info())); } -Status NEReverseKernel::validate(const ITensorInfo *input, const ITensorInfo *output, const ITensorInfo *axis) +Status NEReverseKernel::validate(const ITensorInfo *input, const ITensorInfo *output, const ITensorInfo *axis, bool use_inverted_axis) { - ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(input, output, axis)); + ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(input, output, axis, use_inverted_axis)); return Status{}; } template -void run_reverse(const Window &window, const ITensor *input, const ITensor *axis, ITensor *output) +void run_reverse(const Window &window, const ITensor *input, const ITensor *axis, ITensor *output, bool use_inverted_axis) { - int axis_bit = 0; + unsigned int axis_bit = 0; + const int rank = input->info()->num_dimensions(); + for(unsigned int i = 0; i < axis->info()->dimension(0); ++i) { - const int axis_i = *(reinterpret_cast(axis->buffer()) + i); + int axis_i = *(reinterpret_cast(axis->buffer()) + i); + + // The values of axis tensor must be between [-rank, rank-1]. + if((axis_i < -rank) || (axis_i >= rank)) + { + ARM_COMPUTE_ERROR("the valuses of the axis tensor must be within [-rank, rank-1]."); + } + + // In case of negative axis value i.e targeted axis(i) = rank + axis(i) + if(axis_i < 0) + { + axis_i = rank + axis_i; + } + + // Reverse ACL axis indices convention i.e. (inverted)axis = (tensor_rank - 1) - axis + if(use_inverted_axis) + { + axis_i = (rank - 1) - axis_i; + } + axis_bit |= 1 << axis_i; } @@ -151,13 +175,13 @@ void NEReverseKernel::run(const Window &window, const ThreadInfo &info) switch(_input->info()->element_size()) { case 4: - run_reverse(window, _input, _axis, _output); + run_reverse(window, _input, _axis, _output, _use_inverted_axis); break; case 2: - run_reverse(window, _input, _axis, _output); + run_reverse(window, _input, _axis, _output, _use_inverted_axis); break; case 1: - run_reverse(window, _input, _axis, _output); + run_reverse(window, _input, _axis, _output, _use_inverted_axis); break; default: ARM_COMPUTE_ERROR("Element size not supported"); diff --git a/src/core/NEON/kernels/NEReverseKernel.h b/src/core/NEON/kernels/NEReverseKernel.h index 07b547a327..7d9ec4691c 100644 --- a/src/core/NEON/kernels/NEReverseKernel.h +++ b/src/core/NEON/kernels/NEReverseKernel.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020 Arm Limited. + * Copyright (c) 2018-2020, 2023 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -21,8 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#ifndef ARM_COMPUTE_NEREVERSEKERNEL_H -#define ARM_COMPUTE_NEREVERSEKERNEL_H +#ifndef ACL_SRC_CORE_NEON_KERNELS_NEREVERSEKERNEL_H +#define ACL_SRC_CORE_NEON_KERNELS_NEREVERSEKERNEL_H #include "src/core/NEON/INEKernel.h" @@ -52,21 +52,23 @@ public: ~NEReverseKernel() = default; /** Initialise the kernel's inputs and output * - * @param[in] input Input tensor. Data types supported: All - * @param[out] output Output tensor. Data type supported: Same as @p input - * @param[in] axis Axis tensor. Contains the indices of the dimensions to reverse. Data type supported: U32 + * @param[in] input Input tensor. Data types supported: All + * @param[out] output Output tensor. Data type supported: Same as @p input + * @param[in] axis Axis tensor. Contains the indices of the dimensions to reverse. Data type supported: U32/S32 + * @param[in] use_inverted_axis Reverse ACL axis indices convention i.e. acl.dim(0) = tensor_rank -1 */ - void configure(const ITensor *input, ITensor *output, const ITensor *axis); + void configure(const ITensor *input, ITensor *output, const ITensor *axis, bool use_inverted_axis); /** Static function to check if given info will lead to a valid configuration of @ref NEReverseKernel * - * @param[in] input Input tensor info. Data types supported: All - * @param[in] output Output tensor info. Data type supported: Same as @p input - * @param[in] axis Axis tensor info. Contains the indices of the dimensions to reverse. Data type supported: U32 + * @param[in] input Input tensor info. Data types supported: All + * @param[in] output Output tensor info. Data type supported: Same as @p input + * @param[in] axis Axis tensor info. Contains the indices of the dimensions to reverse. Data type supported: U32/S32 + * @param[in] use_inverted_axis Reverse ACL axis indices convention i.e. acl.dim(0) = tensor_rank -1 * * @return a status */ - static Status validate(const ITensorInfo *input, const ITensorInfo *output, const ITensorInfo *axis); + static Status validate(const ITensorInfo *input, const ITensorInfo *output, const ITensorInfo *axis, bool use_inverted_axis); // Inherited methods overridden: void run(const Window &window, const ThreadInfo &info) override; @@ -75,6 +77,7 @@ private: const ITensor *_input; ITensor *_output; const ITensor *_axis; + bool _use_inverted_axis; }; } // namespace arm_compute -#endif /*ARM_COMPUTE_NEREVERSEKERNEL_H */ +#endif // ACL_SRC_CORE_NEON_KERNELS_NEREVERSEKERNEL_H diff --git a/src/runtime/NEON/functions/NEReverse.cpp b/src/runtime/NEON/functions/NEReverse.cpp index d4ed2a9018..e1988f2ab3 100644 --- a/src/runtime/NEON/functions/NEReverse.cpp +++ b/src/runtime/NEON/functions/NEReverse.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2021 Arm Limited. + * Copyright (c) 2018-2021, 2023 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -29,17 +29,17 @@ namespace arm_compute { -void NEReverse::configure(const ITensor *input, ITensor *output, const ITensor *axis) +void NEReverse::configure(const ITensor *input, ITensor *output, const ITensor *axis, bool use_inverted_axis) { ARM_COMPUTE_LOG_PARAMS(input, output, axis); auto k = std::make_unique(); - k->configure(input, output, axis); + k->configure(input, output, axis, use_inverted_axis); _kernel = std::move(k); } -Status NEReverse::validate(const ITensorInfo *input, const ITensorInfo *output, const ITensorInfo *axis) +Status NEReverse::validate(const ITensorInfo *input, const ITensorInfo *output, const ITensorInfo *axis, bool use_inverted_axis) { - return NEReverseKernel::validate(input, output, axis); + return NEReverseKernel::validate(input, output, axis, use_inverted_axis); } } // namespace arm_compute diff --git a/tests/validation/CL/Reverse.cpp b/tests/validation/CL/Reverse.cpp index 11df0e7803..ff46ba64ad 100644 --- a/tests/validation/CL/Reverse.cpp +++ b/tests/validation/CL/Reverse.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020 Arm Limited. + * Copyright (c) 2018-2020, 2023 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -41,6 +41,7 @@ namespace test { namespace validation { +using framework::dataset::make; namespace { auto run_small_dataset = combine(datasets::SmallShapes(), datasets::Tiny1DShapes()); @@ -53,28 +54,28 @@ TEST_SUITE(Reverse) // *INDENT-OFF* // clang-format off DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip( - framework::dataset::make("InputInfo", { TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S8), // Invalid axis datatype + make("InputInfo", { TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S8), // Invalid axis datatype TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), // Invalid axis shape TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), // Invalid axis length (> 4) TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), // Mismatching shapes TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), TensorInfo(TensorShape(2U), 1, DataType::U8), }), - framework::dataset::make("OutputInfo", { TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S8), + make("OutputInfo", { TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S8), TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), TensorInfo(TensorShape(2U, 13U, 2U), 1, DataType::U8), TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), TensorInfo(TensorShape(2U), 1, DataType::U8), })), - framework::dataset::make("AxisInfo",{ TensorInfo(TensorShape(3U), 1, DataType::U8), + make("AxisInfo",{ TensorInfo(TensorShape(3U), 1, DataType::U8), TensorInfo(TensorShape(2U, 10U), 1, DataType::U32), TensorInfo(TensorShape(8U), 1, DataType::U32), TensorInfo(TensorShape(2U), 1, DataType::U32), TensorInfo(TensorShape(2U), 1, DataType::U32), TensorInfo(TensorShape(2U), 1, DataType::U32), })), - framework::dataset::make("Expected", { false, false, false, false, true, true})), + make("Expected", { false, false, false, false, true, true})), src_info, dst_info, axis_info, expected) { Status s = CLReverse::validate(&src_info.clone()->set_is_resizable(false), @@ -93,7 +94,11 @@ TEST_SUITE(F16) FIXTURE_DATA_TEST_CASE(RunSmall, CLReverseFixture, framework::DatasetMode::PRECOMMIT, - combine(run_small_dataset, framework::dataset::make("DataType", DataType::F16))) + combine( + run_small_dataset, + make("DataType", DataType::F16), + make("use_negative_axis", { false }), + make("use_inverted_axis", { false }))) { // Validate output validate(CLAccessor(_target), _reference); @@ -102,7 +107,11 @@ FIXTURE_DATA_TEST_CASE(RunSmall, FIXTURE_DATA_TEST_CASE(RunLarge, CLReverseFixture, framework::DatasetMode::NIGHTLY, - combine(run_large_dataset, framework::dataset::make("DataType", DataType::F16))) + combine( + run_large_dataset, + make("DataType", DataType::F16), + make("use_negative_axis", { false }), + make("use_inverted_axis", { false }))) { // Validate output validate(CLAccessor(_target), _reference); @@ -113,7 +122,11 @@ TEST_SUITE(FP32) FIXTURE_DATA_TEST_CASE(RunSmall, CLReverseFixture, framework::DatasetMode::PRECOMMIT, - combine(run_small_dataset, framework::dataset::make("DataType", DataType::F32))) + combine( + run_small_dataset, + make("DataType", DataType::F32), + make("use_negative_axis", { false }), + make("use_inverted_axis", { false }))) { // Validate output validate(CLAccessor(_target), _reference); @@ -122,7 +135,11 @@ FIXTURE_DATA_TEST_CASE(RunSmall, FIXTURE_DATA_TEST_CASE(RunLarge, CLReverseFixture, framework::DatasetMode::NIGHTLY, - combine(run_large_dataset, framework::dataset::make("DataType", DataType::F32))) + combine( + run_large_dataset, + make("DataType", DataType::F32), + make("use_negative_axis", { false }), + make("use_inverted_axis", { false }))) { // Validate output validate(CLAccessor(_target), _reference); @@ -135,7 +152,11 @@ TEST_SUITE(QASYMM8) FIXTURE_DATA_TEST_CASE(RunSmall, CLReverseFixture, framework::DatasetMode::PRECOMMIT, - combine(run_small_dataset, framework::dataset::make("DataType", DataType::QASYMM8))) + combine( + run_small_dataset, + make("DataType", DataType::QASYMM8), + make("use_negative_axis", { false }), + make("use_inverted_axis", { false }))) { // Validate output validate(CLAccessor(_target), _reference); @@ -144,7 +165,11 @@ FIXTURE_DATA_TEST_CASE(RunSmall, FIXTURE_DATA_TEST_CASE(RunLarge, CLReverseFixture, framework::DatasetMode::NIGHTLY, - combine(run_large_dataset, framework::dataset::make("DataType", DataType::QASYMM8))) + combine( + run_large_dataset, + make("DataType", DataType::QASYMM8), + make("use_negative_axis", { false }), + make("use_inverted_axis", { false }))) { // Validate output validate(CLAccessor(_target), _reference); diff --git a/tests/validation/NEON/Reverse.cpp b/tests/validation/NEON/Reverse.cpp index 3dc3eeee80..eb0c995ad9 100644 --- a/tests/validation/NEON/Reverse.cpp +++ b/tests/validation/NEON/Reverse.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2021 Arm Limited. + * Copyright (c) 2018-2021, 2023 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -43,6 +43,7 @@ namespace validation { namespace { +using framework::dataset::make; auto run_small_dataset = combine(datasets::SmallShapes(), datasets::Tiny1DShapes()); auto run_large_dataset = combine(datasets::LargeShapes(), datasets::Tiny1DShapes()); @@ -53,28 +54,31 @@ TEST_SUITE(Reverse) // *INDENT-OFF* // clang-format off DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip( - framework::dataset::make("InputInfo", { TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S8), // Invalid axis datatype + make("InputInfo", { TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S8), // Invalid axis datatype TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), // Invalid axis shape TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), // Invalid axis length (> 4) TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), // Mismatching shapes + TensorInfo(TensorShape(32U, 13U, 17U, 3U, 2U), 1, DataType::U8), // Unsupported source dimensions (>4) TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), TensorInfo(TensorShape(2U), 1, DataType::U8), }), - framework::dataset::make("OutputInfo", { TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S8), + make("OutputInfo", { TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S8), TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), TensorInfo(TensorShape(2U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 17U, 3U, 2U), 1, DataType::U8), TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), TensorInfo(TensorShape(2U), 1, DataType::U8), })), - framework::dataset::make("AxisInfo", { TensorInfo(TensorShape(3U), 1, DataType::U8), + make("AxisInfo", { TensorInfo(TensorShape(3U), 1, DataType::U8), TensorInfo(TensorShape(2U, 10U), 1, DataType::U32), TensorInfo(TensorShape(8U), 1, DataType::U32), TensorInfo(TensorShape(2U), 1, DataType::U32), TensorInfo(TensorShape(2U), 1, DataType::U32), TensorInfo(TensorShape(2U), 1, DataType::U32), + TensorInfo(TensorShape(2U), 1, DataType::U32), })), - framework::dataset::make("Expected", { false, false, false, false, true, true})), + make("Expected", { false, false, false, false, false, true, true})), src_info, dst_info, axis_info, expected) { Status s = NEReverse::validate(&src_info.clone()->set_is_resizable(false), @@ -95,7 +99,11 @@ TEST_SUITE(F16) FIXTURE_DATA_TEST_CASE(RunSmall, NEReverseFixture, framework::DatasetMode::PRECOMMIT, - combine(run_small_dataset, framework::dataset::make("DataType", DataType::F16))) + combine( + run_small_dataset, + make("DataType", DataType::F16), + make("use_negative_axis", { true, false }), + make("use_inverted_axis", { true, false }))) { // Validate output validate(Accessor(_target), _reference); @@ -104,7 +112,11 @@ FIXTURE_DATA_TEST_CASE(RunSmall, FIXTURE_DATA_TEST_CASE(RunLarge, NEReverseFixture, framework::DatasetMode::NIGHTLY, - combine(run_large_dataset, framework::dataset::make("DataType", DataType::F16))) + combine( + run_large_dataset, + make("DataType", DataType::F16), + make("use_negative_axis", { true, false }), + make("use_inverted_axis", { true, false }))) { // Validate output validate(Accessor(_target), _reference); @@ -116,7 +128,11 @@ TEST_SUITE(FP32) FIXTURE_DATA_TEST_CASE(RunSmall, NEReverseFixture, framework::DatasetMode::PRECOMMIT, - combine(run_small_dataset, framework::dataset::make("DataType", DataType::F32))) + combine( + run_small_dataset, + make("DataType", DataType::F32), + make("use_negative_axis", { true, false }), + make("use_inverted_axis", { true, false }))) { // Validate output validate(Accessor(_target), _reference); @@ -125,7 +141,11 @@ FIXTURE_DATA_TEST_CASE(RunSmall, FIXTURE_DATA_TEST_CASE(RunLarge, NEReverseFixture, framework::DatasetMode::NIGHTLY, - combine(run_large_dataset, framework::dataset::make("DataType", DataType::F32))) + combine( + run_large_dataset, + make("DataType", DataType::F32), + make("use_negative_axis", { true, false }), + make("use_inverted_axis", { true, false }))) { // Validate output validate(Accessor(_target), _reference); @@ -138,7 +158,11 @@ TEST_SUITE(QASYMM8) FIXTURE_DATA_TEST_CASE(RunSmall, NEReverseFixture, framework::DatasetMode::PRECOMMIT, - combine(run_small_dataset, framework::dataset::make("DataType", DataType::QASYMM8))) + combine( + run_small_dataset, + make("DataType", DataType::QASYMM8), + make("use_negative_axis", { true, false }), + make("use_inverted_axis", { true, false }))) { // Validate output validate(Accessor(_target), _reference); @@ -147,7 +171,11 @@ FIXTURE_DATA_TEST_CASE(RunSmall, FIXTURE_DATA_TEST_CASE(RunLarge, NEReverseFixture, framework::DatasetMode::NIGHTLY, - combine(run_large_dataset, framework::dataset::make("DataType", DataType::QASYMM8))) + combine( + run_large_dataset, + make("DataType", DataType::QASYMM8), + make("use_negative_axis", { true, false }), + make("use_inverted_axis", { true, false }))) { // Validate output validate(Accessor(_target), _reference); diff --git a/tests/validation/fixtures/ReverseFixture.h b/tests/validation/fixtures/ReverseFixture.h index 509fd93abf..8ff8cf9421 100644 --- a/tests/validation/fixtures/ReverseFixture.h +++ b/tests/validation/fixtures/ReverseFixture.h @@ -21,12 +21,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#ifndef ARM_COMPUTE_TEST_REVERSE_FIXTURE -#define ARM_COMPUTE_TEST_REVERSE_FIXTURE +#ifndef ACL_TESTS_VALIDATION_FIXTURES_REVERSEFIXTURE_H +#define ACL_TESTS_VALIDATION_FIXTURES_REVERSEFIXTURE_H #include "arm_compute/core/Helpers.h" #include "arm_compute/core/TensorShape.h" #include "arm_compute/core/Types.h" +#ifdef ARM_COMPUTE_OPENCL_ENABLED +#include "arm_compute/runtime/CL/functions/CLReverse.h" +#endif // ARM_COMPUTE_OPENCL_ENABLED #include "arm_compute/runtime/Tensor.h" #include "tests/AssetsLibrary.h" #include "tests/Globals.h" @@ -41,14 +44,40 @@ namespace test { namespace validation { +namespace +{ +template +#ifdef ARM_COMPUTE_OPENCL_ENABLED +std::enable_if_t < !std::is_same::value, void > +#else // ARM_COMPUTE_OPENCL_ENABLED +void +#endif // ARM_COMPUTE_OPENCL_ENABLED +configureReverse(ReverseFunction &func, TensorType &src, TensorType &axis, TensorType &dst, bool use_inverted_axis) +{ + func.configure(&src, &dst, &axis, use_inverted_axis); +} + +#ifdef ARM_COMPUTE_OPENCL_ENABLED +template +std::enable_if_t::value, void> +configureReverse(ReverseFunction &func, TensorType &src, TensorType &axis, TensorType &dst, bool use_inverted_axis) +{ + ARM_COMPUTE_UNUSED(use_inverted_axis); + func.configure(&src, &dst, &axis); +} + +#endif // ARM_COMPUTE_OPENCL_ENABLED +} //namespace + template class ReverseValidationFixture : public framework::Fixture { public: - void setup(TensorShape shape, TensorShape axis_shape, DataType data_type) + void setup(TensorShape shape, TensorShape axis_shape, DataType data_type, bool use_negative_axis = false, bool use_inverted_axis = false) { - _target = compute_target(shape, axis_shape, data_type); - _reference = compute_reference(shape, axis_shape, data_type); + _num_dims = shape.num_dimensions(); + _target = compute_target(shape, axis_shape, data_type, use_negative_axis, use_inverted_axis); + _reference = compute_reference(shape, axis_shape, data_type, use_negative_axis, use_inverted_axis); } protected: @@ -57,16 +86,25 @@ protected: { library->fill_tensor_uniform(tensor, 0); } - std::vector generate_random_axis() + std::vector generate_random_axis(bool use_negative = false) { - std::vector axis_v = { 0, 1, 2, 3 }; - std::mt19937 g(0); + std::vector axis_v; + if(use_negative) + { + axis_v = { -1, -2, -3, -4 }; + } + else + { + axis_v = { 0, 1, 2, 3 }; + } + axis_v = std::vector(axis_v.begin(), axis_v.begin() + _num_dims); + std::mt19937 g(library->seed()); std::shuffle(axis_v.begin(), axis_v.end(), g); return axis_v; } - TensorType compute_target(const TensorShape &shape, const TensorShape &axis_shape, DataType data_type) + TensorType compute_target(const TensorShape &shape, const TensorShape &axis_shape, DataType data_type, bool use_negative_axis, bool use_inverted_axis = false) { // Create tensors TensorType src = create_tensor(shape, data_type, 1); @@ -75,7 +113,8 @@ protected: // Create and configure function FunctionType reverse_func; - reverse_func.configure(&src, &dst, &axis); + + configureReverse(reverse_func, src, axis, dst, use_inverted_axis); ARM_COMPUTE_ASSERT(src.info()->is_resizable()); ARM_COMPUTE_ASSERT(axis.info()->is_resizable()); @@ -94,7 +133,7 @@ protected: fill(AccessorType(src)); { auto axis_data = AccessorType(axis); - auto axis_v = generate_random_axis(); + auto axis_v = generate_random_axis(use_negative_axis); std::copy(axis_v.begin(), axis_v.begin() + axis_shape.x(), static_cast(axis_data.data())); } @@ -104,24 +143,25 @@ protected: return dst; } - SimpleTensor compute_reference(const TensorShape &shape, const TensorShape &axis_shape, DataType data_type) + SimpleTensor compute_reference(const TensorShape &shape, const TensorShape &axis_shape, DataType data_type, bool use_negative_axis, bool use_inverted_axis = false) { // Create reference - SimpleTensor src{ shape, data_type }; - SimpleTensor axis{ axis_shape, DataType::U32 }; + SimpleTensor src{ shape, data_type }; + SimpleTensor axis{ axis_shape, DataType::S32 }; // Fill reference fill(src); - auto axis_v = generate_random_axis(); + auto axis_v = generate_random_axis(use_negative_axis); std::copy(axis_v.begin(), axis_v.begin() + axis_shape.x(), axis.data()); - return reference::reverse(src, axis); + return reference::reverse(src, axis, use_inverted_axis); } TensorType _target{}; SimpleTensor _reference{}; + unsigned int _num_dims{}; }; } // namespace validation } // namespace test } // namespace arm_compute -#endif /* ARM_COMPUTE_TEST_REVERSE_FIXTURE */ +#endif // ACL_TESTS_VALIDATION_FIXTURES_REVERSEFIXTURE_H diff --git a/tests/validation/reference/DFT.cpp b/tests/validation/reference/DFT.cpp index fd126c7d73..2b03c270ac 100644 --- a/tests/validation/reference/DFT.cpp +++ b/tests/validation/reference/DFT.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 Arm Limited. + * Copyright (c) 2019-2020, 2023 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -400,10 +400,10 @@ SimpleTensor conv2d_dft(const SimpleTensor &src, const SimpleTensor &w, auto padded_src = pad_layer(src, padding_in); // Flip weights - std::vector axis_v = { 0, 1 }; - SimpleTensor axis{ TensorShape(2U), DataType::U32 }; + std::vector axis_v = { 0, 1 }; + SimpleTensor axis{ TensorShape(2U), DataType::S32 }; std::copy(axis_v.begin(), axis_v.begin() + axis.shape().x(), axis.data()); - auto flipped_w = reverse(w, axis); + auto flipped_w = reverse(w, axis, /* use_inverted_axis */ false); // Pad weights to have the same size as input const PaddingList paddings_w = { { 0, src.shape()[0] - 1 }, { 0, src.shape()[1] - 1 } }; diff --git a/tests/validation/reference/Reverse.cpp b/tests/validation/reference/Reverse.cpp index c6c4614278..5fd15b5bfc 100644 --- a/tests/validation/reference/Reverse.cpp +++ b/tests/validation/reference/Reverse.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020 Arm Limited. + * Copyright (c) 2018-2020, 2023 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -35,8 +35,9 @@ namespace validation namespace reference { template -SimpleTensor reverse(const SimpleTensor &src, const SimpleTensor &axis) +SimpleTensor reverse(const SimpleTensor &src, const SimpleTensor &axis, bool use_inverted_axis) { + ARM_COMPUTE_ERROR_ON(src.shape().num_dimensions() > 4); ARM_COMPUTE_ERROR_ON(axis.shape().num_dimensions() > 1); ARM_COMPUTE_ERROR_ON(axis.shape().x() > 4); @@ -48,10 +49,32 @@ SimpleTensor reverse(const SimpleTensor &src, const SimpleTensor const unsigned int depth = src.shape()[2]; const unsigned int batches = src.shape()[3]; + const int rank = src.shape().num_dimensions(); + std::array to_reverse = { { false, false, false, false } }; for(int i = 0; i < axis.num_elements(); ++i) { - to_reverse[axis[i]] = true; + int axis_i = axis[i]; + + // The values of axis tensor must be between [-rank, rank-1]. + if((axis_i < -rank) || (axis_i >= rank)) + { + ARM_COMPUTE_ERROR("the valuses of the axis tensor must be within [-rank, rank-1]."); + } + + // In case of negative axis value i.e targeted axis(i) = rank + axis(i) + if(axis_i < 0) + { + axis_i = rank + axis_i; + } + + // Reverse ACL axis indices convention i.e. (inverted)axis = (tensor_rank - 1) - axis + if(use_inverted_axis) + { + axis_i = (rank - 1) - axis_i; + } + + to_reverse[axis_i] = true; } const uint32_t num_elements = src.num_elements(); @@ -73,9 +96,9 @@ SimpleTensor reverse(const SimpleTensor &src, const SimpleTensor return dst; } -template SimpleTensor reverse(const SimpleTensor &src, const SimpleTensor &axis); -template SimpleTensor reverse(const SimpleTensor &src, const SimpleTensor &axis); -template SimpleTensor reverse(const SimpleTensor &src, const SimpleTensor &axis); +template SimpleTensor reverse(const SimpleTensor &src, const SimpleTensor &axis, bool use_inverted_axis); +template SimpleTensor reverse(const SimpleTensor &src, const SimpleTensor &axis, bool use_inverted_axis); +template SimpleTensor reverse(const SimpleTensor &src, const SimpleTensor &axis, bool use_inverted_axis); } // namespace reference } // namespace validation } // namespace test diff --git a/tests/validation/reference/Reverse.h b/tests/validation/reference/Reverse.h index 4a28da7270..30926b05a5 100644 --- a/tests/validation/reference/Reverse.h +++ b/tests/validation/reference/Reverse.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019 Arm Limited. + * Copyright (c) 2018-2019, 2023 Arm Limited. * * SPDX-License-Identifier: MIT * @@ -21,8 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#ifndef ARM_COMPUTE_TEST_REVERSE_H -#define ARM_COMPUTE_TEST_REVERSE_H +#ifndef ACL_TESTS_VALIDATION_REFERENCE_REVERSE_H +#define ACL_TESTS_VALIDATION_REFERENCE_REVERSE_H #include "tests/SimpleTensor.h" @@ -35,9 +35,9 @@ namespace validation namespace reference { template -SimpleTensor reverse(const SimpleTensor &src, const SimpleTensor &axis); +SimpleTensor reverse(const SimpleTensor &src, const SimpleTensor &axis, bool use_inverted_axis = false); } // namespace reference } // namespace validation } // namespace test } // namespace arm_compute -#endif /* ARM_COMPUTE_TEST_REVERSE_H */ +#endif // ACL_TESTS_VALIDATION_REFERENCE_REVERSE_H -- cgit v1.2.1