From 164a2727d3bbce0e575d24b7db787c85e2e2c203 Mon Sep 17 00:00:00 2001 From: giuros01 Date: Tue, 20 Nov 2018 18:34:46 +0000 Subject: COMPMID-1717: CL: Implement Maximum, Minimum, SquaredDifference Change-Id: Ice653e48211053bd3cd20a693bd76de6b4efc370 Reviewed-on: https://review.mlplatform.org/270 Reviewed-by: Georgios Pinitas Tested-by: Arm Jenkins --- arm_compute/core/CL/CLKernels.h | 4 +- .../core/CL/kernels/CLArithmeticAdditionKernel.h | 83 ----- .../core/CL/kernels/CLArithmeticDivisionKernel.h | 81 ----- .../CL/kernels/CLArithmeticSubtractionKernel.h | 85 ------ .../core/CL/kernels/CLElementwiseOperationKernel.h | 194 ++++++++++++ arm_compute/core/Types.h | 11 + arm_compute/runtime/CL/CLFunctions.h | 4 +- .../runtime/CL/functions/CLArithmeticAddition.h | 64 ---- .../runtime/CL/functions/CLArithmeticDivision.h | 62 ---- .../runtime/CL/functions/CLArithmeticSubtraction.h | 67 ---- .../runtime/CL/functions/CLElementwiseOperations.h | 206 +++++++++++++ .../runtime/CL/functions/CLGEMMConvolutionLayer.h | 20 +- arm_compute/runtime/CL/functions/CLLSTMLayer.h | 145 +++++---- .../runtime/CL/functions/CLLaplacianPyramid.h | 4 +- .../runtime/CL/functions/CLLaplacianReconstruct.h | 2 +- arm_compute/runtime/CL/functions/CLRNNLayer.h | 22 +- arm_compute/runtime/CL/functions/CLReduceMean.h | 2 +- src/core/CL/CLKernelLibrary.cpp | 33 +- src/core/CL/cl_kernels/arithmetic_op.cl | 190 ------------ src/core/CL/cl_kernels/arithmetic_op_quantized.cl | 168 ---------- src/core/CL/cl_kernels/elementwise_operation.cl | 98 ++++++ .../cl_kernels/elementwise_operation_quantized.cl | 107 +++++++ src/core/CL/kernels/CLArithmeticAdditionKernel.cpp | 233 -------------- src/core/CL/kernels/CLArithmeticDivisionKernel.cpp | 185 ----------- .../CL/kernels/CLArithmeticSubtractionKernel.cpp | 232 -------------- .../CL/kernels/CLElementwiseOperationKernel.cpp | 337 +++++++++++++++++++++ src/runtime/CL/functions/CLArithmeticAddition.cpp | 54 ---- src/runtime/CL/functions/CLArithmeticDivision.cpp | 54 ---- .../CL/functions/CLArithmeticSubtraction.cpp | 54 ---- .../CL/functions/CLElementwiseOperations.cpp | 127 ++++++++ .../CL/functions/CLGEMMConvolutionLayer.cpp | 16 +- src/runtime/CL/functions/CLLSTMLayer.cpp | 18 +- src/runtime/CL/functions/CLLaplacianPyramid.cpp | 4 +- src/runtime/CL/functions/CLRNNLayer.cpp | 6 +- tests/validation/CL/ArithmeticAddition.cpp | 4 +- tests/validation/CL/ArithmeticDivision.cpp | 169 +++++++++-- tests/validation/CL/ArithmeticSubtraction.cpp | 83 ++--- tests/validation/CL/ElementwiseMax.cpp | 277 +++++++++++++++++ tests/validation/CL/ElementwiseMin.cpp | 277 +++++++++++++++++ tests/validation/CL/ElementwiseSquaredDiff.cpp | 278 +++++++++++++++++ .../fixtures/ElementwiseOperationsFixture.h | 286 +++++++++++++++++ .../validation/reference/ElementwiseOperations.cpp | 187 ++++++++++++ tests/validation/reference/ElementwiseOperations.h | 47 +++ utils/TypePrinter.h | 49 +++ 44 files changed, 2813 insertions(+), 1816 deletions(-) delete mode 100644 arm_compute/core/CL/kernels/CLArithmeticAdditionKernel.h delete mode 100644 arm_compute/core/CL/kernels/CLArithmeticDivisionKernel.h delete mode 100644 arm_compute/core/CL/kernels/CLArithmeticSubtractionKernel.h create mode 100644 arm_compute/core/CL/kernels/CLElementwiseOperationKernel.h delete mode 100644 arm_compute/runtime/CL/functions/CLArithmeticAddition.h delete mode 100644 arm_compute/runtime/CL/functions/CLArithmeticDivision.h delete mode 100644 arm_compute/runtime/CL/functions/CLArithmeticSubtraction.h create mode 100644 arm_compute/runtime/CL/functions/CLElementwiseOperations.h delete mode 100644 src/core/CL/cl_kernels/arithmetic_op.cl delete mode 100644 src/core/CL/cl_kernels/arithmetic_op_quantized.cl create mode 100644 src/core/CL/cl_kernels/elementwise_operation.cl create mode 100644 src/core/CL/cl_kernels/elementwise_operation_quantized.cl delete mode 100644 src/core/CL/kernels/CLArithmeticAdditionKernel.cpp delete mode 100644 src/core/CL/kernels/CLArithmeticDivisionKernel.cpp delete mode 100644 src/core/CL/kernels/CLArithmeticSubtractionKernel.cpp create mode 100644 src/core/CL/kernels/CLElementwiseOperationKernel.cpp delete mode 100644 src/runtime/CL/functions/CLArithmeticAddition.cpp delete mode 100644 src/runtime/CL/functions/CLArithmeticDivision.cpp delete mode 100644 src/runtime/CL/functions/CLArithmeticSubtraction.cpp create mode 100644 src/runtime/CL/functions/CLElementwiseOperations.cpp create mode 100644 tests/validation/CL/ElementwiseMax.cpp create mode 100644 tests/validation/CL/ElementwiseMin.cpp create mode 100644 tests/validation/CL/ElementwiseSquaredDiff.cpp create mode 100644 tests/validation/fixtures/ElementwiseOperationsFixture.h create mode 100644 tests/validation/reference/ElementwiseOperations.cpp create mode 100644 tests/validation/reference/ElementwiseOperations.h diff --git a/arm_compute/core/CL/CLKernels.h b/arm_compute/core/CL/CLKernels.h index c7c12975e0..c707265c23 100644 --- a/arm_compute/core/CL/CLKernels.h +++ b/arm_compute/core/CL/CLKernels.h @@ -28,9 +28,6 @@ #include "arm_compute/core/CL/kernels/CLAbsoluteDifferenceKernel.h" #include "arm_compute/core/CL/kernels/CLAccumulateKernel.h" #include "arm_compute/core/CL/kernels/CLActivationLayerKernel.h" -#include "arm_compute/core/CL/kernels/CLArithmeticAdditionKernel.h" -#include "arm_compute/core/CL/kernels/CLArithmeticDivisionKernel.h" -#include "arm_compute/core/CL/kernels/CLArithmeticSubtractionKernel.h" #include "arm_compute/core/CL/kernels/CLBatchNormalizationLayerKernel.h" #include "arm_compute/core/CL/kernels/CLBatchToSpaceLayerKernel.h" #include "arm_compute/core/CL/kernels/CLBitwiseAndKernel.h" @@ -62,6 +59,7 @@ #include "arm_compute/core/CL/kernels/CLDilateKernel.h" #include "arm_compute/core/CL/kernels/CLDirectConvolutionLayerKernel.h" #include "arm_compute/core/CL/kernels/CLDirectConvolutionLayerOutputStageKernel.h" +#include "arm_compute/core/CL/kernels/CLElementwiseOperationKernel.h" #include "arm_compute/core/CL/kernels/CLErodeKernel.h" #include "arm_compute/core/CL/kernels/CLFastCornersKernel.h" #include "arm_compute/core/CL/kernels/CLFillBorderKernel.h" diff --git a/arm_compute/core/CL/kernels/CLArithmeticAdditionKernel.h b/arm_compute/core/CL/kernels/CLArithmeticAdditionKernel.h deleted file mode 100644 index 48e72f3c13..0000000000 --- a/arm_compute/core/CL/kernels/CLArithmeticAdditionKernel.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2016-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef __ARM_COMPUTE_CLARITHMETICADDITIONKERNEL_H__ -#define __ARM_COMPUTE_CLARITHMETICADDITIONKERNEL_H__ - -#include "arm_compute/core/CL/ICLKernel.h" -#include "arm_compute/core/Types.h" - -namespace arm_compute -{ -class ICLTensor; - -/** Interface for the arithmetic addition kernel - * - * Arithmetic addition is computed by: - * @f[ output(x,y) = input1(x,y) + input2(x,y) @f] - */ -class CLArithmeticAdditionKernel : public ICLKernel -{ -public: - /** Default constructor */ - CLArithmeticAdditionKernel(); - /** Prevent instances of this class from being copied (As this class contains pointers) */ - CLArithmeticAdditionKernel(const CLArithmeticAdditionKernel &) = delete; - /** Prevent instances of this class from being copied (As this class contains pointers) */ - CLArithmeticAdditionKernel &operator=(const CLArithmeticAdditionKernel &) = delete; - /** Allow instances of this class to be moved */ - CLArithmeticAdditionKernel(CLArithmeticAdditionKernel &&) = default; - /** Allow instances of this class to be moved */ - CLArithmeticAdditionKernel &operator=(CLArithmeticAdditionKernel &&) = default; - /** Default destructor */ - ~CLArithmeticAdditionKernel() = default; - /** Initialise the kernel's inputs, output and conversion policy. - * - * @param[in] input1 First tensor input. Data types supported: U8/QASYMM8/S16/F16/F32. - * @param[in] input2 Second tensor input. Data types supported: U8, QASYMM8 (only if @p input1 is QASYMM8), S16/F16/F32. - * @param[out] output Output tensor. Data types supported: U8 (Only if both inputs are U8), QASYMM8 (only if @p input1 is QASYMM8), S16/F16/F32. - * @param[in] policy Policy to use to handle overflow. - */ - void configure(const ICLTensor *input1, const ICLTensor *input2, ICLTensor *output, ConvertPolicy policy); - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticAdditionKernel - * - * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/S16/F16/F32. - * @param[in] input2 Second tensor input info. Data types supported: U8, QASYMM8 (only if @p input1 is QASYMM8), S16/F16/F32. - * @param[in] output Output tensor info. Data types supported: U8 (Only if both inputs are U8), QASYMM8 (only if both inputs are QASYMM8), S16/F16/F32. - * @param[in] policy Policy to use to handle overflow. - * - * @return a status - */ - static Status validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy); - - // Inherited methods overridden: - void run(const Window &window, cl::CommandQueue &queue) override; - BorderSize border_size() const override; - -private: - const ICLTensor *_input1; /**< Source tensor 1 */ - const ICLTensor *_input2; /**< Source tensor 2 */ - ICLTensor *_output; /**< Destination tensor */ -}; -} // namespace arm_compute -#endif /* __ARM_COMPUTE_CLARITHMETICADDITIONKERNEL_H__ */ diff --git a/arm_compute/core/CL/kernels/CLArithmeticDivisionKernel.h b/arm_compute/core/CL/kernels/CLArithmeticDivisionKernel.h deleted file mode 100644 index 430a641559..0000000000 --- a/arm_compute/core/CL/kernels/CLArithmeticDivisionKernel.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef __ARM_COMPUTE_CLARITHMETICDIVISIONKERNEL_H__ -#define __ARM_COMPUTE_CLARITHMETICDIVISIONKERNEL_H__ - -#include "arm_compute/core/CL/ICLKernel.h" -#include "arm_compute/core/Types.h" - -namespace arm_compute -{ -class ICLTensor; - -/** Interface for the arithmetic division kernel - * - * Arithmetic division is computed by: - * @f[ output(x,y) = input1(x,y) / input2(x,y) @f] - */ -class CLArithmeticDivisionKernel : public ICLKernel -{ -public: - /** Default constructor */ - CLArithmeticDivisionKernel(); - /** Prevent instances of this class from being copied (As this class contains pointers) */ - CLArithmeticDivisionKernel(const CLArithmeticDivisionKernel &) = delete; - /** Prevent instances of this class from being copied (As this class contains pointers) */ - CLArithmeticDivisionKernel &operator=(const CLArithmeticDivisionKernel &) = delete; - /** Allow instances of this class to be moved */ - CLArithmeticDivisionKernel(CLArithmeticDivisionKernel &&) = default; - /** Allow instances of this class to be moved */ - CLArithmeticDivisionKernel &operator=(CLArithmeticDivisionKernel &&) = default; - /** Default destructor */ - ~CLArithmeticDivisionKernel() = default; - /** Initialise the kernel's inputs, output. - * - * @param[in] input1 First tensor input. Data types supported: F16/F32. - * @param[in] input2 Second tensor input. Data types supported: Same as @p input1. - * @param[out] output Output tensor. Data types supported: Same as @p input1. - */ - void configure(const ICLTensor *input1, const ICLTensor *input2, ICLTensor *output); - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticDivisionKernel - * - * @param[in] input1 First tensor input info. Data types supported: F16/F32. - * @param[in] input2 Second tensor input info. Data types supported: Same as @p input1. - * @param[in] output Output tensor info. Data types supported: Same as @p input1. - * - * @return a status - */ - static Status validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output); - - // Inherited methods overridden: - void run(const Window &window, cl::CommandQueue &queue) override; - BorderSize border_size() const override; - -private: - const ICLTensor *_input1; /**< Source tensor 1 */ - const ICLTensor *_input2; /**< Source tensor 2 */ - ICLTensor *_output; /**< Destination tensor */ -}; -} // namespace arm_compute -#endif /* __ARM_COMPUTE_CLARITHMETICDIVISIONKERNEL_H__ */ diff --git a/arm_compute/core/CL/kernels/CLArithmeticSubtractionKernel.h b/arm_compute/core/CL/kernels/CLArithmeticSubtractionKernel.h deleted file mode 100644 index 9875ac7a31..0000000000 --- a/arm_compute/core/CL/kernels/CLArithmeticSubtractionKernel.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2016-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef __ARM_COMPUTE_CLARITHMETICSUBTRACTIONKERNEL_H__ -#define __ARM_COMPUTE_CLARITHMETICSUBTRACTIONKERNEL_H__ - -#include "arm_compute/core/CL/ICLKernel.h" - -#include "arm_compute/core/Types.h" - -namespace arm_compute -{ -class ICLTensor; - -/** Interface for the arithmetic subtraction kernel - * - * Arithmetic subtraction is computed by: - * @f[ output(x,y) = input1(x,y) - input2(x,y) @f] - */ -class CLArithmeticSubtractionKernel : public ICLKernel -{ -public: - /** Default constructor */ - CLArithmeticSubtractionKernel(); - /** Prevent instances of this class from being copied (As this class contains pointers) */ - CLArithmeticSubtractionKernel(const CLArithmeticSubtractionKernel &) = delete; - /** Prevent instances of this class from being copied (As this class contains pointers) */ - CLArithmeticSubtractionKernel &operator=(const CLArithmeticSubtractionKernel &) = delete; - /** Allow instances of this class to be moved */ - CLArithmeticSubtractionKernel(CLArithmeticSubtractionKernel &&) = default; - /** Allow instances of this class to be moved */ - CLArithmeticSubtractionKernel &operator=(CLArithmeticSubtractionKernel &&) = default; - /** Default destructor */ - ~CLArithmeticSubtractionKernel() = default; - - /** Initialise the kernel's inputs, output and conversion policy. - * - * @param[in] input1 First tensor input. Data types supported: U8/QASYMM8/S16/F16/F32. - * @param[in] input2 Second tensor input. Data types supported: U8/QASYMM8/S16/F16/F32. - * @param[out] output Output tensor. Data types supported: U8 (Only if both inputs are U8), QASYMM8/S16/F16/F32. - * @param[in] policy Policy to use to handle overflow. - */ - void configure(const ICLTensor *input1, const ICLTensor *input2, ICLTensor *output, ConvertPolicy policy); - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticSubtractionKernel - * - * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/S16/F16/F32. - * @param[in] input2 Second tensor input info. Data types supported: U8/QASYMM8/S16/F16/F32. - * @param[in] output Output tensor info. Data types supported: U8 (Only if both inputs are U8), QASYMM8/S16/F16/F32. - * @param[in] policy Policy to use to handle overflow. - * - * @return a status - */ - static Status validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy); - - // Inherited methods overridden: - void run(const Window &window, cl::CommandQueue &queue) override; - BorderSize border_size() const override; - -private: - const ICLTensor *_input1; /**< Source tensor 1 */ - const ICLTensor *_input2; /**< Source tensor 2 */ - ICLTensor *_output; /**< Destination tensor */ -}; -} // namespace arm_compute -#endif /* __ARM_COMPUTE_CLARITHMETICSUBTRACTIONKERNEL_H__ */ diff --git a/arm_compute/core/CL/kernels/CLElementwiseOperationKernel.h b/arm_compute/core/CL/kernels/CLElementwiseOperationKernel.h new file mode 100644 index 0000000000..2c65789115 --- /dev/null +++ b/arm_compute/core/CL/kernels/CLElementwiseOperationKernel.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __ARM_COMPUTE_CLELEMENTWISEOPERATIONKERNEL_H__ +#define __ARM_COMPUTE_CLELEMENTWISEOPERATIONKERNEL_H__ + +#include "arm_compute/core/CL/ICLKernel.h" +#include "arm_compute/core/Types.h" + +namespace arm_compute +{ +class ICLTensor; + +/** Interface for an element-wise operation kernel + * + * Element-wise operation is computed by: + * @f[ output(x,y) = OP(input1(x,y), input2(x,y))@f] + * + */ +class CLElementwiseOperationKernel : public ICLKernel +{ +public: + /** Default constructor */ + CLElementwiseOperationKernel(); + /** Prevent instances of this class from being copied (As this class contains pointers) */ + CLElementwiseOperationKernel(const CLElementwiseOperationKernel &) = delete; + /** Prevent instances of this class from being copied (As this class contains pointers) */ + CLElementwiseOperationKernel &operator=(const CLElementwiseOperationKernel &) = delete; + /** Allow instances of this class to be moved */ + CLElementwiseOperationKernel(CLElementwiseOperationKernel &&) = default; + /** Allow instances of this class to be moved */ + CLElementwiseOperationKernel &operator=(CLElementwiseOperationKernel &&) = default; + /** Default destructor */ + ~CLElementwiseOperationKernel() = default; + + // Inherited methods overridden: + void run(const Window &window, cl::CommandQueue &queue) override; + + BorderSize border_size() const override; + +protected: + /** The name of the operation */ + virtual std::string name() = 0; + + /** Initialise the kernel's output. + * + * @param[in] input1 First tensor input. Data types supported: U8/S8/QASYMM8/U16/S16/F16/U32/S32/F32. + * @param[in] input2 Second tensor input. Data types supported: Same as @p input1. + * @param[in] output Output tensor. Data types supported: Same as @p input1. + * + * @return a pair of Status and Window + */ + virtual std::pair validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) = 0; + + /** Validate the argument passed to the kernel + * + * @param[in] input1 First tensor input. Data types supported: U8/S8/QASYMM8/U16/S16/F16/U32/S32/F32. + * @param[in] input2 Second tensor input. Data types supported: Same as @p input1. + * @param[in] output Output tensor. Data types supported: Same as @p input1. + */ + virtual Status validate_arguments(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) = 0; + + /** Generate the build options for the specific kernel + * + * @reutrn a CLBuildOptions struct + */ + virtual CLBuildOptions generate_build_options(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) = 0; + + /** Generate the identifier for tuning + * + * @reutrn a string + */ + virtual std::string generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) = 0; + + /** Commmon configure function for element-wise operators with no additional options (e.g., Div, Min, Max, SquaredDiff) + * + */ + void configure_common(const ICLTensor *input1, const ICLTensor *input2, ICLTensor *output); + +private: + const ICLTensor *_input1; /**< Source tensor 1 */ + const ICLTensor *_input2; /**< Source tensor 2 */ + ICLTensor *_output; /**< Destination tensor */ +}; + +/** Addition operation */ +class CLSaturatedArithmeticOperationKernel : public CLElementwiseOperationKernel +{ +public: + CLSaturatedArithmeticOperationKernel() + : CLElementwiseOperationKernel(), _policy(), _op() + { + } + + /** Static function to check if given info will lead to a valid configuration of @ref CLSaturatedArithmeticOperationKernel + * + * @param[in] op Arithmetic operation to be executed. + * @param[in] input1 First tensor input. Data types supported: U8/S8/QASYMM8/U16/S16/F16/U32/S32/F32. + * @param[in] input1 First tensor input. Data types supported: U8/S8/QASYMM8/U16/S16/F16/U32/S32/F32. + * @param[in] input2 Second tensor input. Data types supported: Same as @p input1. + * @param[in] output Output tensor. Data types supported: Same as @p input1. + * @param[in] policy Policy to use to handle overflow. + */ + void configure(ArithmeticOperation op, const ICLTensor *input1, const ICLTensor *input2, ICLTensor *output, const ConvertPolicy &policy); + + /** Static function to check if given info will lead to a valid configuration of @ref CLSaturatedArithmeticOperationKernel + * + * @param[in] op Arithmetic operation to be executed. + * @param[in] input1 First tensor input info. Data types supported: U8/S8/QASYMM8/U16/S16/F16/U32/S32/F32. + * @param[in] input1 First tensor input info. Data types supported: U8/S8/QASYMM8/U16/S16/F16/U32/S32/F32. + * @param[in] input2 Second tensor input info. Data types supported: Same as @p input1. + * @param[in] output Output tensor info. Data types supported: Same as @p input1. + * @param[in] policy Policy to use to handle overflow. + * + * @return a Status + */ + static Status validate(ArithmeticOperation op, const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, const ConvertPolicy &policy); + +protected: + // Inherited methods overridden: + std::string name() override; + std::pair validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) override; + Status validate_arguments(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) override; + CLBuildOptions generate_build_options(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) override; + std::string generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) override; + +private: + ConvertPolicy _policy; + ArithmeticOperation _op; +}; + +class CLArithmeticOperationKernel : public CLElementwiseOperationKernel +{ +public: + CLArithmeticOperationKernel() + : CLElementwiseOperationKernel(), _op() + { + } + + /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel + * + * @param[in] op Arithmetic operation to be executed. + * @param[in] input1 First tensor input. Data types supported: U8/S8/QASYMM8/U16/S16/F16/U32/S32/F32. + * @param[in] input1 First tensor input. Data types supported: U8/S8/QASYMM8/U16/S16/F16/U32/S32/F32. + * @param[in] input2 Second tensor input. Data types supported: Same as @p input1. + * @param[in] output Output tensor. Data types supported: Same as @p input1. + */ + void configure(ArithmeticOperation op, const ICLTensor *input1, const ICLTensor *input2, ICLTensor *output); + + /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel + * + * @param[in] op Arithmetic operation to be executed. + * @param[in] input1 First tensor input info. Data types supported: U8/S8/QASYMM8/U16/S16/F16/U32/S32/F32. + * @param[in] input1 First tensor input info. Data types supported: U8/S8/QASYMM8/U16/S16/F16/U32/S32/F32. + * @param[in] input2 Second tensor input info. Data types supported: Same as @p input1. + * @param[in] output Output tensor info. Data types supported: Same as @p input1. + * + * @return a Status + */ + static Status validate(ArithmeticOperation op, const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output); + +protected: + // Inherited methods overridden: + std::string name() override; + std::pair validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) override; + Status validate_arguments(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) override; + CLBuildOptions generate_build_options(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) override; + std::string generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) override; + +private: + ArithmeticOperation _op; +}; +} // namespace arm_compute +#endif /* __ARM_COMPUTE_CLELEMENTWISEOPERATIONKERNEL_H__ */ diff --git a/arm_compute/core/Types.h b/arm_compute/core/Types.h index 7db2f5fddf..7d632fec28 100644 --- a/arm_compute/core/Types.h +++ b/arm_compute/core/Types.h @@ -552,6 +552,17 @@ enum class ReductionOperation ARG_IDX_MIN /**< Index of the min value */ }; +/** Available element-wise operations */ +enum class ArithmeticOperation +{ + ADD, /**< (x + y) */ + SUB, /**< (x - y) */ + DIV, /**< (x / y) */ + MIN, /**< Min(x, y) */ + MAX, /**< Max(x, y) */ + SQUARED_DIFF, /**< (x - y)^2 */ +}; + /** The normalization type used for the normalization layer */ enum class NormType { diff --git a/arm_compute/runtime/CL/CLFunctions.h b/arm_compute/runtime/CL/CLFunctions.h index 780597ef07..e68e719a13 100644 --- a/arm_compute/runtime/CL/CLFunctions.h +++ b/arm_compute/runtime/CL/CLFunctions.h @@ -29,9 +29,6 @@ #include "arm_compute/runtime/CL/functions/CLAccumulate.h" #include "arm_compute/runtime/CL/functions/CLActivationLayer.h" #include "arm_compute/runtime/CL/functions/CLArgMinMaxLayer.h" -#include "arm_compute/runtime/CL/functions/CLArithmeticAddition.h" -#include "arm_compute/runtime/CL/functions/CLArithmeticDivision.h" -#include "arm_compute/runtime/CL/functions/CLArithmeticSubtraction.h" #include "arm_compute/runtime/CL/functions/CLBatchNormalizationLayer.h" #include "arm_compute/runtime/CL/functions/CLBatchToSpaceLayer.h" #include "arm_compute/runtime/CL/functions/CLBitwiseAnd.h" @@ -63,6 +60,7 @@ #include "arm_compute/runtime/CL/functions/CLDerivative.h" #include "arm_compute/runtime/CL/functions/CLDilate.h" #include "arm_compute/runtime/CL/functions/CLDirectConvolutionLayer.h" +#include "arm_compute/runtime/CL/functions/CLElementwiseOperations.h" #include "arm_compute/runtime/CL/functions/CLEqualizeHistogram.h" #include "arm_compute/runtime/CL/functions/CLErode.h" #include "arm_compute/runtime/CL/functions/CLFastCorners.h" diff --git a/arm_compute/runtime/CL/functions/CLArithmeticAddition.h b/arm_compute/runtime/CL/functions/CLArithmeticAddition.h deleted file mode 100644 index 5aba60ad01..0000000000 --- a/arm_compute/runtime/CL/functions/CLArithmeticAddition.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2016-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef __ARM_COMPUTE_CLARITHMETICADDITION_H__ -#define __ARM_COMPUTE_CLARITHMETICADDITION_H__ - -#include "arm_compute/core/Types.h" -#include "arm_compute/runtime/CL/ICLSimpleFunction.h" - -namespace arm_compute -{ -class ICLTensor; - -/** Basic function to run @ref CLArithmeticAdditionKernel - * - * @note The tensor data type for the inputs must be U8/S16/F16/F32. - * @note The function performs an arithmetic addition between two tensors. - */ -class CLArithmeticAddition : public ICLSimpleFunction -{ -public: - /** Initialise the kernel's inputs, output and convertion policy. - * - * @param[in, out] input1 First tensor input. Data types supported: U8/QASYMM8/S16/F16/F32. - * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. - * @param[in, out] input2 Second tensor input. Data types supported: U8, QASYMM8 (only if @p input1 is QASYMM8), S16/F16/F32. - * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. - * @param[out] output Output tensor. Data types supported: U8 (Only if both inputs are U8), QASYMM8 (only if both inputs are QASYMM8), S16/F16/F32. - * @param[in] policy Policy to use to handle overflow. - */ - void configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output, ConvertPolicy policy); - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticAddition - * - * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/S16/F16/F32. - * @param[in] input2 Second tensor input info. Data types supported: U8, QASYMM8 (only if @p input1 is QASYMM8), S16/F16/F32. - * @param[in] output Output tensor info. Data types supported: U8 (Only if both inputs are U8), QASYMM8 ( only if both inputs are QASYMM8), S16/F16/F32. - * @param[in] policy Policy to use to handle overflow. - * - * @return a status - */ - static Status validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy); -}; -} -#endif /* __ARM_COMPUTE_CLARITHMETICADDITION_H__ */ diff --git a/arm_compute/runtime/CL/functions/CLArithmeticDivision.h b/arm_compute/runtime/CL/functions/CLArithmeticDivision.h deleted file mode 100644 index c91435cee9..0000000000 --- a/arm_compute/runtime/CL/functions/CLArithmeticDivision.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef __ARM_COMPUTE_CLARITHMETICDIVISION_H__ -#define __ARM_COMPUTE_CLARITHMETICDIVISION_H__ - -#include "arm_compute/core/Types.h" -#include "arm_compute/runtime/CL/ICLSimpleFunction.h" - -namespace arm_compute -{ -class ICLTensor; - -/** Basic function to run @ref CLArithmeticDivisionKernel - * - * @note The tensor data type for the inputs must be F16/F32. - * @note The function performs an arithmetic division between two tensors. - */ -class CLArithmeticDivision : public ICLSimpleFunction -{ -public: - /** Initialise the kernel's inputs, output. - * - * @param[in, out] input1 First tensor input. Data types supported: F16/F32. - * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. - * @param[in, out] input2 Second tensor input. Same as @p input1. - * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. - * @param[out] output Output tensor. Data types supported: Same as @p input1. - */ - void configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output); - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticDivision - * - * @param[in] input1 First tensor input info. Data types supported: F16/F32. - * @param[in] input2 Second tensor input info. Data types supported: Same as @p input1. - * @param[in] output Output tensor info. Data types supported: Same as @p input1. - * - * @return a status - */ - static Status validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output); -}; -} -#endif /* __ARM_COMPUTE_CLARITHMETICDIVISION_H__ */ diff --git a/arm_compute/runtime/CL/functions/CLArithmeticSubtraction.h b/arm_compute/runtime/CL/functions/CLArithmeticSubtraction.h deleted file mode 100644 index 2940044ed9..0000000000 --- a/arm_compute/runtime/CL/functions/CLArithmeticSubtraction.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2016-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef __ARM_COMPUTE_CLARITHMETICSUBTRACTION_H__ -#define __ARM_COMPUTE_CLARITHMETICSUBTRACTION_H__ - -#include "arm_compute/runtime/CL/ICLSimpleFunction.h" - -#include "arm_compute/core/Types.h" - -namespace arm_compute -{ -class ICLTensor; - -/** Basic function to run @ref CLArithmeticSubtractionKernel - * - * @note The tensor data type for the inputs must be U8/S16/F16/F32. - * @note The function performs an arithmetic subtraction between two tensors. - * - * This function calls the following kernels: - * -# @ref CLFillBorderKernel (In case of broadcasting, in the input being broadcasted) - * -# @ref CLArithmeticSubtractionKernel - */ -class CLArithmeticSubtraction : public ICLSimpleFunction -{ -public: - /** Initialise the kernel's inputs, output and convertion policy. - * - * @param[in] input1 First tensor input. Data types supported: U8/S16/F16/F32. - * @param[in] input2 Second tensor input. Data types supported: U8/S16/F16/F32. - * @param[out] output Output tensor. Data types supported: U8 (Only if both inputs are U8), S16/F16/F32. - * @param[in] policy Policy to use to handle overflow. - */ - void configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output, ConvertPolicy policy); - /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticSubtraction - * - * @param[in] input1 First tensor input info. Data types supported: U8/S16/F16/F32. - * @param[in] input2 Second tensor input info. Data types supported: U8/S16/F16/F32. - * @param[in] output Output tensor info. Data types supported: U8 (Only if both inputs are U8), S16/F16/F32. - * @param[in] policy Policy to use to handle overflow. - * - * @return a status - */ - static Status validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy); -}; -} -#endif /* __ARM_COMPUTE_CLARITHMETICSUBTRACTION_H__ */ diff --git a/arm_compute/runtime/CL/functions/CLElementwiseOperations.h b/arm_compute/runtime/CL/functions/CLElementwiseOperations.h new file mode 100644 index 0000000000..4a0911ec4e --- /dev/null +++ b/arm_compute/runtime/CL/functions/CLElementwiseOperations.h @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARI SING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __ARM_COMPUTE_CLELEMENTWISEOPERATIONS_H__ +#define __ARM_COMPUTE_CLELEMENTWISEOPERATIONS_H__ + +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/CL/ICLSimpleFunction.h" + +namespace arm_compute +{ +class ICLTensor; + +/** Basic function to run @ref CLSaturatedArithmeticOperationKernel for addition + * + * @note The tensor data type for the inputs must be U8/QASYMM8/S16/S32/U32/F16/F32. + * @note The function performs an arithmetic addition between two tensors. + */ +class CLArithmeticAddition : public ICLSimpleFunction +{ +public: + /** Initialise the kernel's inputs, output and conversion policy. + * + * @param[in, out] input1 First tensor input. Data types supported: U8/QASYMM8/S16/S32/U32/F16/F32. + * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. + * @param[in, out] input2 Second tensor input. Data types supported: U8, QASYMM8 (only if @p input1 is QASYMM8), S16/F16/F32. + * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. + * @param[out] output Output tensor. Data types supported: U8 (Only if both inputs are U8), QASYMM8 (only if both inputs are QASYMM8), S16/F16/F32. + * @param[in] policy Policy to use to handle overflow. + */ + void configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output, ConvertPolicy policy); + /** Static function to check if given info will lead to a valid configuration of @ref CLSaturatedArithmeticOperationKernel for addition + * + * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/S16/S32/U32/F16/F32. + * @param[in] input2 Second tensor input info. Data types supported: U8, QASYMM8 (only if @p input1 is QASYMM8), S16/F16/F32. + * @param[in] output Output tensor info. Data types supported: U8 (Only if both inputs are U8), QASYMM8 ( only if both inputs are QASYMM8), S16/F16/F32. + * @param[in] policy Policy to use to handle overflow. + * + * @return a status + */ + static Status validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy); +}; + +/** Basic function to run @ref CLSaturatedArithmeticOperationKernel for subtraction + * + * @note The tensor data type for the inputs must be U8/QASYMM8/S16/S32/U32/F16/F32. + * @note The function performs an arithmetic subtraction between two tensors. + */ +class CLArithmeticSubtraction : public ICLSimpleFunction +{ +public: + /** Initialise the kernel's inputs, output and conversion policy. + * + * @param[in, out] input1 First tensor input. Data types supported: U8/QASYMM8/S16/S32/U32/F16/F32. + * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. + * @param[in, out] input2 Second tensor input. Data types supported: U8, QASYMM8 (only if @p input1 is QASYMM8), S16/F16/F32. + * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. + * @param[out] output Output tensor. Data types supported: U8 (Only if both inputs are U8), QASYMM8 (only if both inputs are QASYMM8), S16/F16/F32. + * @param[in] policy Policy to use to handle overflow. + */ + void configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output, ConvertPolicy policy); + /** Static function to check if given info will lead to a valid configuration of @ref CLSaturatedArithmeticOperationKernel for subtraction + * + * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/S16/S32/U32/F16/F32. + * @param[in] input2 Second tensor input info. Data types supported: U8, QASYMM8 (only if @p input1 is QASYMM8), S16/F16/F32. + * @param[in] output Output tensor info. Data types supported: U8 (Only if both inputs are U8), QASYMM8 ( only if both inputs are QASYMM8), S16/F16/F32. + * @param[in] policy Policy to use to handle overflow. + * + * @return a status + */ + static Status validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy); +}; + +/** Basic function to run @ref CLSaturatedArithmeticOperationKernel for division + * + * @note The tensor data type for the inputs must be F16/F32. + * @note The function performs an arithmetic division between two tensors. + */ +class CLArithmeticDivision : public ICLSimpleFunction +{ +public: + /** Initialise the kernel's inputs, output. + * + * @param[in, out] input1 First tensor input. Data types supported: F16/F32. + * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. + * @param[in, out] input2 Second tensor input. Same as @p input1. + * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. + * @param[out] output Output tensor. Data types supported: Same as @p input1. + */ + void configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output); + /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticDivision + * + * @param[in] input1 First tensor input info. Data types supported: F16/F32. + * @param[in] input2 Second tensor input info. Data types supported: Same as @p input1. + * @param[in] output Output tensor info. Data types supported: Same as @p input1. + * + * @return a status + */ + static Status validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output); +}; + +/** Basic function to run @ref CLArithmeticOperationKernel for max + * + * @note The tensor data type for the inputs must be U8/QASYMM8/S16/S32/U32/F16/F32. + * @note The function performs a max operation between two tensors. + */ +class CLElementwiseMax : public ICLSimpleFunction +{ +public: + /** Initialise the kernel's inputs, output and conversion policy. + * + * @param[in, out] input1 First tensor input. Data types supported: U8/QASYMM8/S16/S32/U32/F16/F32. + * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. + * @param[in, out] input2 Second tensor input. Data types supported: U8, QASYMM8 (only if @p input1 is QASYMM8), S16/F16/F32. + * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. + * @param[out] output Output tensor. Data types supported: U8 (Only if both inputs are U8), QASYMM8 (only if both inputs are QASYMM8), S16/F16/F32. + */ + void configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output); + /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel for max + * + * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/S16/S32/U32/F16/F32. + * @param[in] input2 Second tensor input info. Data types supported: U8, QASYMM8 (only if @p input1 is QASYMM8), S16/F16/F32. + * @param[in] output Output tensor info. Data types supported: U8 (Only if both inputs are U8), QASYMM8 ( only if both inputs are QASYMM8), S16/F16/F32. + * + * @return a status + */ + static Status validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output); +}; + +/** Basic function to run @ref CLArithmeticOperationKernel for min + * + * @note The tensor data type for the inputs must be U8/QASYMM8/S16/S32/U32/F16/F32. + * @note The function performs a max operation between two tensors. + */ +class CLElementwiseMin : public ICLSimpleFunction +{ +public: + /** Initialise the kernel's inputs, output and conversion policy. + * + * @param[in, out] input1 First tensor input. Data types supported: U8/QASYMM8/S16/S32/U32/F16/F32. + * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. + * @param[in, out] input2 Second tensor input. Data types supported: U8, QASYMM8 (only if @p input1 is QASYMM8), S16/F16/F32. + * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. + * @param[out] output Output tensor. Data types supported: U8 (Only if both inputs are U8), QASYMM8 (only if both inputs are QASYMM8), S16/F16/F32. + */ + void configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output); + /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel for min + * + * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/S16/S32/U32/F16/F32. + * @param[in] input2 Second tensor input info. Data types supported: U8, QASYMM8 (only if @p input1 is QASYMM8), S16/F16/F32. + * @param[in] output Output tensor info. Data types supported: U8 (Only if both inputs are U8), QASYMM8 ( only if both inputs are QASYMM8), S16/F16/F32. + * + * @return a status + */ + static Status validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output); +}; + +/** Basic function to run @ref CLArithmeticOperationKernel for squared difference + * + * @note The tensor data type for the inputs must be QASYMM8/U8/S16/F16/F32. + * @note The function performs a squared different operation between two tensors (i.e., out[i] = (in1[i] - in2[i])^2 + */ +class CLElementwiseSquaredDiff : public ICLSimpleFunction +{ +public: + /** Initialise the kernel's inputs, output and conversion policy. + * + * @param[in, out] input1 First tensor input. Data types supported: U8/QASYMM8/S16/F16/F32. + * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. + * @param[in, out] input2 Second tensor input. Data types supported: U8, QASYMM8 (only if @p input1 is QASYMM8), S16/F16/F32. + * The input tensor is [in, out] because its TensorInfo might be modified inside the kernel in case of broadcasting of dimension 0. + * @param[out] output Output tensor. Data types supported: U8 (Only if both inputs are U8), QASYMM8 (only if both inputs are QASYMM8), S16/F16/F32. + */ + void configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output); + /** Static function to check if given info will lead to a valid configuration of @ref CLArithmeticOperationKernel for squared difference + * + * @param[in] input1 First tensor input info. Data types supported: U8/QASYMM8/S16/F16/F32. + * @param[in] input2 Second tensor input info. Data types supported: U8, QASYMM8 (only if @p input1 is QASYMM8), S16/F16/F32. + * @param[in] output Output tensor info. Data types supported: U8 (Only if both inputs are U8), QASYMM8 ( only if both inputs are QASYMM8), S16/F16/F32. + * + * @return a status + */ + static Status validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output); +}; +} // namespace arm_compute +#endif /* __ARM_COMPUTE_CLELEMENTWISEOPERATIONS_H__ */ diff --git a/arm_compute/runtime/CL/functions/CLGEMMConvolutionLayer.h b/arm_compute/runtime/CL/functions/CLGEMMConvolutionLayer.h index fbf0c08b36..1468b156eb 100644 --- a/arm_compute/runtime/CL/functions/CLGEMMConvolutionLayer.h +++ b/arm_compute/runtime/CL/functions/CLGEMMConvolutionLayer.h @@ -26,8 +26,8 @@ #include "arm_compute/runtime/IFunction.h" -#include "arm_compute/core/CL/kernels/CLArithmeticAdditionKernel.h" #include "arm_compute/core/CL/kernels/CLCol2ImKernel.h" +#include "arm_compute/core/CL/kernels/CLElementwiseOperationKernel.h" #include "arm_compute/core/CL/kernels/CLGEMMInterleave4x4Kernel.h" #include "arm_compute/core/CL/kernels/CLGEMMMatrixMultiplyKernel.h" #include "arm_compute/core/CL/kernels/CLGEMMTranspose1xWKernel.h" @@ -90,7 +90,7 @@ private: * -# @ref CLGEMM (if the data type is FP32 or FP16) * -# @ref CLGEMMLowpMatrixMultiplyCore (if the data type is QASYMM8) * -# @ref CLGEMMLowpQuantizeDownInt32ToUint8ScaleByFixedPoint (if the data type is QASYMM8) - * -# @ref CLArithmeticAdditionKernel (if biases != nullptr and we have a 1x1 convolution with the NHWC data layout) + * -# @ref CLElementwiseOperationKernel for addition (if biases != nullptr and we have a 1x1 convolution with the NHWC data layout) * -# @ref CLCol2ImKernel (if NCHW data layout) */ class CLGEMMConvolutionLayer : public IFunction @@ -185,14 +185,14 @@ private: int gemm_3d_depth = 1, bool skip_im2col = false); private: - CLMemoryGroup _memory_group; - CLConvolutionLayerReshapeWeights _reshape_weights; - CLIm2ColKernel _im2col_kernel; - CLGEMM _mm_gemm; - CLGEMMLowpMatrixMultiplyCore _mm_gemmlowp; - CLCol2ImKernel _col2im_kernel; - CLActivationLayer _activationlayer_function; - CLArithmeticAdditionKernel _add_bias_kernel; + CLMemoryGroup _memory_group; + CLConvolutionLayerReshapeWeights _reshape_weights; + CLIm2ColKernel _im2col_kernel; + CLGEMM _mm_gemm; + CLGEMMLowpMatrixMultiplyCore _mm_gemmlowp; + CLCol2ImKernel _col2im_kernel; + CLActivationLayer _activationlayer_function; + CLSaturatedArithmeticOperationKernel _add_bias_kernel; const ICLTensor *_original_weights; diff --git a/arm_compute/runtime/CL/functions/CLLSTMLayer.h b/arm_compute/runtime/CL/functions/CLLSTMLayer.h index 72e41a7aca..87fb1190b7 100644 --- a/arm_compute/runtime/CL/functions/CLLSTMLayer.h +++ b/arm_compute/runtime/CL/functions/CLLSTMLayer.h @@ -27,14 +27,13 @@ #include "arm_compute/runtime/IFunction.h" #include "arm_compute/core/CL/kernels/CLActivationLayerKernel.h" -#include "arm_compute/core/CL/kernels/CLArithmeticAdditionKernel.h" -#include "arm_compute/core/CL/kernels/CLArithmeticSubtractionKernel.h" #include "arm_compute/core/CL/kernels/CLCopyKernel.h" +#include "arm_compute/core/CL/kernels/CLElementwiseOperationKernel.h" #include "arm_compute/core/CL/kernels/CLPixelWiseMultiplicationKernel.h" #include "arm_compute/core/Types.h" #include "arm_compute/runtime/CL/CLMemoryGroup.h" #include "arm_compute/runtime/CL/CLTensor.h" -#include "arm_compute/runtime/CL/functions/CLArithmeticAddition.h" +#include "arm_compute/runtime/CL/functions/CLElementwiseOperations.h" #include "arm_compute/runtime/CL/functions/CLFullyConnectedLayer.h" #include "arm_compute/runtime/CL/functions/CLGEMM.h" #include "arm_compute/runtime/CL/functions/CLWidthConcatenateLayer.h" @@ -141,76 +140,76 @@ public: void run() override; private: - CLMemoryGroup _memory_group; - CLFullyConnectedLayer _fully_connected_input_gate; - CLGEMM _gemm_input_gate; - CLTransposeKernel _transpose_input_gate; - CLArithmeticAdditionKernel _accum_input_gate1; - CLArithmeticAddition _accum_input_gate2; - CLArithmeticSubtractionKernel _subtract_input_gate; - CLPixelWiseMultiplicationKernel _pixelwise_mul_input_gate; - CLActivationLayerKernel _activation_input_gate; - CLFullyConnectedLayer _fully_connected_forget_gate; - CLGEMM _gemm_forget_gate; - CLTransposeKernel _transpose_forget_gate; - CLArithmeticAdditionKernel _accum_forget_gate1; - CLArithmeticAddition _accum_forget_gate2; - CLPixelWiseMultiplicationKernel _pixelwise_mul_forget_gate; - CLActivationLayerKernel _activation_forget_gate; - CLFullyConnectedLayer _fully_connected_cell_state; - CLGEMM _gemm_cell_state1; - CLGEMM _gemm_cell_state2; - CLTransposeKernel _transpose_cell_state; - CLArithmeticAdditionKernel _accum_cell_state1; - CLArithmeticAdditionKernel _accum_cell_state2; - CLPixelWiseMultiplicationKernel _pixelwise_mul_cell_state1; - CLActivationLayerKernel _activation_cell_state; - CLActivationLayerKernel _cell_clip; - CLPixelWiseMultiplicationKernel _pixelwise_mul_cell_state2; - CLFullyConnectedLayer _fully_connected_output; - CLGEMM _gemm_output; - CLPixelWiseMultiplicationKernel _pixelwise_mul_output_state1; - CLTransposeKernel _transpose_output; - CLArithmeticAdditionKernel _accum_output1; - CLArithmeticAddition _accum_output2; - CLActivationLayerKernel _activation_output; - CLActivationLayerKernel _activation_output_state; - CLPixelWiseMultiplicationKernel _pixelwise_mul_output_state2; - CLFullyConnectedLayer _fully_connected_output_state; - CLGEMM _gemm_output_state; - CLArithmeticAdditionKernel _accum_output_state; - CLActivationLayerKernel _projection_clip; - CLCopyKernel _copy_cell_state; - CLCopyKernel _copy_output; - CLWidthConcatenateLayer _concat_scratch_buffer; - CLTensor _input_gate_out1; - CLTensor _input_gate_out2; - CLTensor _input_gate_out3; - CLTensor _input_gate_out4; - CLTensor _input_gate_out5; - CLTensor _forget_gate_out1; - CLTensor _forget_gate_out2; - CLTensor _forget_gate_out3; - CLTensor _forget_gate_out4; - CLTensor _forget_gate_out5; - CLTensor _cell_state_out1; - CLTensor _cell_state_out2; - CLTensor _cell_state_out3; - CLTensor _cell_state_out4; - CLTensor _cell_state_out5; - CLTensor _output1; - CLTensor _output2; - CLTensor _output3; - CLTensor _output4; - CLTensor _output5; - CLTensor _cell_state_activation; - CLTensor _output_state1; - CLTensor _ones; - bool _run_peephole_opt; - bool _run_cifg_opt; - bool _perform_cell_clipping; - bool _has_projection_weights; - bool _perform_projection_clipping; + CLMemoryGroup _memory_group; + CLFullyConnectedLayer _fully_connected_input_gate; + CLGEMM _gemm_input_gate; + CLTransposeKernel _transpose_input_gate; + CLSaturatedArithmeticOperationKernel _accum_input_gate1; + CLArithmeticAddition _accum_input_gate2; + CLSaturatedArithmeticOperationKernel _subtract_input_gate; + CLPixelWiseMultiplicationKernel _pixelwise_mul_input_gate; + CLActivationLayerKernel _activation_input_gate; + CLFullyConnectedLayer _fully_connected_forget_gate; + CLGEMM _gemm_forget_gate; + CLTransposeKernel _transpose_forget_gate; + CLSaturatedArithmeticOperationKernel _accum_forget_gate1; + CLArithmeticAddition _accum_forget_gate2; + CLPixelWiseMultiplicationKernel _pixelwise_mul_forget_gate; + CLActivationLayerKernel _activation_forget_gate; + CLFullyConnectedLayer _fully_connected_cell_state; + CLGEMM _gemm_cell_state1; + CLGEMM _gemm_cell_state2; + CLTransposeKernel _transpose_cell_state; + CLSaturatedArithmeticOperationKernel _accum_cell_state1; + CLSaturatedArithmeticOperationKernel _accum_cell_state2; + CLPixelWiseMultiplicationKernel _pixelwise_mul_cell_state1; + CLActivationLayerKernel _activation_cell_state; + CLActivationLayerKernel _cell_clip; + CLPixelWiseMultiplicationKernel _pixelwise_mul_cell_state2; + CLFullyConnectedLayer _fully_connected_output; + CLGEMM _gemm_output; + CLPixelWiseMultiplicationKernel _pixelwise_mul_output_state1; + CLTransposeKernel _transpose_output; + CLSaturatedArithmeticOperationKernel _accum_output1; + CLArithmeticAddition _accum_output2; + CLActivationLayerKernel _activation_output; + CLActivationLayerKernel _activation_output_state; + CLPixelWiseMultiplicationKernel _pixelwise_mul_output_state2; + CLFullyConnectedLayer _fully_connected_output_state; + CLGEMM _gemm_output_state; + CLSaturatedArithmeticOperationKernel _accum_output_state; + CLActivationLayerKernel _projection_clip; + CLCopyKernel _copy_cell_state; + CLCopyKernel _copy_output; + CLWidthConcatenateLayer _concat_scratch_buffer; + CLTensor _input_gate_out1; + CLTensor _input_gate_out2; + CLTensor _input_gate_out3; + CLTensor _input_gate_out4; + CLTensor _input_gate_out5; + CLTensor _forget_gate_out1; + CLTensor _forget_gate_out2; + CLTensor _forget_gate_out3; + CLTensor _forget_gate_out4; + CLTensor _forget_gate_out5; + CLTensor _cell_state_out1; + CLTensor _cell_state_out2; + CLTensor _cell_state_out3; + CLTensor _cell_state_out4; + CLTensor _cell_state_out5; + CLTensor _output1; + CLTensor _output2; + CLTensor _output3; + CLTensor _output4; + CLTensor _output5; + CLTensor _cell_state_activation; + CLTensor _output_state1; + CLTensor _ones; + bool _run_peephole_opt; + bool _run_cifg_opt; + bool _perform_cell_clipping; + bool _has_projection_weights; + bool _perform_projection_clipping; }; } #endif /* __ARM_COMPUTE_CLLSTMLAYER_H__ */ diff --git a/arm_compute/runtime/CL/functions/CLLaplacianPyramid.h b/arm_compute/runtime/CL/functions/CLLaplacianPyramid.h index 585a013e31..ae86e931df 100644 --- a/arm_compute/runtime/CL/functions/CLLaplacianPyramid.h +++ b/arm_compute/runtime/CL/functions/CLLaplacianPyramid.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 ARM Limited. + * Copyright (c) 2017-2018 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -26,8 +26,8 @@ #include "arm_compute/core/Types.h" #include "arm_compute/runtime/CL/CLPyramid.h" -#include "arm_compute/runtime/CL/functions/CLArithmeticSubtraction.h" #include "arm_compute/runtime/CL/functions/CLDepthConvertLayer.h" +#include "arm_compute/runtime/CL/functions/CLElementwiseOperations.h" #include "arm_compute/runtime/CL/functions/CLGaussian5x5.h" #include "arm_compute/runtime/CL/functions/CLGaussianPyramid.h" #include "arm_compute/runtime/IFunction.h" diff --git a/arm_compute/runtime/CL/functions/CLLaplacianReconstruct.h b/arm_compute/runtime/CL/functions/CLLaplacianReconstruct.h index 6905b03652..622b049f11 100644 --- a/arm_compute/runtime/CL/functions/CLLaplacianReconstruct.h +++ b/arm_compute/runtime/CL/functions/CLLaplacianReconstruct.h @@ -26,8 +26,8 @@ #include "arm_compute/core/Types.h" #include "arm_compute/runtime/CL/CLPyramid.h" -#include "arm_compute/runtime/CL/functions/CLArithmeticAddition.h" #include "arm_compute/runtime/CL/functions/CLDepthConvertLayer.h" +#include "arm_compute/runtime/CL/functions/CLElementwiseOperations.h" #include "arm_compute/runtime/CL/functions/CLScale.h" #include "arm_compute/runtime/IFunction.h" diff --git a/arm_compute/runtime/CL/functions/CLRNNLayer.h b/arm_compute/runtime/CL/functions/CLRNNLayer.h index ab7407dbfc..fc86992bdf 100644 --- a/arm_compute/runtime/CL/functions/CLRNNLayer.h +++ b/arm_compute/runtime/CL/functions/CLRNNLayer.h @@ -25,8 +25,8 @@ #define __ARM_COMPUTE_CLRNN_LAYER_H__ #include "arm_compute/core/CL/kernels/CLActivationLayerKernel.h" -#include "arm_compute/core/CL/kernels/CLArithmeticAdditionKernel.h" #include "arm_compute/core/CL/kernels/CLCopyKernel.h" +#include "arm_compute/core/CL/kernels/CLElementwiseOperationKernel.h" #include "arm_compute/runtime/CL/ICLSimpleFunction.h" #include "arm_compute/runtime/CL/functions/CLFullyConnectedLayer.h" #include "arm_compute/runtime/CL/functions/CLGEMM.h" @@ -72,16 +72,16 @@ public: void prepare() override; private: - CLMemoryGroup _memory_group; - CLGEMM _gemm_state_f; - CLArithmeticAdditionKernel _add_kernel; - CLActivationLayerKernel _activation_kernel; - CLFullyConnectedLayer _fully_connected_kernel; - CLCopyKernel _copy_kernel; - CLTensor _fully_connected_out; - CLTensor _gemm_output; - CLTensor _add_output; - bool _is_prepared; + CLMemoryGroup _memory_group; + CLGEMM _gemm_state_f; + CLSaturatedArithmeticOperationKernel _add_kernel; + CLActivationLayerKernel _activation_kernel; + CLFullyConnectedLayer _fully_connected_kernel; + CLCopyKernel _copy_kernel; + CLTensor _fully_connected_out; + CLTensor _gemm_output; + CLTensor _add_output; + bool _is_prepared; }; } #endif /* __ARM_COMPUTE_CLRNN_LAYER_H__ */ diff --git a/arm_compute/runtime/CL/functions/CLReduceMean.h b/arm_compute/runtime/CL/functions/CLReduceMean.h index 5a919e5dcd..ba10134a00 100644 --- a/arm_compute/runtime/CL/functions/CLReduceMean.h +++ b/arm_compute/runtime/CL/functions/CLReduceMean.h @@ -25,7 +25,7 @@ #define __ARM_COMPUTE_CL_REDUCE_MEAN_H__ #include "arm_compute/runtime/CL/ICLSimpleFunction.h" -#include "arm_compute/runtime/CL/functions/CLArithmeticDivision.h" +#include "arm_compute/runtime/CL/functions/CLElementwiseOperations.h" #include "arm_compute/runtime/CL/functions/CLReductionOperation.h" #include "arm_compute/runtime/CL/functions/CLReshapeLayer.h" #include "arm_compute/runtime/IMemoryManager.h" diff --git a/src/core/CL/CLKernelLibrary.cpp b/src/core/CL/CLKernelLibrary.cpp index f2b5d45e2c..ac1d4b349e 100644 --- a/src/core/CL/CLKernelLibrary.cpp +++ b/src/core/CL/CLKernelLibrary.cpp @@ -149,11 +149,6 @@ const std::map CLKernelLibrary::_kernel_program_map = { "accumulate_weighted", "accumulate.cl" }, { "activation_layer", "activation_layer.cl" }, { "activation_layer_qa8", "activation_layer_qa8.cl" }, - { "arithmetic_add_quantized", "arithmetic_op_quantized.cl" }, - { "arithmetic_add", "arithmetic_op.cl" }, - { "arithmetic_sub", "arithmetic_op.cl" }, - { "arithmetic_sub_quantized", "arithmetic_op_quantized.cl" }, - { "arithmetic_div", "arithmetic_op.cl" }, { "batch_to_space_nchw", "batch_to_space.cl" }, { "batch_to_space_static_nchw", "batch_to_space.cl" }, { "batch_to_space_nhwc", "batch_to_space.cl" }, @@ -246,6 +241,18 @@ const std::map CLKernelLibrary::_kernel_program_map = { "direct_convolution5x5_nhwc", "direct_convolution5x5.cl" }, { "direct_convolution5x5_f32_bifrost", "direct_convolution5x5.cl" }, { "direct_convolution_1x1_3x3_5x5_quantized", "direct_convolution_1x1_3x3_5x5_quantized.cl" }, + { "elementwise_operation_ADD", "elementwise_operation.cl" }, + { "elementwise_operation_SUB", "elementwise_operation.cl" }, + { "elementwise_operation_MAX", "elementwise_operation.cl" }, + { "elementwise_operation_MIN", "elementwise_operation.cl" }, + { "elementwise_operation_DIV", "elementwise_operation.cl" }, + { "elementwise_operation_SQUARED_DIFF", "elementwise_operation.cl" }, + { "elementwise_operation_ADD_quantized", "elementwise_operation_quantized.cl" }, + { "elementwise_operation_SUB_quantized", "elementwise_operation_quantized.cl" }, + { "elementwise_operation_MAX_quantized", "elementwise_operation_quantized.cl" }, + { "elementwise_operation_MIN_quantized", "elementwise_operation_quantized.cl" }, + { "elementwise_operation_DIV_quantized", "elementwise_operation_quantized.cl" }, + { "elementwise_operation_SQUARED_DIFF_quantized", "elementwise_operation_quantized.cl" }, { "erode", "erode.cl" }, { "fast_corners", "fast_corners.cl" }, { "flatten", "flatten.cl" }, @@ -508,14 +515,6 @@ const std::map CLKernelLibrary::_program_source_map = { "activation_layer_qa8.cl", #include "./cl_kernels/activation_layer_qa8.clembed" - }, - { - "arithmetic_op.cl", -#include "./cl_kernels/arithmetic_op.clembed" - }, - { - "arithmetic_op_quantized.cl", -#include "./cl_kernels/arithmetic_op_quantized.clembed" }, { "batch_to_space.cl", @@ -640,6 +639,14 @@ const std::map CLKernelLibrary::_program_source_map = { "direct_convolution_1x1_3x3_5x5_quantized.cl", #include "./cl_kernels/direct_convolution_1x1_3x3_5x5_quantized.clembed" + }, + { + "elementwise_operation.cl", +#include "./cl_kernels/elementwise_operation.clembed" + }, + { + "elementwise_operation_quantized.cl", +#include "./cl_kernels/elementwise_operation_quantized.clembed" }, { "erode.cl", diff --git a/src/core/CL/cl_kernels/arithmetic_op.cl b/src/core/CL/cl_kernels/arithmetic_op.cl deleted file mode 100644 index 557615e7f2..0000000000 --- a/src/core/CL/cl_kernels/arithmetic_op.cl +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2016-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "helpers.h" - -#ifdef SATURATE -#define ADD(x, y) add_sat((x), (y)) -#define SUB(x, y) sub_sat((x), (y)) -#else /* SATURATE */ -#define ADD(x, y) (x) + (y) -#define SUB(x, y) (x) - (y) -#endif /* SATURATE */ - -#define DIV(x, y) (x) / (y) - -#if defined(DATA_TYPE_IN1) && defined(DATA_TYPE_IN2) && defined(DATA_TYPE_OUT) && defined(VEC_SIZE) -/** This function adds two tensors. - * - * @attention The input and output data_types need to be passed at compile time using -DDATA_TYPE_IN1, -DDATA_TYPE_IN2 and -DDATA_TYPE_OUT: - * e.g. -DDATA_TYPE_IN1=uchar -DDATA_TYPE_IN2=uchar -DDATA_TYPE_OUT=short - * @attention To perform saturating operation -DSATURATE has to be passed to the compiler otherwise wrapping policy will be used. - * @attention Vector size should be given as a preprocessor argument using -DVEC_SIZE=size. e.g. -DVEC_SIZE=16 - * - * @param[in] in1_ptr Pointer to the source tensor. Supported data types: U8/S16/F16/F32 - * @param[in] in1_stride_x Stride of the source tensor in X dimension (in bytes) - * @param[in] in1_step_x in1_stride_x * number of elements along X processed per workitem(in bytes) - * @param[in] in1_stride_y Stride of the source tensor in Y dimension (in bytes) - * @param[in] in1_step_y in1_stride_y * number of elements along Y processed per workitem(in bytes) - * @param[in] in1_stride_z Stride of the source tensor in Z dimension (in bytes) - * @param[in] in1_step_z in1_stride_z * number of elements along Z processed per workitem(in bytes) - * @param[in] in1_offset_first_element_in_bytes The offset of the first element in the source tensor - * @param[in] in2_ptr Pointer to the source tensor. Supported data types: U8/S16/F16/F32 - * @param[in] in2_stride_x Stride of the source tensor in X dimension (in bytes) - * @param[in] in2_step_x in2_stride_x * number of elements along X processed per workitem(in bytes) - * @param[in] in2_stride_y Stride of the source tensor in Y dimension (in bytes) - * @param[in] in2_step_y in2_stride_y * number of elements along Y processed per workitem(in bytes) - * @param[in] in2_stride_z Stride of the source tensor in Z dimension (in bytes) - * @param[in] in2_step_z in2_stride_z * number of elements along Z processed per workitem(in bytes) - * @param[in] in2_offset_first_element_in_bytes The offset of the first element in the source tensor - * @param[out] out_ptr Pointer to the destination tensor. Supported data types: U8 (only if both inputs are U8), S16/F16/F32 - * @param[in] out_stride_x Stride of the destination tensor in X dimension (in bytes) - * @param[in] out_step_x out_stride_x * number of elements along X processed per workitem(in bytes) - * @param[in] out_stride_y Stride of the destination tensor in Y dimension (in bytes) - * @param[in] out_step_y out_stride_y * number of elements along Y processed per workitem(in bytes) - * @param[in] out_stride_z Stride of the source tensor in Z dimension (in bytes) - * @param[in] out_step_z out_stride_z * number of elements along Z processed per workitem(in bytes) - * @param[in] out_offset_first_element_in_bytes The offset of the first element in the destination tensor - */ -__kernel void arithmetic_add( - TENSOR3D_DECLARATION(in1), - TENSOR3D_DECLARATION(in2), - TENSOR3D_DECLARATION(out)) -{ - // Get pixels pointer - Tensor3D in1 = CONVERT_TO_TENSOR3D_STRUCT(in1); - Tensor3D in2 = CONVERT_TO_TENSOR3D_STRUCT(in2); - Tensor3D out = CONVERT_TO_TENSOR3D_STRUCT(out); - - // Load values - VEC_DATA_TYPE(DATA_TYPE_OUT, VEC_SIZE) - in_a = CONVERT(VLOAD(VEC_SIZE)(0, (__global DATA_TYPE_IN1 *)in1.ptr), VEC_DATA_TYPE(DATA_TYPE_OUT, VEC_SIZE)); - VEC_DATA_TYPE(DATA_TYPE_OUT, VEC_SIZE) - in_b = CONVERT(VLOAD(VEC_SIZE)(0, (__global DATA_TYPE_IN2 *)in2.ptr), VEC_DATA_TYPE(DATA_TYPE_OUT, VEC_SIZE)); - - // Calculate and store result - VSTORE(VEC_SIZE) - (ADD(in_a, in_b), 0, (__global DATA_TYPE_OUT *)out.ptr); -} -#endif /* defined(DATA_TYPE_IN1) && defined(DATA_TYPE_IN2) && defined(DATA_TYPE_OUT) && defined(VEC_SIZE) */ - -/** This function subtracts one tensor from another. - * - * @attention The input and output data_types need to be passed at compile time using -DDATA_TYPE_IN1, -DDATA_TYPE_IN2 and -DDATA_TYPE_OUT: - * e.g. -DDATA_TYPE_IN1=uchar -DDATA_TYPE_IN2=uchar -DDATA_TYPE_OUT=short - * @attention To perform saturating operation -DSATURATE has to be passed to the compiler otherwise wrapping policy will be used. - * - * @param[in] in1_ptr Pointer to the source tensor. Supported data types: U8, S16 - * @param[in] in1_stride_x Stride of the source tensor in X dimension (in bytes) - * @param[in] in1_step_x in1_stride_x * number of elements along X processed per workitem(in bytes) - * @param[in] in1_stride_y Stride of the source tensor in Y dimension (in bytes) - * @param[in] in1_step_y in1_stride_y * number of elements along Y processed per workitem(in bytes) - * @param[in] in1_stride_z Stride of the source tensor in Z dimension (in bytes) - * @param[in] in1_step_z in1_stride_z * number of elements along Z processed per workitem(in bytes) - * @param[in] in1_offset_first_element_in_bytes The offset of the first element in the source tensor - * @param[in] in2_ptr Pointer to the source tensor. Supported data types: U8, S16 - * @param[in] in2_stride_x Stride of the source tensor in X dimension (in bytes) - * @param[in] in2_step_x in2_stride_x * number of elements along X processed per workitem(in bytes) - * @param[in] in2_stride_y Stride of the source tensor in Y dimension (in bytes) - * @param[in] in2_step_y in2_stride_y * number of elements along Y processed per workitem(in bytes) - * @param[in] in2_stride_z Stride of the source tensor in Z dimension (in bytes) - * @param[in] in2_step_z in2_stride_z * number of elements along Z processed per workitem(in bytes) - * @param[in] in2_offset_first_element_in_bytes The offset of the first element in the source tensor - * @param[out] out_ptr Pointer to the destination tensor. Supported data types: U8, S16 - * @param[in] out_stride_x Stride of the destination tensor in X dimension (in bytes) - * @param[in] out_step_x out_stride_x * number of elements along X processed per workitem(in bytes) - * @param[in] out_stride_y Stride of the destination tensor in Y dimension (in bytes) - * @param[in] out_step_y out_stride_y * number of elements along Y processed per workitem(in bytes) - * @param[in] out_stride_z Stride of the source tensor in Z dimension (in bytes) - * @param[in] out_step_z out_stride_z * number of elements along Z processed per workitem(in bytes) - * @param[in] out_offset_first_element_in_bytes The offset of the first element in the destination tensor - */ -__kernel void arithmetic_sub( - TENSOR3D_DECLARATION(in1), - TENSOR3D_DECLARATION(in2), - TENSOR3D_DECLARATION(out)) -{ - // Get pixels pointer - Tensor3D in1 = CONVERT_TO_TENSOR3D_STRUCT(in1); - Tensor3D in2 = CONVERT_TO_TENSOR3D_STRUCT(in2); - Tensor3D out = CONVERT_TO_TENSOR3D_STRUCT(out); - - // Load values - VEC_DATA_TYPE(DATA_TYPE_OUT, 16) - in_a = CONVERT(vload16(0, (__global DATA_TYPE_IN1 *)in1.ptr), VEC_DATA_TYPE(DATA_TYPE_OUT, 16)); - VEC_DATA_TYPE(DATA_TYPE_OUT, 16) - in_b = CONVERT(vload16(0, (__global DATA_TYPE_IN2 *)in2.ptr), VEC_DATA_TYPE(DATA_TYPE_OUT, 16)); - - // Calculate and store result - vstore16(SUB(in_a, in_b), 0, (__global DATA_TYPE_OUT *)out.ptr); -} - -/** This function divides one tensor from another. - * - * @attention The input and output data_types need to be passed at compile time using -DDATA_TYPE_IN1, -DDATA_TYPE_IN2 and -DDATA_TYPE_OUT: - * e.g. -DDATA_TYPE_IN1=float -DDATA_TYPE_IN2=float -DDATA_TYPE_OUT=float - * - * @param[in] in1_ptr Pointer to the source tensor. Supported data types: F16/F32 - * @param[in] in1_stride_x Stride of the source tensor in X dimension (in bytes) - * @param[in] in1_step_x in1_stride_x * number of elements along X processed per workitem(in bytes) - * @param[in] in1_stride_y Stride of the source tensor in Y dimension (in bytes) - * @param[in] in1_step_y in1_stride_y * number of elements along Y processed per workitem(in bytes) - * @param[in] in1_stride_z Stride of the source tensor in Z dimension (in bytes) - * @param[in] in1_step_z in1_stride_z * number of elements along Z processed per workitem(in bytes) - * @param[in] in1_offset_first_element_in_bytes The offset of the first element in the source tensor - * @param[in] in2_ptr Pointer to the source tensor. Supported data types: Same as @p in1_ptr - * @param[in] in2_stride_x Stride of the source tensor in X dimension (in bytes) - * @param[in] in2_step_x in2_stride_x * number of elements along X processed per workitem(in bytes) - * @param[in] in2_stride_y Stride of the source tensor in Y dimension (in bytes) - * @param[in] in2_step_y in2_stride_y * number of elements along Y processed per workitem(in bytes) - * @param[in] in2_stride_z Stride of the source tensor in Z dimension (in bytes) - * @param[in] in2_step_z in2_stride_z * number of elements along Z processed per workitem(in bytes) - * @param[in] in2_offset_first_element_in_bytes The offset of the first element in the source tensor - * @param[out] out_ptr Pointer to the destination tensor. Supported data types: Same as @p in1_ptr - * @param[in] out_stride_x Stride of the destination tensor in X dimension (in bytes) - * @param[in] out_step_x out_stride_x * number of elements along X processed per workitem(in bytes) - * @param[in] out_stride_y Stride of the destination tensor in Y dimension (in bytes) - * @param[in] out_step_y out_stride_y * number of elements along Y processed per workitem(in bytes) - * @param[in] out_stride_z Stride of the source tensor in Z dimension (in bytes) - * @param[in] out_step_z out_stride_z * number of elements along Z processed per workitem(in bytes) - * @param[in] out_offset_first_element_in_bytes The offset of the first element in the destination tensor - */ -__kernel void arithmetic_div( - TENSOR3D_DECLARATION(in1), - TENSOR3D_DECLARATION(in2), - TENSOR3D_DECLARATION(out)) -{ - // Get pixels pointer - Tensor3D in1 = CONVERT_TO_TENSOR3D_STRUCT(in1); - Tensor3D in2 = CONVERT_TO_TENSOR3D_STRUCT(in2); - Tensor3D out = CONVERT_TO_TENSOR3D_STRUCT(out); - - // Load values - VEC_DATA_TYPE(DATA_TYPE_OUT, 16) - in_a = CONVERT(vload16(0, (__global DATA_TYPE_IN1 *)in1.ptr), VEC_DATA_TYPE(DATA_TYPE_OUT, 16)); - VEC_DATA_TYPE(DATA_TYPE_OUT, 16) - in_b = CONVERT(vload16(0, (__global DATA_TYPE_IN2 *)in2.ptr), VEC_DATA_TYPE(DATA_TYPE_OUT, 16)); - - // Calculate and store result - vstore16(DIV(in_a, in_b), 0, (__global DATA_TYPE_OUT *)out.ptr); -} diff --git a/src/core/CL/cl_kernels/arithmetic_op_quantized.cl b/src/core/CL/cl_kernels/arithmetic_op_quantized.cl deleted file mode 100644 index fc7fa771f3..0000000000 --- a/src/core/CL/cl_kernels/arithmetic_op_quantized.cl +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2016-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "helpers.h" - -#ifdef SATURATE -#define ADD(x, y) add_sat((x), (y)) -#define SUB(x, y) sub_sat((x), (y)) -#else /* SATURATE */ -#define ADD(x, y) (x) + (y) -#define SUB(x, y) (x) - (y) -#endif /* SATURATE */ - -#define CONVERT_RTE(x, type) (convert_##type##_rte((x))) -#define CONVERT_DOWN(x, type) CONVERT_RTE(x, type) - -#if defined(OFFSET_IN1) && defined(OFFSET_IN2) && defined(OFFSET_OUT) && defined(SCALE_IN1) && defined(SCALE_IN2) && defined(SCALE_OUT) - -#if defined(VEC_SIZE) - -#define VEC_FLOAT VEC_DATA_TYPE(float, VEC_SIZE) -#define VEC_INT VEC_DATA_TYPE(int, VEC_SIZE) -#define VEC_UCHAR VEC_DATA_TYPE(uchar, VEC_SIZE) - -/** This function adds two tensors. - * - * @note The quantization offset of the first operand must be passed at compile time using -DOFFSET_IN1, i.e. -DOFFSET_IN1=10 - * @note The quantization offset of the second operand must be passed at compile time using -DOFFSET_IN2, i.e. -DOFFSET_IN2=10 - * @note The quantization offset of the output must be passed at compile time using -DOFFSET_OUT, i.e. -DOFFSET_OUT=10 - * @note The quantization scale of the first operand must be passed at compile time using -DSCALE_IN1, i.e. -DSCALE_IN1=10 - * @note The quantization scale of the second operand must be passed at compile time using -DSCALE_IN2, i.e. -DSCALE_IN2=10 - * @note The quantization scale of the output must be passed at compile time using -DSCALE_OUT, i.e. -DSCALE_OUT=10 - * @note To perform saturating operation -DSATURATE has to be passed to the compiler otherwise wrapping policy will be used. - * @note Vector size should be given as a preprocessor argument using -DVEC_SIZE=size. e.g. -DVEC_SIZE=16 - * - * @param[in] in1_ptr Pointer to the source tensor. Supported data types: QASYMM8 - * @param[in] in1_stride_x Stride of the source tensor in X dimension (in bytes) - * @param[in] in1_step_x in1_stride_x * number of elements along X processed per workitem(in bytes) - * @param[in] in1_stride_y Stride of the source tensor in Y dimension (in bytes) - * @param[in] in1_step_y in1_stride_y * number of elements along Y processed per workitem(in bytes) - * @param[in] in1_stride_z Stride of the source tensor in Z dimension (in bytes) - * @param[in] in1_step_z in1_stride_z * number of elements along Z processed per workitem(in bytes) - * @param[in] in1_offset_first_element_in_bytes The offset of the first element in the source tensor - * @param[in] in2_ptr Pointer to the source tensor. Supported data types: same as @p in1_ptr - * @param[in] in2_stride_x Stride of the source tensor in X dimension (in bytes) - * @param[in] in2_step_x in2_stride_x * number of elements along X processed per workitem(in bytes) - * @param[in] in2_stride_y Stride of the source tensor in Y dimension (in bytes) - * @param[in] in2_step_y in2_stride_y * number of elements along Y processed per workitem(in bytes) - * @param[in] in2_stride_z Stride of the source tensor in Z dimension (in bytes) - * @param[in] in2_step_z in2_stride_z * number of elements along Z processed per workitem(in bytes) - * @param[in] in2_offset_first_element_in_bytes The offset of the first element in the source tensor - * @param[out] out_ptr Pointer to the destination tensor. Supported data types: same as @p in1_ptr - * @param[in] out_stride_x Stride of the destination tensor in X dimension (in bytes) - * @param[in] out_step_x out_stride_x * number of elements along X processed per workitem(in bytes) - * @param[in] out_stride_y Stride of the destination tensor in Y dimension (in bytes) - * @param[in] out_step_y out_stride_y * number of elements along Y processed per workitem(in bytes) - * @param[in] out_stride_z Stride of the source tensor in Z dimension (in bytes) - * @param[in] out_step_z out_stride_z * number of elements along Z processed per workitem(in bytes) - * @param[in] out_offset_first_element_in_bytes The offset of the first element in the destination tensor - */ -__kernel void arithmetic_add_quantized( - TENSOR3D_DECLARATION(in1), - TENSOR3D_DECLARATION(in2), - TENSOR3D_DECLARATION(out)) -{ - // Get pixels pointer - Tensor3D in1 = CONVERT_TO_TENSOR3D_STRUCT(in1); - Tensor3D in2 = CONVERT_TO_TENSOR3D_STRUCT(in2); - Tensor3D out = CONVERT_TO_TENSOR3D_STRUCT(out); - - VEC_INT in_a = CONVERT(VLOAD(VEC_SIZE)(0, (__global uchar *)in1.ptr), VEC_INT); - VEC_INT in_b = CONVERT(VLOAD(VEC_SIZE)(0, (__global uchar *)in2.ptr), VEC_INT); - - in_a = SUB(in_a, (VEC_INT)((int)OFFSET_IN1)); - in_b = SUB(in_b, (VEC_INT)((int)OFFSET_IN2)); - - const VEC_FLOAT in1f32 = CONVERT(in_a, VEC_FLOAT) * (VEC_FLOAT)((float)SCALE_IN1); - const VEC_FLOAT in2f32 = CONVERT(in_b, VEC_FLOAT) * (VEC_FLOAT)((float)SCALE_IN2); - - const VEC_FLOAT qresf32 = (in1f32 + in2f32) / ((VEC_FLOAT)(float)SCALE_OUT) + ((VEC_FLOAT)((float)OFFSET_OUT)); - const VEC_UCHAR res = CONVERT_SAT(CONVERT_DOWN(qresf32, VEC_INT), VEC_UCHAR); - - // Store result - VSTORE(VEC_SIZE) - (res, 0, (__global uchar *)out.ptr); -} -#endif /* defined(VEC_SIZE) */ - -/** This function subtracts two tensors. - * - * @note The quantization offset of the first operand must be passed at compile time using -DOFFSET_IN1, i.e. -DOFFSET_IN1=10 - * @note The quantization offset of the second operand must be passed at compile time using -DOFFSET_IN2, i.e. -DOFFSET_IN2=10 - * @note The quantization offset of the output must be passed at compile time using -DOFFSET_OUT, i.e. -DOFFSET_OUT=10 - * @note The quantization scale of the first operand must be passed at compile time using -DSCALE_IN1, i.e. -DSCALE_IN1=10 - * @note The quantization scale of the second operand must be passed at compile time using -DSCALE_IN2, i.e. -DSCALE_IN2=10 - * @note The quantization scale of the output must be passed at compile time using -DSCALE_OUT, i.e. -DSCALE_OUT=10 - * @note To perform saturating operation -DSATURATE has to be passed to the compiler otherwise wrapping policy will be used. - * - * @param[in] in1_ptr Pointer to the source tensor. Supported data types: QASYMM8 - * @param[in] in1_stride_x Stride of the source tensor in X dimension (in bytes) - * @param[in] in1_step_x in1_stride_x * number of elements along X processed per workitem(in bytes) - * @param[in] in1_stride_y Stride of the source tensor in Y dimension (in bytes) - * @param[in] in1_step_y in1_stride_y * number of elements along Y processed per workitem(in bytes) - * @param[in] in1_stride_z Stride of the source tensor in Z dimension (in bytes) - * @param[in] in1_step_z in1_stride_z * number of elements along Z processed per workitem(in bytes) - * @param[in] in1_offset_first_element_in_bytes The offset of the first element in the source tensor - * @param[in] in2_ptr Pointer to the source tensor. Supported data types: same as @p in1_ptr - * @param[in] in2_stride_x Stride of the source tensor in X dimension (in bytes) - * @param[in] in2_step_x in2_stride_x * number of elements along X processed per workitem(in bytes) - * @param[in] in2_stride_y Stride of the source tensor in Y dimension (in bytes) - * @param[in] in2_step_y in2_stride_y * number of elements along Y processed per workitem(in bytes) - * @param[in] in2_stride_z Stride of the source tensor in Z dimension (in bytes) - * @param[in] in2_step_z in2_stride_z * number of elements along Z processed per workitem(in bytes) - * @param[in] in2_offset_first_element_in_bytes The offset of the first element in the source tensor - * @param[out] out_ptr Pointer to the destination tensor. Supported data types: same as @p in1_ptr - * @param[in] out_stride_x Stride of the destination tensor in X dimension (in bytes) - * @param[in] out_step_x out_stride_x * number of elements along X processed per workitem(in bytes) - * @param[in] out_stride_y Stride of the destination tensor in Y dimension (in bytes) - * @param[in] out_step_y out_stride_y * number of elements along Y processed per workitem(in bytes) - * @param[in] out_stride_z Stride of the source tensor in Z dimension (in bytes) - * @param[in] out_step_z out_stride_z * number of elements along Z processed per workitem(in bytes) - * @param[in] out_offset_first_element_in_bytes The offset of the first element in the destination tensor - */ -__kernel void arithmetic_sub_quantized( - TENSOR3D_DECLARATION(in1), - TENSOR3D_DECLARATION(in2), - TENSOR3D_DECLARATION(out)) -{ - // Get pixels pointer - Tensor3D in1 = CONVERT_TO_TENSOR3D_STRUCT(in1); - Tensor3D in2 = CONVERT_TO_TENSOR3D_STRUCT(in2); - Tensor3D out = CONVERT_TO_TENSOR3D_STRUCT(out); - - int16 in_a = CONVERT(vload16(0, (__global uchar *)in1.ptr), int16); - int16 in_b = CONVERT(vload16(0, (__global uchar *)in2.ptr), int16); - - in_a = SUB(in_a, (int16)((int)OFFSET_IN1)); - in_b = SUB(in_b, (int16)((int)OFFSET_IN2)); - - const float16 in1f32 = convert_float16(in_a) * (float16)((float)SCALE_IN1); - const float16 in2f32 = convert_float16(in_b) * (float16)((float)SCALE_IN2); - const float16 qresf32 = (in1f32 - in2f32) / ((float16)(float)SCALE_OUT) + ((float16)((float16)OFFSET_OUT)); - const uchar16 res = convert_uchar16_sat(convert_int16_rte(qresf32)); - - // Store result - vstore16(res, 0, (__global uchar *)out.ptr); -} -#endif /* defined(OFFSET_IN1) && defined(OFFSET_IN2) && defined(OFFSET_OUT) && defined(SCALE_IN1) && defined(SCALE_IN2) && defined(SCALE_OUT) */ diff --git a/src/core/CL/cl_kernels/elementwise_operation.cl b/src/core/CL/cl_kernels/elementwise_operation.cl new file mode 100644 index 0000000000..00d7ed3ba1 --- /dev/null +++ b/src/core/CL/cl_kernels/elementwise_operation.cl @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "helpers.h" + +/** List of all the operations supported by this kernel. + * @note ADD and SUB operations, when executed on integers, support saturation */ +#ifdef SATURATE +#define ADD(x, y) add_sat((x), (y)) +#define SUB(x, y) sub_sat((x), (y)) +#else /* SATURATE */ +#define ADD(x, y) (x) + (y) +#define SUB(x, y) (x) - (y) +#endif /* SATURATE */ + +#define MAX(x, y) max(x, y) +#define MIN(x, y) min(x, y) +#define SQUARED_DIFF(x, y) (x - y) * (x - y) +#define DIV(x, y) (x / y) + +#define OP_FUN_NAME_STR(op) elementwise_operation_##op +#define OP_FUN_NAME(op) OP_FUN_NAME_STR(op) + +#if defined(OP) && defined(DATA_TYPE_IN1) && defined(DATA_TYPE_IN2) && defined(DATA_TYPE_OUT) && defined(VEC_SIZE) +/** This function executes an element-wise operation among two tensors. + * + * @attention The input and output data_types need to be passed at compile time using -DDATA_TYPE_IN1, -DDATA_TYPE_IN2 and -DDATA_TYPE_OUT: + * e.g. -DDATA_TYPE_IN1=uchar -DDATA_TYPE_IN2=uchar -DDATA_TYPE_OUT=short + * @attention To perform saturating operation -DSATURATE has to be passed to the compiler otherwise wrapping policy will be used. + * @attention Vector size should be given as a preprocessor argument using -DVEC_SIZE=size. e.g. -DVEC_SIZE=16 + * @attention The element-wise operation to be executed has to be passed at compile time using -DOP (e.g., -DOP=ADD) + * + * @param[in] in1_ptr Pointer to the source tensor. Supported data types: U8/S16/F16/F32 + * @param[in] in1_stride_x Stride of the source tensor in X dimension (in bytes) + * @param[in] in1_step_x in1_stride_x * number of elements along X processed per workitem(in bytes) + * @param[in] in1_stride_y Stride of the source tensor in Y dimension (in bytes) + * @param[in] in1_step_y in1_stride_y * number of elements along Y processed per workitem(in bytes) + * @param[in] in1_stride_z Stride of the source tensor in Z dimension (in bytes) + * @param[in] in1_step_z in1_stride_z * number of elements along Z processed per workitem(in bytes) + * @param[in] in1_offset_first_element_in_bytes The offset of the first element in the source tensor + * @param[in] in2_ptr Pointer to the source tensor. Supported data types: U8/S16/F16/F32 + * @param[in] in2_stride_x Stride of the source tensor in X dimension (in bytes) + * @param[in] in2_step_x in2_stride_x * number of elements along X processed per workitem(in bytes) + * @param[in] in2_stride_y Stride of the source tensor in Y dimension (in bytes) + * @param[in] in2_step_y in2_stride_y * number of elements along Y processed per workitem(in bytes) + * @param[in] in2_stride_z Stride of the source tensor in Z dimension (in bytes) + * @param[in] in2_step_z in2_stride_z * number of elements along Z processed per workitem(in bytes) + * @param[in] in2_offset_first_element_in_bytes The offset of the first element in the source tensor + * @param[out] out_ptr Pointer to the destination tensor. Supported data types: U8 (only if both inputs are U8), S16/F16/F32 + * @param[in] out_stride_x Stride of the destination tensor in X dimension (in bytes) + * @param[in] out_step_x out_stride_x * number of elements along X processed per workitem(in bytes) + * @param[in] out_stride_y Stride of the destination tensor in Y dimension (in bytes) + * @param[in] out_step_y out_stride_y * number of elements along Y processed per workitem(in bytes) + * @param[in] out_stride_z Stride of the source tensor in Z dimension (in bytes) + * @param[in] out_step_z out_stride_z * number of elements along Z processed per workitem(in bytes) + * @param[in] out_offset_first_element_in_bytes The offset of the first element in the destination tensor + */ +__kernel void OP_FUN_NAME(OP)( + TENSOR3D_DECLARATION(in1), + TENSOR3D_DECLARATION(in2), + TENSOR3D_DECLARATION(out)) +{ + // Get pixels pointer + Tensor3D in1 = CONVERT_TO_TENSOR3D_STRUCT(in1); + Tensor3D in2 = CONVERT_TO_TENSOR3D_STRUCT(in2); + Tensor3D out = CONVERT_TO_TENSOR3D_STRUCT(out); + + // Load values + VEC_DATA_TYPE(DATA_TYPE_OUT, VEC_SIZE) + in_a = CONVERT(VLOAD(VEC_SIZE)(0, (__global DATA_TYPE_IN1 *)in1.ptr), VEC_DATA_TYPE(DATA_TYPE_OUT, VEC_SIZE)); + VEC_DATA_TYPE(DATA_TYPE_OUT, VEC_SIZE) + in_b = CONVERT(VLOAD(VEC_SIZE)(0, (__global DATA_TYPE_IN2 *)in2.ptr), VEC_DATA_TYPE(DATA_TYPE_OUT, VEC_SIZE)); + + // Calculate and store result + VSTORE(VEC_SIZE) + (OP(in_a, in_b), 0, (__global DATA_TYPE_OUT *)out.ptr); +} +#endif /* defined(DATA_TYPE_IN1) && defined(DATA_TYPE_IN2) && defined(DATA_TYPE_OUT) && defined(VEC_SIZE) */ diff --git a/src/core/CL/cl_kernels/elementwise_operation_quantized.cl b/src/core/CL/cl_kernels/elementwise_operation_quantized.cl new file mode 100644 index 0000000000..1f0533be13 --- /dev/null +++ b/src/core/CL/cl_kernels/elementwise_operation_quantized.cl @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "helpers.h" + +#define SUB(x, y) (x - y) +#define ADD(x, y) (x + y) +#define MAX(x, y) max((x), (y)) +#define MIN(x, y) min((x), (y)) +#define SQUARED_DIFF(x, y) (x - y) * (x - y) +#define DIV(x, y) (x / y) + +#define CONVERT_RTE(x, type) (convert_##type##_rte((x))) +#define CONVERT_DOWN(x, type) CONVERT_RTE(x, type) + +#define OP_FUN_NAME_STR(op) elementwise_operation_##op##_quantized +#define OP_FUN_NAME(op) OP_FUN_NAME_STR(op) + +#if defined(OP) && defined(VEC_SIZE) && defined(OFFSET_IN1) && defined(OFFSET_IN2) && defined(OFFSET_OUT) && defined(SCALE_IN1) && defined(SCALE_IN2) && defined(SCALE_OUT) + +#define VEC_FLOAT VEC_DATA_TYPE(float, VEC_SIZE) +#define VEC_INT VEC_DATA_TYPE(int, VEC_SIZE) +#define VEC_UCHAR VEC_DATA_TYPE(uchar, VEC_SIZE) + +/** This function executes an element-wise operation among two tensors. + * + * @attention The quantization offset of the first operand must be passed at compile time using -DOFFSET_IN1, i.e. -DOFFSET_IN1=10 + * @attention The quantization offset of the second operand must be passed at compile time using -DOFFSET_IN2, i.e. -DOFFSET_IN2=10 + * @attention The quantization offset of the output must be passed at compile time using -DOFFSET_OUT, i.e. -DOFFSET_OUT=10 + * @attention The quantization scale of the first operand must be passed at compile time using -DSCALE_IN1, i.e. -DSCALE_IN1=10 + * @attention The quantization scale of the second operand must be passed at compile time using -DSCALE_IN2, i.e. -DSCALE_IN2=10 + * @attention The quantization scale of the output must be passed at compile time using -DSCALE_OUT, i.e. -DSCALE_OUT=10 + * @attention To perform saturating operation -DSATURATE has to be passed to the compiler otherwise wrapping policy will be used. + * @attention Vector size should be given as a preprocessor argument using -DVEC_SIZE=size. e.g. -DVEC_SIZE=16 + * @attention The element-wise operation to be executed has to be passed at compile time using -DOP (e.g., -DOP=ADD) + * + * @param[in] in1_ptr Pointer to the source tensor. Supported data types: QASYMM8 + * @param[in] in1_stride_x Stride of the source tensor in X dimension (in bytes) + * @param[in] in1_step_x in1_stride_x * number of elements along X processed per workitem(in bytes) + * @param[in] in1_stride_y Stride of the source tensor in Y dimension (in bytes) + * @param[in] in1_step_y in1_stride_y * number of elements along Y processed per workitem(in bytes) + * @param[in] in1_stride_z Stride of the source tensor in Z dimension (in bytes) + * @param[in] in1_step_z in1_stride_z * number of elements along Z processed per workitem(in bytes) + * @param[in] in1_offset_first_element_in_bytes The offset of the first element in the source tensor + * @param[in] in2_ptr Pointer to the source tensor. Supported data types: same as @p in1_ptr + * @param[in] in2_stride_x Stride of the source tensor in X dimension (in bytes) + * @param[in] in2_step_x in2_stride_x * number of elements along X processed per workitem(in bytes) + * @param[in] in2_stride_y Stride of the source tensor in Y dimension (in bytes) + * @param[in] in2_step_y in2_stride_y * number of elements along Y processed per workitem(in bytes) + * @param[in] in2_stride_z Stride of the source tensor in Z dimension (in bytes) + * @param[in] in2_step_z in2_stride_z * number of elements along Z processed per workitem(in bytes) + * @param[in] in2_offset_first_element_in_bytes The offset of the first element in the source tensor + * @param[out] out_ptr Pointer to the destination tensor. Supported data types: same as @p in1_ptr + * @param[in] out_stride_x Stride of the destination tensor in X dimension (in bytes) + * @param[in] out_step_x out_stride_x * number of elements along X processed per workitem(in bytes) + * @param[in] out_stride_y Stride of the destination tensor in Y dimension (in bytes) + * @param[in] out_step_y out_stride_y * number of elements along Y processed per workitem(in bytes) + * @param[in] out_stride_z Stride of the source tensor in Z dimension (in bytes) + * @param[in] out_step_z out_stride_z * number of elements along Z processed per workitem(in bytes) + * @param[in] out_offset_first_element_in_bytes The offset of the first element in the destination tensor + */ +__kernel void OP_FUN_NAME(OP)( + TENSOR3D_DECLARATION(in1), + TENSOR3D_DECLARATION(in2), + TENSOR3D_DECLARATION(out)) +{ + // Get pixels pointer + Tensor3D in1 = CONVERT_TO_TENSOR3D_STRUCT(in1); + Tensor3D in2 = CONVERT_TO_TENSOR3D_STRUCT(in2); + Tensor3D out = CONVERT_TO_TENSOR3D_STRUCT(out); + + VEC_INT in_a = CONVERT(VLOAD(VEC_SIZE)(0, (__global uchar *)in1.ptr), VEC_INT); + VEC_INT in_b = CONVERT(VLOAD(VEC_SIZE)(0, (__global uchar *)in2.ptr), VEC_INT); + + in_a = SUB(in_a, (VEC_INT)((int)OFFSET_IN1)); + in_b = SUB(in_b, (VEC_INT)((int)OFFSET_IN2)); + + const VEC_FLOAT in1f32 = CONVERT(in_a, VEC_FLOAT) * (VEC_FLOAT)((float)SCALE_IN1); + const VEC_FLOAT in2f32 = CONVERT(in_b, VEC_FLOAT) * (VEC_FLOAT)((float)SCALE_IN2); + const VEC_FLOAT qresf32 = OP(in1f32, in2f32) / ((VEC_FLOAT)(float)SCALE_OUT) + ((VEC_FLOAT)((float)OFFSET_OUT)); + const VEC_UCHAR res = CONVERT_SAT(CONVERT_DOWN(qresf32, VEC_INT), VEC_UCHAR); + + // Store result + VSTORE(VEC_SIZE) + (res, 0, (__global uchar *)out.ptr); +} +#endif /* defined(OFFSET_IN1) && defined(OFFSET_IN2) && defined(OFFSET_OUT) && defined(SCALE_IN1) && defined(SCALE_IN2) && defined(SCALE_OUT) */ diff --git a/src/core/CL/kernels/CLArithmeticAdditionKernel.cpp b/src/core/CL/kernels/CLArithmeticAdditionKernel.cpp deleted file mode 100644 index 10d7fd4f2c..0000000000 --- a/src/core/CL/kernels/CLArithmeticAdditionKernel.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) 2016-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/core/CL/kernels/CLArithmeticAdditionKernel.h" - -#include "arm_compute/core/CL/CLHelpers.h" -#include "arm_compute/core/CL/CLValidate.h" -#include "arm_compute/core/CL/ICLTensor.h" - -using namespace arm_compute; - -namespace -{ -constexpr unsigned int num_elems_processed_per_iteration = 8; - -Status validate_arguments(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output, ConvertPolicy policy) -{ - ARM_COMPUTE_UNUSED(policy); - ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&input1); - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&input1, 1, DataType::U8, DataType::QASYMM8, DataType::S16, DataType::F16, DataType::F32); - ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&input2); - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&input2, 1, DataType::U8, DataType::QASYMM8, DataType::S16, DataType::F16, DataType::F32); - - const bool is_qasymm = is_data_type_quantized_asymmetric(input1.data_type()) || is_data_type_quantized_asymmetric(input2.data_type()); - if(is_qasymm) - { - ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(&input1, &input2); - } - - const TensorShape out_shape = TensorShape::broadcast_shape(input1.tensor_shape(), input2.tensor_shape()); - - ARM_COMPUTE_RETURN_ERROR_ON_MSG(out_shape.total_size() == 0, "Inputs are not broadcast compatible"); - - // Validate in case of configured output - if(output.total_size() > 0) - { - ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&output); - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&output, 1, DataType::U8, DataType::QASYMM8, DataType::S16, DataType::F16, DataType::F32); - ARM_COMPUTE_RETURN_ERROR_ON_MSG((output.data_type() == DataType::U8) && ((input1.data_type() != DataType::U8) || (input2.data_type() != DataType::U8)), - "Output can only be U8 if both inputs are U8"); - ARM_COMPUTE_RETURN_ERROR_ON_MSG(detail::have_different_dimensions(out_shape, output.tensor_shape(), 0), - "Wrong shape for output"); - if(is_qasymm) - { - ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(&input1, &output); - } - } - - return Status{}; -} - -std::pair validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) -{ - const std::pair broadcast_pair = ITensorInfo::broadcast_shape_and_valid_region(input1, input2); - const TensorShape &out_shape = broadcast_pair.first; - const ValidRegion &valid_region = broadcast_pair.second; - - // Auto initialize output if not initialized - { - set_shape_if_empty(output, out_shape); - - if(input1.data_type() == DataType::S16 || input2.data_type() == DataType::S16) - { - set_format_if_unknown(output, Format::S16); - } - else if(input1.data_type() == DataType::F16 && input2.data_type() == DataType::F16) - { - set_format_if_unknown(output, Format::F16); - } - else if(input1.data_type() == DataType::F32 || input2.data_type() == DataType::F32) - { - set_format_if_unknown(output, Format::F32); - } - } - - Window win = calculate_max_window(valid_region, Steps(num_elems_processed_per_iteration)); - Window win_input1 = win.broadcast_if_dimension_le_one(input1); - Window win_input2 = win.broadcast_if_dimension_le_one(input2); - - AccessWindowHorizontal input1_access(&input1, 0, num_elems_processed_per_iteration); - AccessWindowHorizontal input2_access(&input2, 0, num_elems_processed_per_iteration); - AccessWindowHorizontal output_access(&output, 0, num_elems_processed_per_iteration); - - bool window_changed = update_window_and_padding(win_input1, input1_access) - || update_window_and_padding(win_input2, input2_access) - || update_window_and_padding(win, output_access); - - output_access.set_valid_region(win, valid_region); - - Status err = (window_changed) ? ARM_COMPUTE_CREATE_ERROR(ErrorCode::RUNTIME_ERROR, "Insufficient Padding!") : Status{}; - return std::make_pair(err, win); -} -} // namespace - -CLArithmeticAdditionKernel::CLArithmeticAdditionKernel() - : _input1(nullptr), _input2(nullptr), _output(nullptr) -{ -} - -void CLArithmeticAdditionKernel::configure(const ICLTensor *input1, const ICLTensor *input2, ICLTensor *output, ConvertPolicy policy) -{ - ARM_COMPUTE_ERROR_ON_NULLPTR(input1, input2, output); - ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(*input1->info(), *input2->info(), *output->info(), policy)); - - // Configure kernel window - auto win_config = validate_and_configure_window(*input1->info(), *input2->info(), *output->info()); - ARM_COMPUTE_ERROR_THROW_ON(win_config.first); - - _input1 = input1; - _input2 = input2; - _output = output; - - const bool has_float_out = is_data_type_float(output->info()->data_type()); - - std::string kernel_name = "arithmetic_add"; - - // Set kernel build options - std::set build_opts; - build_opts.emplace((policy == ConvertPolicy::WRAP || has_float_out) ? "-DWRAP" : "-DSATURATE"); - build_opts.emplace("-DDATA_TYPE_IN1=" + get_cl_type_from_data_type(input1->info()->data_type())); - build_opts.emplace("-DDATA_TYPE_IN2=" + get_cl_type_from_data_type(input2->info()->data_type())); - build_opts.emplace("-DDATA_TYPE_OUT=" + get_cl_type_from_data_type(output->info()->data_type())); - build_opts.emplace("-DVEC_SIZE=" + support::cpp11::to_string(num_elems_processed_per_iteration)); - if(is_data_type_quantized_asymmetric(input1->info()->data_type())) - { - build_opts.emplace("-DOFFSET_IN1=" + support::cpp11::to_string(input1->info()->quantization_info().offset)); - build_opts.emplace("-DOFFSET_IN2=" + support::cpp11::to_string(input2->info()->quantization_info().offset)); - build_opts.emplace("-DOFFSET_OUT=" + support::cpp11::to_string(output->info()->quantization_info().offset)); - build_opts.emplace("-DSCALE_IN1=" + support::cpp11::to_string(input1->info()->quantization_info().scale)); - build_opts.emplace("-DSCALE_IN2=" + support::cpp11::to_string(input2->info()->quantization_info().scale)); - build_opts.emplace("-DSCALE_OUT=" + support::cpp11::to_string(output->info()->quantization_info().scale)); - kernel_name += "_quantized"; - } - - // Create kernel - _kernel = static_cast(CLKernelLibrary::get().create_kernel(kernel_name, build_opts)); - - ICLKernel::configure_internal(win_config.second); - - // Set config_id for enabling LWS tuning - _config_id = kernel_name; - _config_id += "_"; - _config_id += lower_string(string_from_data_type(input1->info()->data_type())); - _config_id += "_"; - _config_id += support::cpp11::to_string(output->info()->dimension(0)); - _config_id += "_"; - _config_id += support::cpp11::to_string(output->info()->dimension(1)); - _config_id += (policy == ConvertPolicy::WRAP) ? "_wrap_" : "_saturate_"; - _config_id += lower_string(string_from_data_layout(input1->info()->data_layout())); -} - -Status CLArithmeticAdditionKernel::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy) -{ - ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input1, input2, output); - - ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(*input1, *input2, *output, policy)); - ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window(*input1->clone(), *input2->clone(), *output->clone()).first); - - return Status{}; -} - -void CLArithmeticAdditionKernel::run(const Window &window, cl::CommandQueue &queue) -{ - ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this); - ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(ICLKernel::window(), window); - - const TensorShape &in_shape1 = _input1->info()->tensor_shape(); - const TensorShape &in_shape2 = _input2->info()->tensor_shape(); - const TensorShape &out_shape = _output->info()->tensor_shape(); - - bool can_collapse = true; - const bool is_vector = in_shape1.num_dimensions() == 1 || in_shape2.num_dimensions() == 1; - if(std::min(in_shape1.total_size(), in_shape2.total_size()) > 1 && !is_vector) - { - can_collapse = (std::min(in_shape1.num_dimensions(), in_shape2.num_dimensions()) > Window::DimZ); - for(size_t d = Window::DimZ; can_collapse && (d < out_shape.num_dimensions()); d++) - { - can_collapse = (in_shape1[d] == in_shape2[d]); - } - } - - bool has_collapsed = false; - Window collapsed = can_collapse ? window.collapse_if_possible(ICLKernel::window(), Window::DimZ, &has_collapsed) : window; - - const TensorShape &in_shape1_collapsed = has_collapsed ? in_shape1.collapsed_from(Window::DimZ) : in_shape1; - const TensorShape &in_shape2_collapsed = has_collapsed ? in_shape2.collapsed_from(Window::DimZ) : in_shape2; - - Window slice = collapsed.first_slice_window_3D(); - Window slice_input1 = slice.broadcast_if_dimension_le_one(in_shape1_collapsed); - Window slice_input2 = slice.broadcast_if_dimension_le_one(in_shape2_collapsed); - - do - { - unsigned int idx = 0; - - add_3D_tensor_argument(idx, _input1, slice_input1); - add_3D_tensor_argument(idx, _input2, slice_input2); - add_3D_tensor_argument(idx, _output, slice); - - enqueue(queue, *this, slice, lws_hint()); - - collapsed.slide_window_slice_3D(slice_input1); - collapsed.slide_window_slice_3D(slice_input2); - } - while(collapsed.slide_window_slice_3D(slice)); -} - -BorderSize CLArithmeticAdditionKernel::border_size() const -{ - const unsigned int replicateSize = _output->info()->dimension(0) - std::min(_input1->info()->dimension(0), _input2->info()->dimension(0)); - const unsigned int border = std::min(num_elems_processed_per_iteration - 1U, replicateSize); - return BorderSize(0, border, 0, 0); -} diff --git a/src/core/CL/kernels/CLArithmeticDivisionKernel.cpp b/src/core/CL/kernels/CLArithmeticDivisionKernel.cpp deleted file mode 100644 index e995ba1a41..0000000000 --- a/src/core/CL/kernels/CLArithmeticDivisionKernel.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/core/CL/kernels/CLArithmeticDivisionKernel.h" - -#include "arm_compute/core/CL/CLHelpers.h" -#include "arm_compute/core/CL/CLValidate.h" -#include "arm_compute/core/CL/ICLTensor.h" - -using namespace arm_compute; - -namespace -{ -constexpr unsigned int num_elems_processed_per_iteration = 16; - -Status validate_arguments(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output) -{ - ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input1, input2, output); - ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(input1); - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input1, 1, DataType::F16, DataType::F32); - ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input1, input2); - - const TensorShape out_shape = TensorShape::broadcast_shape(input1->tensor_shape(), input2->tensor_shape()); - - ARM_COMPUTE_RETURN_ERROR_ON_MSG(out_shape.total_size() == 0, "Inputs are not broadcast compatible"); - - // Validate in case of configured output - if(output->total_size() > 0) - { - ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input1, output); - ARM_COMPUTE_RETURN_ERROR_ON_MSG(detail::have_different_dimensions(out_shape, output->tensor_shape(), 0), - "Wrong shape for output"); - } - - return Status{}; -} - -std::pair validate_and_configure_window(ITensorInfo *input1, ITensorInfo *input2, ITensorInfo *output) -{ - const std::pair broadcast_pair = ITensorInfo::broadcast_shape_and_valid_region(*input1, *input2); - const TensorShape &out_shape = broadcast_pair.first; - const ValidRegion &valid_region = broadcast_pair.second; - - // Auto initialize output if not initialized - { - set_shape_if_empty(*output, out_shape); - - if(input1->data_type() == DataType::F16 && input2->data_type() == DataType::F16) - { - set_format_if_unknown(*output, Format::F16); - } - else if(input1->data_type() == DataType::F32 || input2->data_type() == DataType::F32) - { - set_format_if_unknown(*output, Format::F32); - } - } - - Window win = calculate_max_window(valid_region, Steps(num_elems_processed_per_iteration)); - Window win_input1 = win.broadcast_if_dimension_le_one(*input1); - Window win_input2 = win.broadcast_if_dimension_le_one(*input2); - - AccessWindowHorizontal input1_access(input1, 0, num_elems_processed_per_iteration); - AccessWindowHorizontal input2_access(input2, 0, num_elems_processed_per_iteration); - AccessWindowHorizontal output_access(output, 0, num_elems_processed_per_iteration); - - bool window_changed = update_window_and_padding(win_input1, input1_access) - || update_window_and_padding(win_input2, input2_access) - || update_window_and_padding(win, output_access); - - output_access.set_valid_region(win, valid_region); - - Status err = (window_changed) ? ARM_COMPUTE_CREATE_ERROR(ErrorCode::RUNTIME_ERROR, "Insufficient Padding!") : Status{}; - return std::make_pair(err, win); -} -} // namespace - -CLArithmeticDivisionKernel::CLArithmeticDivisionKernel() - : _input1(nullptr), _input2(nullptr), _output(nullptr) -{ -} - -void CLArithmeticDivisionKernel::configure(const ICLTensor *input1, const ICLTensor *input2, ICLTensor *output) -{ - ARM_COMPUTE_ERROR_ON_NULLPTR(input1, input2, output); - ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(input1->info(), input2->info(), output->info())); - - // Configure kernel window - auto win_config = validate_and_configure_window(input1->info(), input2->info(), output->info()); - ARM_COMPUTE_ERROR_THROW_ON(win_config.first); - - _input1 = input1; - _input2 = input2; - _output = output; - - // Set kernel build options - std::set build_opts; - build_opts.emplace("-DDATA_TYPE_IN1=" + get_cl_type_from_data_type(input1->info()->data_type())); - build_opts.emplace("-DDATA_TYPE_IN2=" + get_cl_type_from_data_type(input2->info()->data_type())); - build_opts.emplace("-DDATA_TYPE_OUT=" + get_cl_type_from_data_type(output->info()->data_type())); - - // Create kernel - _kernel = static_cast(CLKernelLibrary::get().create_kernel("arithmetic_div", build_opts)); - - ICLKernel::configure_internal(win_config.second); -} - -Status CLArithmeticDivisionKernel::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output) -{ - ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(input1, input2, output)); - ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window(input1->clone().get(), input2->clone().get(), output->clone().get()).first); - - return Status{}; -} - -void CLArithmeticDivisionKernel::run(const Window &window, cl::CommandQueue &queue) -{ - ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this); - ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(ICLKernel::window(), window); - - const TensorShape &in_shape1 = _input1->info()->tensor_shape(); - const TensorShape &in_shape2 = _input2->info()->tensor_shape(); - const TensorShape &out_shape = _output->info()->tensor_shape(); - - bool can_collapse = true; - if(std::min(in_shape1.total_size(), in_shape2.total_size()) > 1) - { - can_collapse = (std::min(in_shape1.num_dimensions(), in_shape2.num_dimensions()) > Window::DimZ); - for(size_t d = Window::DimZ; can_collapse && (d < out_shape.num_dimensions()); d++) - { - can_collapse = (in_shape1[d] == in_shape2[d]); - } - } - - bool has_collapsed = false; - Window collapsed = can_collapse ? window.collapse_if_possible(ICLKernel::window(), Window::DimZ, &has_collapsed) : window; - - const TensorShape &in_shape1_collapsed = has_collapsed ? in_shape1.collapsed_from(Window::DimZ) : in_shape1; - const TensorShape &in_shape2_collapsed = has_collapsed ? in_shape2.collapsed_from(Window::DimZ) : in_shape2; - - Window slice = collapsed.first_slice_window_3D(); - Window slice_input1 = slice.broadcast_if_dimension_le_one(in_shape1_collapsed); - Window slice_input2 = slice.broadcast_if_dimension_le_one(in_shape2_collapsed); - - do - { - unsigned int idx = 0; - - add_3D_tensor_argument(idx, _input1, slice_input1); - add_3D_tensor_argument(idx, _input2, slice_input2); - add_3D_tensor_argument(idx, _output, slice); - - enqueue(queue, *this, slice); - - collapsed.slide_window_slice_3D(slice_input1); - collapsed.slide_window_slice_3D(slice_input2); - } - while(collapsed.slide_window_slice_3D(slice)); -} - -BorderSize CLArithmeticDivisionKernel::border_size() const -{ - const unsigned int replicateSize = _output->info()->dimension(0) - std::min(_input1->info()->dimension(0), _input2->info()->dimension(0)); - const unsigned int border = std::min(num_elems_processed_per_iteration - 1U, replicateSize); - return BorderSize(0, border, 0, 0); -} diff --git a/src/core/CL/kernels/CLArithmeticSubtractionKernel.cpp b/src/core/CL/kernels/CLArithmeticSubtractionKernel.cpp deleted file mode 100644 index 95d201104d..0000000000 --- a/src/core/CL/kernels/CLArithmeticSubtractionKernel.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (c) 2016-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/core/CL/kernels/CLArithmeticSubtractionKernel.h" - -#include "arm_compute/core/CL/CLHelpers.h" -#include "arm_compute/core/CL/CLKernelLibrary.h" -#include "arm_compute/core/CL/CLValidate.h" -#include "arm_compute/core/CL/ICLTensor.h" -#include "arm_compute/core/CL/OpenCL.h" -#include "arm_compute/core/Helpers.h" -#include "arm_compute/core/IAccessWindow.h" -#include "arm_compute/core/TensorInfo.h" -#include "arm_compute/core/Window.h" - -#include -#include - -namespace arm_compute -{ -namespace -{ -constexpr unsigned int num_elems_processed_per_iteration = 16; - -Status validate_arguments(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output, ConvertPolicy policy) -{ - ARM_COMPUTE_UNUSED(policy); - ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&input1); - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&input1, 1, DataType::U8, DataType::QASYMM8, DataType::S16, DataType::F16, DataType::F32); - ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&input2); - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&input2, 1, DataType::U8, DataType::QASYMM8, DataType::S16, DataType::F16, DataType::F32); - const bool is_qasymm = is_data_type_quantized_asymmetric(input1.data_type()) || is_data_type_quantized_asymmetric(input2.data_type()); - if(is_qasymm) - { - ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(&input1, &input2); - } - - const TensorShape out_shape = TensorShape::broadcast_shape(input1.tensor_shape(), input2.tensor_shape()); - - ARM_COMPUTE_RETURN_ERROR_ON_MSG(out_shape.total_size() == 0, "Inputs are not broadcast compatible"); - - // Validate in case of configured output - if(output.total_size() > 0) - { - ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&output); - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&output, 1, DataType::U8, DataType::QASYMM8, DataType::S16, DataType::F16, DataType::F32); - ARM_COMPUTE_RETURN_ERROR_ON_MSG((output.data_type() == DataType::U8) && ((input1.data_type() != DataType::U8) || (input2.data_type() != DataType::U8)), - "Output can only be U8 if both inputs are U8"); - ARM_COMPUTE_RETURN_ERROR_ON_MSG(detail::have_different_dimensions(out_shape, output.tensor_shape(), 0), - "Wrong shape for output"); - if(is_qasymm) - { - ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(&input1, &output); - } - } - - return Status{}; -} - -std::pair validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) -{ - const std::pair broadcast_pair = ITensorInfo::broadcast_shape_and_valid_region(input1, input2); - const TensorShape &out_shape = broadcast_pair.first; - const ValidRegion &valid_region = broadcast_pair.second; - - // Auto initialize output if not initialized - { - set_shape_if_empty(output, out_shape); - - if(input1.data_type() == DataType::S16 || input2.data_type() == DataType::S16) - { - set_format_if_unknown(output, Format::S16); - } - else if(input1.data_type() == DataType::F16 && input2.data_type() == DataType::F16) - { - set_format_if_unknown(output, Format::F16); - } - else if(input1.data_type() == DataType::F32 || input2.data_type() == DataType::F32) - { - set_format_if_unknown(output, Format::F32); - } - } - - Window win = calculate_max_window(valid_region, Steps(num_elems_processed_per_iteration)); - Window win_input1 = win.broadcast_if_dimension_le_one(input1); - Window win_input2 = win.broadcast_if_dimension_le_one(input2); - - AccessWindowHorizontal input1_access(&input1, 0, num_elems_processed_per_iteration); - AccessWindowHorizontal input2_access(&input2, 0, num_elems_processed_per_iteration); - AccessWindowHorizontal output_access(&output, 0, num_elems_processed_per_iteration); - - bool window_changed = update_window_and_padding(win_input1, input1_access) - || update_window_and_padding(win_input2, input2_access) - || update_window_and_padding(win, output_access); - - output_access.set_valid_region(win, valid_region); - - Status err = (window_changed) ? ARM_COMPUTE_CREATE_ERROR(ErrorCode::RUNTIME_ERROR, "Insufficient Padding!") : Status{}; - return std::make_pair(err, win); -} -} // namespace - -CLArithmeticSubtractionKernel::CLArithmeticSubtractionKernel() - : _input1(nullptr), _input2(nullptr), _output(nullptr) -{ -} - -void CLArithmeticSubtractionKernel::configure(const ICLTensor *input1, const ICLTensor *input2, ICLTensor *output, ConvertPolicy policy) -{ - ARM_COMPUTE_ERROR_ON_NULLPTR(input1, input2, output); - ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(*input1->info(), *input2->info(), *output->info(), policy)); - - // Configure kernel window - auto win_config = validate_and_configure_window(*input1->info(), *input2->info(), *output->info()); - ARM_COMPUTE_ERROR_THROW_ON(win_config.first); - - _input1 = input1; - _input2 = input2; - _output = output; - - bool has_float_out = is_data_type_float(output->info()->data_type()); - - // Setup kernel - std::string kernel_name = "arithmetic_sub"; - - // Set kernel build options - CLBuildOptions build_opts; - build_opts.add_option_if_else(policy == ConvertPolicy::WRAP || has_float_out, "-DWRAP", "-DSATURATE"); - build_opts.add_option("-DDATA_TYPE_IN1=" + get_cl_type_from_data_type(input1->info()->data_type())); - build_opts.add_option("-DDATA_TYPE_IN2=" + get_cl_type_from_data_type(input2->info()->data_type())); - build_opts.add_option("-DDATA_TYPE_OUT=" + get_cl_type_from_data_type(output->info()->data_type())); - if(is_data_type_quantized_asymmetric(input1->info()->data_type())) - { - build_opts.add_option("-DOFFSET_IN1=" + support::cpp11::to_string(input1->info()->quantization_info().offset)); - build_opts.add_option("-DOFFSET_IN2=" + support::cpp11::to_string(input2->info()->quantization_info().offset)); - build_opts.add_option("-DOFFSET_OUT=" + support::cpp11::to_string(output->info()->quantization_info().offset)); - build_opts.add_option("-DSCALE_IN1=" + support::cpp11::to_string(input1->info()->quantization_info().scale)); - build_opts.add_option("-DSCALE_IN2=" + support::cpp11::to_string(input2->info()->quantization_info().scale)); - build_opts.add_option("-DSCALE_OUT=" + support::cpp11::to_string(output->info()->quantization_info().scale)); - kernel_name += "_quantized"; - } - - // Create kernel - _kernel = static_cast(CLKernelLibrary::get().create_kernel(kernel_name, build_opts.options())); - - // Configure kernel window - ICLKernel::configure_internal(win_config.second); -} - -Status CLArithmeticSubtractionKernel::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy) -{ - ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input1, input2, output); - - ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(*input1, *input2, *output, policy)); - ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window(*input1->clone(), *input2->clone(), *output->clone()).first); - - return Status{}; -} - -void CLArithmeticSubtractionKernel::run(const Window &window, cl::CommandQueue &queue) -{ - ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this); - ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(ICLKernel::window(), window); - - const TensorShape &in_shape1 = _input1->info()->tensor_shape(); - const TensorShape &in_shape2 = _input2->info()->tensor_shape(); - const TensorShape &out_shape = _output->info()->tensor_shape(); - - // Collapse only if broadcast dimensions is less than 2, or in case of no broadcasting - bool can_collapse = true; - if(std::min(in_shape1.total_size(), in_shape2.total_size()) > 1) - { - can_collapse = (std::min(in_shape1.num_dimensions(), in_shape2.num_dimensions()) > Window::DimZ); - for(size_t d = Window::DimZ; can_collapse && (d < out_shape.num_dimensions()); d++) - { - can_collapse = (in_shape1[d] == in_shape2[d]); - } - } - - bool has_collapsed = false; - Window collapsed = can_collapse ? window.collapse_if_possible(ICLKernel::window(), Window::DimZ, &has_collapsed) : window; - - const TensorShape &in_shape1_collapsed = has_collapsed ? in_shape1.collapsed_from(Window::DimZ) : in_shape1; - const TensorShape &in_shape2_collapsed = has_collapsed ? in_shape2.collapsed_from(Window::DimZ) : in_shape2; - - Window slice = collapsed.first_slice_window_3D(); - Window slice_input1 = slice.broadcast_if_dimension_le_one(in_shape1_collapsed); - Window slice_input2 = slice.broadcast_if_dimension_le_one(in_shape2_collapsed); - - do - { - unsigned int idx = 0; - - add_3D_tensor_argument(idx, _input1, slice_input1); - add_3D_tensor_argument(idx, _input2, slice_input2); - add_3D_tensor_argument(idx, _output, slice); - - enqueue(queue, *this, slice); - - collapsed.slide_window_slice_3D(slice_input1); - collapsed.slide_window_slice_3D(slice_input2); - } - while(collapsed.slide_window_slice_3D(slice)); -} - -BorderSize CLArithmeticSubtractionKernel::border_size() const -{ - const unsigned int replicateSize = _output->info()->dimension(0) - std::min(_input1->info()->dimension(0), _input2->info()->dimension(0)); - const unsigned int border = std::min(num_elems_processed_per_iteration - 1U, replicateSize); - return BorderSize(0, border, 0, 0); -} -} // namespace arm_compute \ No newline at end of file diff --git a/src/core/CL/kernels/CLElementwiseOperationKernel.cpp b/src/core/CL/kernels/CLElementwiseOperationKernel.cpp new file mode 100644 index 0000000000..5dc5b7e13f --- /dev/null +++ b/src/core/CL/kernels/CLElementwiseOperationKernel.cpp @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/core/CL/kernels/CLElementwiseOperationKernel.h" + +#include "arm_compute/core/CL/CLHelpers.h" +#include "arm_compute/core/CL/CLValidate.h" +#include "arm_compute/core/CL/ICLTensor.h" +#include + +namespace arm_compute +{ +namespace +{ +constexpr unsigned int num_elems_processed_per_iteration = 16; + +std::map supported_arithmetic_ops = +{ + { ArithmeticOperation::ADD, "ADD" }, + { ArithmeticOperation::SUB, "SUB" }, + { ArithmeticOperation::DIV, "DIV" }, + { ArithmeticOperation::SQUARED_DIFF, "SQUARED_DIFF" }, + { ArithmeticOperation::MIN, "MIN" }, + { ArithmeticOperation::MAX, "MAX" }, +}; + +std::map supported_sat_arithmetic_ops = +{ + { ArithmeticOperation::ADD, "ADD" }, + { ArithmeticOperation::SUB, "SUB" }, +}; + +std::string generate_id_for_tuning_common(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) +{ + std::string config_id; + // Set config_id for enabling LWS tuning + config_id = kernel_name; + config_id += "_"; + config_id += lower_string(string_from_data_type(input1.data_type())); + config_id += "_"; + config_id += support::cpp11::to_string(output.dimension(0)); + config_id += "_"; + config_id += support::cpp11::to_string(output.dimension(1)); + return config_id; +} + +Status validate_arguments_with_arithmetic_rules(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) +{ + ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&input1); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&input1, 1, DataType::U8, DataType::QASYMM8, DataType::S16, DataType::F16, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&input2); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&input2, 1, DataType::U8, DataType::QASYMM8, DataType::S16, DataType::F16, DataType::F32); + + const bool is_qasymm = is_data_type_quantized_asymmetric(input1.data_type()) || is_data_type_quantized_asymmetric(input2.data_type()); + if(is_qasymm) + { + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(&input1, &input2); + } + + const TensorShape out_shape = TensorShape::broadcast_shape(input1.tensor_shape(), input2.tensor_shape()); + + ARM_COMPUTE_RETURN_ERROR_ON_MSG(out_shape.total_size() == 0, "Inputs are not broadcast compatible"); + + // Validate in case of configured output + if(output.total_size() > 0) + { + ARM_COMPUTE_RETURN_ERROR_ON_F16_UNSUPPORTED(&output); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&output, 1, DataType::U8, DataType::QASYMM8, DataType::S16, DataType::F16, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON_MSG((output.data_type() == DataType::U8) && ((input1.data_type() != DataType::U8) || (input2.data_type() != DataType::U8)), + "Output can only be U8 if both inputs are U8"); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(detail::have_different_dimensions(out_shape, output.tensor_shape(), 0), + "Wrong shape for output"); + if(is_qasymm) + { + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(&input1, &output); + } + } + return Status{}; +} + +CLBuildOptions generate_build_options_with_arithmetic_rules(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output, const std::string &operation_string) +{ + CLBuildOptions build_opts; + + build_opts.add_option("-DDATA_TYPE_IN1=" + get_cl_type_from_data_type(input1.data_type())); + build_opts.add_option("-DDATA_TYPE_IN2=" + get_cl_type_from_data_type(input2.data_type())); + build_opts.add_option("-DDATA_TYPE_OUT=" + get_cl_type_from_data_type(output.data_type())); + build_opts.add_option("-DVEC_SIZE=" + support::cpp11::to_string(num_elems_processed_per_iteration)); + build_opts.add_option("-DOP=" + operation_string); + if(is_data_type_quantized_asymmetric(input1.data_type())) + { + build_opts.add_option("-DOFFSET_IN1=" + support::cpp11::to_string(input1.quantization_info().offset)); + build_opts.add_option("-DOFFSET_IN2=" + support::cpp11::to_string(input2.quantization_info().offset)); + build_opts.add_option("-DOFFSET_OUT=" + support::cpp11::to_string(output.quantization_info().offset)); + build_opts.add_option("-DSCALE_IN1=" + float_to_string_with_full_precision(input1.quantization_info().scale)); + build_opts.add_option("-DSCALE_IN2=" + float_to_string_with_full_precision(input2.quantization_info().scale)); + build_opts.add_option("-DSCALE_OUT=" + float_to_string_with_full_precision(output.quantization_info().scale)); + } + return build_opts; +} + +std::pair validate_and_configure_window_for_arithmetic_operators(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) +{ + const std::pair broadcast_pair = ITensorInfo::broadcast_shape_and_valid_region(input1, input2); + const TensorShape &out_shape = broadcast_pair.first; + const ValidRegion &valid_region = broadcast_pair.second; + + set_shape_if_empty(output, out_shape); + + if(input1.data_type() == DataType::S16 || input2.data_type() == DataType::S16) + { + set_format_if_unknown(output, Format::S16); + } + else if(input1.data_type() == DataType::F16 && input2.data_type() == DataType::F16) + { + set_format_if_unknown(output, Format::F16); + } + else if(input1.data_type() == DataType::F32 || input2.data_type() == DataType::F32) + { + set_format_if_unknown(output, Format::F32); + } + + Window win = calculate_max_window(valid_region, Steps(num_elems_processed_per_iteration)); + Window win_input1 = win.broadcast_if_dimension_le_one(input1); + Window win_input2 = win.broadcast_if_dimension_le_one(input2); + + AccessWindowHorizontal input1_access(&input1, 0, num_elems_processed_per_iteration); + AccessWindowHorizontal input2_access(&input2, 0, num_elems_processed_per_iteration); + AccessWindowHorizontal output_access(&output, 0, num_elems_processed_per_iteration); + + bool window_changed = update_window_and_padding(win_input1, input1_access) + || update_window_and_padding(win_input2, input2_access) + || update_window_and_padding(win, output_access); + + output_access.set_valid_region(win, valid_region); + + Status err = (window_changed) ? ARM_COMPUTE_CREATE_ERROR(ErrorCode::RUNTIME_ERROR, "Insufficient Padding!") : Status{}; + return std::make_pair(err, win); +} +} // namespace + +CLElementwiseOperationKernel::CLElementwiseOperationKernel() + : _input1(nullptr), _input2(nullptr), _output(nullptr) +{ +} + +void CLElementwiseOperationKernel::configure_common(const ICLTensor *input1, const ICLTensor *input2, ICLTensor *output) +{ + ARM_COMPUTE_ERROR_ON_NULLPTR(input1, input2, output); + ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(*input1->info(), *input2->info(), *output->info())); + + // Configure kernel window + auto win_config = validate_and_configure_window(*input1->info(), *input2->info(), *output->info()); + ARM_COMPUTE_ERROR_THROW_ON(win_config.first); + + _input1 = input1; + _input2 = input2; + _output = output; + + std::string kernel_name = "elementwise_operation_" + name(); + if(is_data_type_quantized_asymmetric(input1->info()->data_type())) + { + kernel_name += "_quantized"; + } + + // Set kernel build options + CLBuildOptions build_opts = generate_build_options(*input1->info(), *input2->info(), *output->info()); + + // Create kernel + _kernel = static_cast(CLKernelLibrary::get().create_kernel(kernel_name, build_opts.options())); + + ICLKernel::configure_internal(win_config.second); + + _config_id = generate_id_for_tuning(kernel_name, *input1->info(), *output->info()); +} + +void CLElementwiseOperationKernel::run(const Window &window, cl::CommandQueue &queue) +{ + ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this); + ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(ICLKernel::window(), window); + + const TensorShape &in_shape1 = _input1->info()->tensor_shape(); + const TensorShape &in_shape2 = _input2->info()->tensor_shape(); + const TensorShape &out_shape = _output->info()->tensor_shape(); + + bool can_collapse = true; + const bool is_vector = in_shape1.num_dimensions() == 1 || in_shape2.num_dimensions() == 1; + if(std::min(in_shape1.total_size(), in_shape2.total_size()) > 1 && !is_vector) + { + can_collapse = (std::min(in_shape1.num_dimensions(), in_shape2.num_dimensions()) > Window::DimZ); + for(size_t d = Window::DimZ; can_collapse && (d < out_shape.num_dimensions()); d++) + { + can_collapse = (in_shape1[d] == in_shape2[d]); + } + } + + bool has_collapsed = false; + Window collapsed = can_collapse ? window.collapse_if_possible(ICLKernel::window(), Window::DimZ, &has_collapsed) : window; + + const TensorShape &in_shape1_collapsed = has_collapsed ? in_shape1.collapsed_from(Window::DimZ) : in_shape1; + const TensorShape &in_shape2_collapsed = has_collapsed ? in_shape2.collapsed_from(Window::DimZ) : in_shape2; + + Window slice = collapsed.first_slice_window_3D(); + Window slice_input1 = slice.broadcast_if_dimension_le_one(in_shape1_collapsed); + Window slice_input2 = slice.broadcast_if_dimension_le_one(in_shape2_collapsed); + + do + { + unsigned int idx = 0; + + add_3D_tensor_argument(idx, _input1, slice_input1); + add_3D_tensor_argument(idx, _input2, slice_input2); + add_3D_tensor_argument(idx, _output, slice); + + enqueue(queue, *this, slice, lws_hint()); + + collapsed.slide_window_slice_3D(slice_input1); + collapsed.slide_window_slice_3D(slice_input2); + } + while(collapsed.slide_window_slice_3D(slice)); +} + +BorderSize CLElementwiseOperationKernel::border_size() const +{ + const unsigned int replicateSize = _output->info()->dimension(0) - std::min(_input1->info()->dimension(0), _input2->info()->dimension(0)); + const unsigned int border = std::min(num_elems_processed_per_iteration - 1U, replicateSize); + return BorderSize(0, border, 0, 0); +} + +/** Arithmetic operations with saturation*/ + +void CLSaturatedArithmeticOperationKernel::configure(ArithmeticOperation op, const ICLTensor *input1, const ICLTensor *input2, ICLTensor *output, const ConvertPolicy &policy) +{ + _policy = policy; + _op = op; + configure_common(input1, input2, output); +} + +Status CLSaturatedArithmeticOperationKernel::validate(ArithmeticOperation op, const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, const ConvertPolicy &policy) +{ + ARM_COMPUTE_UNUSED(op, policy); + ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input1, input2, output); + ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments_with_arithmetic_rules(*input1, *input2, *output)); + ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window_for_arithmetic_operators(*input1->clone(), *input2->clone(), *output->clone()).first); + + return Status{}; +} + +std::pair CLSaturatedArithmeticOperationKernel::validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) +{ + return validate_and_configure_window_for_arithmetic_operators(input1, input2, output); +} + +Status CLSaturatedArithmeticOperationKernel::validate_arguments(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) +{ + return validate_arguments_with_arithmetic_rules(input1, input2, output); +} + +CLBuildOptions CLSaturatedArithmeticOperationKernel::generate_build_options(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) +{ + const bool has_float_out = is_data_type_float(output.data_type()); + auto build_options = generate_build_options_with_arithmetic_rules(input1, input2, output, name()); + build_options.add_option((_policy == ConvertPolicy::WRAP || has_float_out) ? "-DWRAP" : "-DSATURATE"); + return build_options; +} +std::string CLSaturatedArithmeticOperationKernel::generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) +{ + auto config_id = generate_id_for_tuning_common(kernel_name, input1, output); + config_id += (_policy == ConvertPolicy::WRAP) ? "_wrap_" : "_saturate_"; + config_id += lower_string(string_from_data_layout(input1.data_layout())); + return config_id; +} + +std::string CLSaturatedArithmeticOperationKernel::name() +{ + return supported_sat_arithmetic_ops[_op]; +} + +/** Arithmetic operations*/ + +void CLArithmeticOperationKernel::configure(ArithmeticOperation op, const ICLTensor *input1, const ICLTensor *input2, ICLTensor *output) +{ + _op = op; + configure_common(input1, input2, output); +} + +Status CLArithmeticOperationKernel::validate(ArithmeticOperation op, const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output) +{ + ARM_COMPUTE_UNUSED(op); + ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input1, input2, output); + ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments_with_arithmetic_rules(*input1, *input2, *output)); + ARM_COMPUTE_RETURN_ON_ERROR(validate_and_configure_window_for_arithmetic_operators(*input1->clone(), *input2->clone(), *output->clone()).first); + return Status{}; +} +std::pair CLArithmeticOperationKernel::validate_and_configure_window(ITensorInfo &input1, ITensorInfo &input2, ITensorInfo &output) +{ + return validate_and_configure_window_for_arithmetic_operators(input1, input2, output); +} +Status CLArithmeticOperationKernel::validate_arguments(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) +{ + return validate_arguments_with_arithmetic_rules(input1, input2, output); +} + +CLBuildOptions CLArithmeticOperationKernel::generate_build_options(const ITensorInfo &input1, const ITensorInfo &input2, const ITensorInfo &output) +{ + return generate_build_options_with_arithmetic_rules(input1, input2, output, name()); +} +std::string CLArithmeticOperationKernel::generate_id_for_tuning(const std::string &kernel_name, const ITensorInfo &input1, const ITensorInfo &output) +{ + return generate_id_for_tuning_common(kernel_name, input1, output); +} + +std::string CLArithmeticOperationKernel::name() +{ + return supported_arithmetic_ops[_op]; +} +} // namespace arm_compute diff --git a/src/runtime/CL/functions/CLArithmeticAddition.cpp b/src/runtime/CL/functions/CLArithmeticAddition.cpp deleted file mode 100644 index 0b05058c4d..0000000000 --- a/src/runtime/CL/functions/CLArithmeticAddition.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2016-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/runtime/CL/functions/CLArithmeticAddition.h" - -#include "arm_compute/core/CL/ICLTensor.h" -#include "arm_compute/core/CL/kernels/CLArithmeticAdditionKernel.h" -#include "support/ToolchainSupport.h" - -#include - -using namespace arm_compute; - -void CLArithmeticAddition::configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output, ConvertPolicy policy) -{ - auto k = arm_compute::support::cpp14::make_unique(); - k->configure(input1, input2, output, policy); - _kernel = std::move(k); - - if(output->info()->dimension(0) > 1) - { - ICLTensor *broadcasted_info = (input1->info()->dimension(0) == 1) ? input1 : input2; - - if(broadcasted_info->info()->dimension(0) == 1) - { - _border_handler.configure(broadcasted_info, _kernel->border_size(), BorderMode::REPLICATE); - } - } -} - -Status CLArithmeticAddition::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy) -{ - return CLArithmeticAdditionKernel::validate(input1, input2, output, policy); -} diff --git a/src/runtime/CL/functions/CLArithmeticDivision.cpp b/src/runtime/CL/functions/CLArithmeticDivision.cpp deleted file mode 100644 index 1c2849cee9..0000000000 --- a/src/runtime/CL/functions/CLArithmeticDivision.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/runtime/CL/functions/CLArithmeticDivision.h" - -#include "arm_compute/core/CL/ICLTensor.h" -#include "arm_compute/core/CL/kernels/CLArithmeticDivisionKernel.h" -#include "support/ToolchainSupport.h" - -#include - -using namespace arm_compute; - -void CLArithmeticDivision::configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output) -{ - auto k = arm_compute::support::cpp14::make_unique(); - k->configure(input1, input2, output); - _kernel = std::move(k); - - if(output->info()->dimension(0) > 1) - { - ICLTensor *broadcasted_info = (input1->info()->dimension(0) == 1) ? input1 : input2; - - if(broadcasted_info->info()->dimension(0) == 1) - { - _border_handler.configure(broadcasted_info, _kernel->border_size(), BorderMode::REPLICATE); - } - } -} - -Status CLArithmeticDivision::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output) -{ - return CLArithmeticDivisionKernel::validate(input1, input2, output); -} diff --git a/src/runtime/CL/functions/CLArithmeticSubtraction.cpp b/src/runtime/CL/functions/CLArithmeticSubtraction.cpp deleted file mode 100644 index e661f6adc1..0000000000 --- a/src/runtime/CL/functions/CLArithmeticSubtraction.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2016-2018 ARM Limited. - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "arm_compute/runtime/CL/functions/CLArithmeticSubtraction.h" - -#include "arm_compute/core/CL/ICLTensor.h" -#include "arm_compute/core/CL/kernels/CLArithmeticSubtractionKernel.h" -#include "support/ToolchainSupport.h" - -#include - -using namespace arm_compute; - -void CLArithmeticSubtraction::configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output, ConvertPolicy policy) -{ - auto k = arm_compute::support::cpp14::make_unique(); - k->configure(input1, input2, output, policy); - _kernel = std::move(k); - - if(output->info()->dimension(0) > 1) - { - ICLTensor *broadcasted_info = (input1->info()->dimension(0) == 1) ? input1 : input2; - - if(broadcasted_info->info()->dimension(0) == 1) - { - _border_handler.configure(broadcasted_info, _kernel->border_size(), BorderMode::REPLICATE); - } - } -} - -Status CLArithmeticSubtraction::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy) -{ - return CLArithmeticSubtractionKernel::validate(input1, input2, output, policy); -} diff --git a/src/runtime/CL/functions/CLElementwiseOperations.cpp b/src/runtime/CL/functions/CLElementwiseOperations.cpp new file mode 100644 index 0000000000..28f4b13f22 --- /dev/null +++ b/src/runtime/CL/functions/CLElementwiseOperations.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/core/CL/ICLTensor.h" +#include "arm_compute/core/CL/kernels/CLElementwiseOperationKernel.h" +#include "support/ToolchainSupport.h" +#include + +#include + +namespace arm_compute +{ +namespace +{ +void configure_border_handler(CLFillBorderKernel &border_handler, BorderSize border_size, ICLTensor *input1, ICLTensor *input2, const ICLTensor *output) +{ + if(output->info()->dimension(0) > 1) + { + ICLTensor *broadcasted_info = (input1->info()->dimension(0) == 1) ? input1 : input2; + + if(broadcasted_info->info()->dimension(0) == 1) + { + border_handler.configure(broadcasted_info, border_size, BorderMode::REPLICATE); + } + } +} +} // namespace + +void CLArithmeticAddition::configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output, ConvertPolicy policy) +{ + auto k = arm_compute::support::cpp14::make_unique(); + k->configure(ArithmeticOperation::ADD, input1, input2, output, policy); + _kernel = std::move(k); + configure_border_handler(_border_handler, _kernel->border_size(), input1, input2, output); +} + +Status CLArithmeticAddition::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy) +{ + return CLSaturatedArithmeticOperationKernel::validate(ArithmeticOperation::ADD, input1, input2, output, policy); +} + +void CLArithmeticSubtraction::configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output, ConvertPolicy policy) +{ + auto k = arm_compute::support::cpp14::make_unique(); + k->configure(ArithmeticOperation::SUB, input1, input2, output, policy); + _kernel = std::move(k); + configure_border_handler(_border_handler, _kernel->border_size(), input1, input2, output); +} + +Status CLArithmeticSubtraction::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output, ConvertPolicy policy) +{ + ARM_COMPUTE_UNUSED(policy); + return CLSaturatedArithmeticOperationKernel::validate(ArithmeticOperation::SUB, input1, input2, output, policy); +} + +void CLArithmeticDivision::configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output) +{ + auto k = arm_compute::support::cpp14::make_unique(); + k->configure(ArithmeticOperation::DIV, input1, input2, output); + _kernel = std::move(k); + configure_border_handler(_border_handler, _kernel->border_size(), input1, input2, output); +} + +Status CLArithmeticDivision::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output) +{ + return CLArithmeticOperationKernel::validate(ArithmeticOperation::DIV, input1, input2, output); +} + +void CLElementwiseMax::configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output) +{ + auto k = arm_compute::support::cpp14::make_unique(); + k->configure(ArithmeticOperation::MAX, input1, input2, output); + _kernel = std::move(k); + configure_border_handler(_border_handler, _kernel->border_size(), input1, input2, output); +} + +Status CLElementwiseMax::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output) +{ + return CLArithmeticOperationKernel::validate(ArithmeticOperation::MAX, input1, input2, output); +} + +void CLElementwiseMin::configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output) +{ + auto k = arm_compute::support::cpp14::make_unique(); + k->configure(ArithmeticOperation::MIN, input1, input2, output); + _kernel = std::move(k); + configure_border_handler(_border_handler, _kernel->border_size(), input1, input2, output); +} + +Status CLElementwiseMin::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output) +{ + return CLArithmeticOperationKernel::validate(ArithmeticOperation::MIN, input1, input2, output); +} + +void CLElementwiseSquaredDiff::configure(ICLTensor *input1, ICLTensor *input2, ICLTensor *output) +{ + auto k = arm_compute::support::cpp14::make_unique(); + k->configure(ArithmeticOperation::SQUARED_DIFF, input1, input2, output); + _kernel = std::move(k); + configure_border_handler(_border_handler, _kernel->border_size(), input1, input2, output); +} + +Status CLElementwiseSquaredDiff::validate(const ITensorInfo *input1, const ITensorInfo *input2, const ITensorInfo *output) +{ + return CLArithmeticOperationKernel::validate(ArithmeticOperation::SQUARED_DIFF, input1, input2, output); +} +} // namespace arm_compute diff --git a/src/runtime/CL/functions/CLGEMMConvolutionLayer.cpp b/src/runtime/CL/functions/CLGEMMConvolutionLayer.cpp index 4694aa7f37..3a8b1a5891 100644 --- a/src/runtime/CL/functions/CLGEMMConvolutionLayer.cpp +++ b/src/runtime/CL/functions/CLGEMMConvolutionLayer.cpp @@ -242,7 +242,7 @@ void CLGEMMConvolutionLayer::configure(const ICLTensor *input, const ICLTensor * else if(_append_bias) { // Configure add bias kernel - _add_bias_kernel.configure(output, biases, output, ConvertPolicy::SATURATE); + _add_bias_kernel.configure(ArithmeticOperation::ADD, output, biases, output, ConvertPolicy::SATURATE); } // Create GEMM output tensor @@ -276,9 +276,9 @@ void CLGEMMConvolutionLayer::configure(const ICLTensor *input, const ICLTensor * { const QuantizationInfo output_quant_info = (output->info()->total_size() == 0) ? input->info()->quantization_info() : output->info()->quantization_info(); - const float multiplier = (input->info()->quantization_info().scale * weights->info()->quantization_info().scale) / output_quant_info.scale; - int output_multiplier = 0; - int output_shift = 0; + const float multiplier = (input->info()->quantization_info().scale * weights->info()->quantization_info().scale) / output_quant_info.scale; + int output_multiplier = 0; + int output_shift = 0; quantization::calculate_quantized_multiplier_less_than_one(multiplier, &output_multiplier, &output_shift); int min_activation = 0; @@ -432,7 +432,7 @@ Status CLGEMMConvolutionLayer::validate(const ITensorInfo *input, const ITensorI else if(append_bias) { // Validate add bias kernel - ARM_COMPUTE_RETURN_ON_ERROR(CLArithmeticAdditionKernel::validate(output, biases, output, ConvertPolicy::SATURATE)); + ARM_COMPUTE_RETURN_ON_ERROR(CLSaturatedArithmeticOperationKernel::validate(ArithmeticOperation::ADD, output, biases, output, ConvertPolicy::SATURATE)); } // Create GEMM output tensor @@ -459,9 +459,9 @@ Status CLGEMMConvolutionLayer::validate(const ITensorInfo *input, const ITensorI { const QuantizationInfo output_quant_info = (output->total_size() == 0) ? input->quantization_info() : output->quantization_info(); - const float multiplier = (input->quantization_info().scale * weights->quantization_info().scale) / output_quant_info.scale; - int output_multiplier = 0; - int output_shift = 0; + const float multiplier = (input->quantization_info().scale * weights->quantization_info().scale) / output_quant_info.scale; + int output_multiplier = 0; + int output_shift = 0; ARM_COMPUTE_RETURN_ON_ERROR(quantization::calculate_quantized_multiplier_less_than_one(multiplier, &output_multiplier, &output_shift)); diff --git a/src/runtime/CL/functions/CLLSTMLayer.cpp b/src/runtime/CL/functions/CLLSTMLayer.cpp index a89c4e3dbf..8a27d68d4a 100644 --- a/src/runtime/CL/functions/CLLSTMLayer.cpp +++ b/src/runtime/CL/functions/CLLSTMLayer.cpp @@ -110,7 +110,7 @@ void CLLSTMLayer::configure(const ICLTensor *input, _gemm_forget_gate.configure(output_state_in, &_forget_gate_out2, nullptr, &_forget_gate_out3, 1.f, 0.f); _forget_gate_out2.allocator()->allocate(); _memory_group.manage(&_forget_gate_out5); - _accum_forget_gate1.configure(&_forget_gate_out1, &_forget_gate_out3, &_forget_gate_out5, ConvertPolicy::SATURATE); + _accum_forget_gate1.configure(ArithmeticOperation::ADD, &_forget_gate_out1, &_forget_gate_out3, &_forget_gate_out5, ConvertPolicy::SATURATE); CLTensor *forget_gate_out = &_forget_gate_out5; if(lstm_params.has_peephole_opt()) @@ -139,7 +139,7 @@ void CLLSTMLayer::configure(const ICLTensor *input, { _memory_group.manage(&_input_gate_out1); _ones.allocator()->init(TensorInfo(cell_state_shape, 1, input->info()->data_type())); - _subtract_input_gate.configure(&_ones, &_forget_gate_out1, &_input_gate_out1, ConvertPolicy::SATURATE); + _subtract_input_gate.configure(ArithmeticOperation::SUB, &_ones, &_forget_gate_out1, &_input_gate_out1, ConvertPolicy::SATURATE); _ones.allocator()->allocate(); _run_cifg_opt = true; } @@ -160,7 +160,7 @@ void CLLSTMLayer::configure(const ICLTensor *input, _gemm_input_gate.configure(output_state_in, &_input_gate_out2, nullptr, &_input_gate_out3, 1.f, 0.f); _input_gate_out2.allocator()->allocate(); _memory_group.manage(&_input_gate_out4); - _accum_input_gate1.configure(&_input_gate_out1, &_input_gate_out3, &_input_gate_out4, ConvertPolicy::SATURATE); + _accum_input_gate1.configure(ArithmeticOperation::ADD, &_input_gate_out1, &_input_gate_out3, &_input_gate_out4, ConvertPolicy::SATURATE); if(_run_peephole_opt) { _memory_group.manage(&_input_gate_out5); @@ -190,14 +190,14 @@ void CLLSTMLayer::configure(const ICLTensor *input, _gemm_cell_state1.configure(output_state_in, &_cell_state_out2, nullptr, &_cell_state_out3, 1.f, 0.f); _cell_state_out2.allocator()->allocate(); _memory_group.manage(&_cell_state_out4); - _accum_cell_state1.configure(&_cell_state_out1, &_cell_state_out3, &_cell_state_out4, ConvertPolicy::SATURATE); + _accum_cell_state1.configure(ArithmeticOperation::ADD, &_cell_state_out1, &_cell_state_out3, &_cell_state_out4, ConvertPolicy::SATURATE); _activation_cell_state.configure(&_cell_state_out4, nullptr, activation_info); _memory_group.manage(&_cell_state_out5); _pixelwise_mul_cell_state1.configure(&_cell_state_out4, &_input_gate_out1, &_cell_state_out5, 1, ConvertPolicy::SATURATE, RoundingPolicy::TO_NEAREST_EVEN); _cell_state_out4.allocator()->allocate(); _pixelwise_mul_cell_state2.configure(&_forget_gate_out1, cell_state_in, &_cell_state_out3, 1, ConvertPolicy::SATURATE, RoundingPolicy::TO_NEAREST_EVEN); _forget_gate_out1.allocator()->allocate(); - _accum_cell_state2.configure(&_cell_state_out5, &_cell_state_out3, &_cell_state_out1, ConvertPolicy::SATURATE); + _accum_cell_state2.configure(ArithmeticOperation::ADD, &_cell_state_out5, &_cell_state_out3, &_cell_state_out1, ConvertPolicy::SATURATE); _cell_state_out3.allocator()->allocate(); _cell_state_out5.allocator()->allocate(); // Perform clipping @@ -223,7 +223,7 @@ void CLLSTMLayer::configure(const ICLTensor *input, _gemm_output.configure(output_state_in, &_output2, nullptr, &_output3, 1.f, 0.f); _output2.allocator()->allocate(); _memory_group.manage(&_output5); - _accum_output1.configure(&_output1, &_output3, &_output5, ConvertPolicy::SATURATE); + _accum_output1.configure(ArithmeticOperation::ADD, &_output1, &_output3, &_output5, ConvertPolicy::SATURATE); _output3.allocator()->allocate(); CLTensor *output_gate_out = &_output5; if(lstm_params.has_peephole_opt()) @@ -364,7 +364,7 @@ Status CLLSTMLayer::validate(const ITensorInfo *input, // Validate forget gate ARM_COMPUTE_RETURN_ON_ERROR(CLFullyConnectedLayer::validate(input, input_to_forget_weights, forget_gate_bias, &forget_gate)); ARM_COMPUTE_RETURN_ON_ERROR(CLGEMM::validate(output_state_in, &units_out_transposed_info, nullptr, &forget_gate, 1.f, 0.f, GEMMInfo())); - ARM_COMPUTE_RETURN_ON_ERROR(CLArithmeticAdditionKernel::validate(&forget_gate, &forget_gate, &forget_gate, ConvertPolicy::SATURATE)); + ARM_COMPUTE_RETURN_ON_ERROR(CLSaturatedArithmeticOperationKernel::validate(ArithmeticOperation::ADD, &forget_gate, &forget_gate, &forget_gate, ConvertPolicy::SATURATE)); if(lstm_params.has_peephole_opt()) { ARM_COMPUTE_RETURN_ON_ERROR(CLPixelWiseMultiplicationKernel::validate(cell_state_in, lstm_params.cell_to_forget_weights(), &forget_gate, 1, ConvertPolicy::SATURATE, RoundingPolicy::TO_NEAREST_EVEN)); @@ -396,7 +396,7 @@ Status CLLSTMLayer::validate(const ITensorInfo *input, } else { - ARM_COMPUTE_RETURN_ON_ERROR(CLArithmeticSubtractionKernel::validate(&forget_gate, &forget_gate, &forget_gate, ConvertPolicy::SATURATE)); + ARM_COMPUTE_RETURN_ON_ERROR(CLSaturatedArithmeticOperationKernel::validate(ArithmeticOperation::SUB, &forget_gate, &forget_gate, &forget_gate, ConvertPolicy::SATURATE)); } // Validate cell state @@ -544,4 +544,4 @@ void CLLSTMLayer::run() _concat_scratch_buffer.run(); _memory_group.release(); -} \ No newline at end of file +} diff --git a/src/runtime/CL/functions/CLLaplacianPyramid.cpp b/src/runtime/CL/functions/CLLaplacianPyramid.cpp index 7e5278f380..559b57fd8d 100644 --- a/src/runtime/CL/functions/CLLaplacianPyramid.cpp +++ b/src/runtime/CL/functions/CLLaplacianPyramid.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 ARM Limited. + * Copyright (c) 2017-2018 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -28,8 +28,8 @@ #include "arm_compute/core/TensorInfo.h" #include "arm_compute/core/Validate.h" #include "arm_compute/runtime/CL/CLTensor.h" -#include "arm_compute/runtime/CL/functions/CLArithmeticSubtraction.h" #include "arm_compute/runtime/CL/functions/CLDepthConvertLayer.h" +#include "arm_compute/runtime/CL/functions/CLElementwiseOperations.h" #include "arm_compute/runtime/CL/functions/CLGaussian5x5.h" #include "arm_compute/runtime/CL/functions/CLGaussianPyramid.h" #include "support/ToolchainSupport.h" diff --git a/src/runtime/CL/functions/CLRNNLayer.cpp b/src/runtime/CL/functions/CLRNNLayer.cpp index 1809e6e64e..63f00ac8ef 100644 --- a/src/runtime/CL/functions/CLRNNLayer.cpp +++ b/src/runtime/CL/functions/CLRNNLayer.cpp @@ -60,7 +60,7 @@ Status CLRNNLayer::validate(const ITensorInfo *input, const ITensorInfo *weights ARM_COMPUTE_RETURN_ON_ERROR(CLFullyConnectedLayer::validate(input, weights, bias, &shape_info)); ARM_COMPUTE_RETURN_ON_ERROR(CLGEMM::validate(hidden_state, recurrent_weights, nullptr, &shape_info, 1.f, 0.f)); - ARM_COMPUTE_RETURN_ON_ERROR(CLArithmeticAdditionKernel::validate(&shape_info, &shape_info, &shape_info, ConvertPolicy::SATURATE)); + ARM_COMPUTE_RETURN_ON_ERROR(CLSaturatedArithmeticOperationKernel::validate(ArithmeticOperation::ADD, &shape_info, &shape_info, &shape_info, ConvertPolicy::SATURATE)); ARM_COMPUTE_RETURN_ON_ERROR(CLActivationLayerKernel::validate(&shape_info, &shape_info, info)); return Status{}; @@ -90,7 +90,7 @@ void CLRNNLayer::configure(const ICLTensor *input, const ICLTensor *weights, con _add_output.allocator()->init(TensorInfo(shape, 1, input->info()->data_type())); _memory_group.manage(&_add_output); - _add_kernel.configure(&_fully_connected_out, &_gemm_output, &_add_output, ConvertPolicy::SATURATE); + _add_kernel.configure(ArithmeticOperation::ADD, &_fully_connected_out, &_gemm_output, &_add_output, ConvertPolicy::SATURATE); _fully_connected_out.allocator()->allocate(); _gemm_output.allocator()->allocate(); @@ -127,4 +127,4 @@ void CLRNNLayer::prepare() _is_prepared = true; } -} \ No newline at end of file +} diff --git a/tests/validation/CL/ArithmeticAddition.cpp b/tests/validation/CL/ArithmeticAddition.cpp index 09f1b7c5a9..6f7aa94521 100644 --- a/tests/validation/CL/ArithmeticAddition.cpp +++ b/tests/validation/CL/ArithmeticAddition.cpp @@ -24,7 +24,7 @@ #include "arm_compute/core/Types.h" #include "arm_compute/runtime/CL/CLTensor.h" #include "arm_compute/runtime/CL/CLTensorAllocator.h" -#include "arm_compute/runtime/CL/functions/CLArithmeticAddition.h" +#include "arm_compute/runtime/CL/functions/CLElementwiseOperations.h" #include "tests/CL/CLAccessor.h" #include "tests/PaddingCalculator.h" #include "tests/datasets/ConvertPolicyDataset.h" @@ -43,7 +43,7 @@ namespace validation { namespace { -constexpr unsigned int num_elems_processed_per_iteration = 8; +constexpr unsigned int num_elems_processed_per_iteration = 16; /** Input data sets **/ const auto ArithmeticAdditionU8Dataset = combine(combine(framework::dataset::make("DataType", DataType::U8), framework::dataset::make("DataType", DataType::U8)), framework::dataset::make("DataType", DataType::U8)); diff --git a/tests/validation/CL/ArithmeticDivision.cpp b/tests/validation/CL/ArithmeticDivision.cpp index 5d4fa1fd5e..87039d775f 100644 --- a/tests/validation/CL/ArithmeticDivision.cpp +++ b/tests/validation/CL/ArithmeticDivision.cpp @@ -24,7 +24,7 @@ #include "arm_compute/core/Types.h" #include "arm_compute/runtime/CL/CLTensor.h" #include "arm_compute/runtime/CL/CLTensorAllocator.h" -#include "arm_compute/runtime/CL/functions/CLArithmeticDivision.h" +#include "arm_compute/runtime/CL/functions/CLElementwiseOperations.h" #include "tests/CL/CLAccessor.h" #include "tests/PaddingCalculator.h" #include "tests/datasets/ConvertPolicyDataset.h" @@ -33,7 +33,7 @@ #include "tests/framework/Macros.h" #include "tests/framework/datasets/Datasets.h" #include "tests/validation/Validation.h" -#include "tests/validation/fixtures/ArithmeticDivisionFixture.h" +#include "tests/validation/fixtures/ElementwiseOperationsFixture.h" namespace arm_compute { @@ -45,6 +45,20 @@ namespace { RelativeTolerance tolerance_fp32(0.000001f); RelativeTolerance tolerance_fp16(0.001f); + +constexpr unsigned int num_elems_processed_per_iteration = 16; +/** Input data sets **/ +const auto ArithmeticDivisionU8Dataset = combine(combine(framework::dataset::make("DataType", DataType::U8), framework::dataset::make("DataType", DataType::U8)), framework::dataset::make("DataType", + DataType::U8)); +const auto ArithmeticDivisionQASYMM8Dataset = combine(combine(framework::dataset::make("DataType", DataType::QASYMM8), framework::dataset::make("DataType", DataType::QASYMM8)), + framework::dataset::make("DataType", + DataType::QASYMM8)); +const auto ArithmeticDivisionS16Dataset = combine(combine(framework::dataset::make("DataType", { DataType::U8, DataType::S16 }), framework::dataset::make("DataType", DataType::S16)), + framework::dataset::make("DataType", DataType::S16)); +const auto ArithmeticDivisionFP16Dataset = combine(combine(framework::dataset::make("DataType", DataType::F16), framework::dataset::make("DataType", DataType::F16)), + framework::dataset::make("DataType", DataType::F16)); +const auto ArithmeticDivisionFP32Dataset = combine(combine(framework::dataset::make("DataType", DataType::F32), framework::dataset::make("DataType", DataType::F32)), + framework::dataset::make("DataType", DataType::F32)); } // namespace TEST_SUITE(CL) @@ -53,25 +67,25 @@ TEST_SUITE(ArithmeticDivision) // *INDENT-OFF* // clang-format off DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip( - framework::dataset::make("Input1Info", { TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), // Wrong data type + framework::dataset::make("Input1Info", { TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), // Window shrink TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), // Invalid data type combination TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::F32), // Mismatching shapes - TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::F32), }), framework::dataset::make("Input2Info",{ TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S16), TensorInfo(TensorShape(48U, 11U, 2U), 1, DataType::F32), - TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::F32), })), framework::dataset::make("OutputInfo",{ TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S16), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), TensorInfo(TensorShape(48U, 11U, 2U), 1, DataType::F32), - TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::F32), })), - framework::dataset::make("Expected", { false, false, false, false, true })), + framework::dataset::make("Expected", { true, true, false, false, false})), input1_info, input2_info, output_info, expected) { ARM_COMPUTE_EXPECT(bool(CLArithmeticDivision::validate(&input1_info.clone()->set_is_resizable(false), &input2_info.clone()->set_is_resizable(false), &output_info.clone()->set_is_resizable(false))) == expected, framework::LogLevel::ERRORS); @@ -82,17 +96,128 @@ DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip( template using CLArithmeticDivisionFixture = ArithmeticDivisionValidationFixture; +TEST_SUITE(U8) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), + shape) +{ + // Create tensors + CLTensor ref_src1 = create_tensor(shape, DataType::U8); + CLTensor ref_src2 = create_tensor(shape, DataType::U8); + CLTensor dst = create_tensor(shape, DataType::U8); + + // Create and Configure function + CLArithmeticDivision add; + add.configure(&ref_src1, &ref_src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); + validate(ref_src1.info()->padding(), padding); + validate(ref_src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, CLArithmeticDivisionFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapes(), ArithmeticDivisionU8Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference); +} +TEST_SUITE_END() + +template +using CLArithmeticDivisionQuantizedFixture = ArithmeticDivisionValidationQuantizedFixture; + +TEST_SUITE(Quantized) +TEST_SUITE(QASYMM8) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), + shape) +{ + // Create tensors + CLTensor ref_src1 = create_tensor(shape, DataType::QASYMM8); + CLTensor ref_src2 = create_tensor(shape, DataType::QASYMM8); + CLTensor dst = create_tensor(shape, DataType::QASYMM8); + + // Create and Configure function + CLArithmeticDivision add; + add.configure(&ref_src1, &ref_src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); + validate(ref_src1.info()->padding(), padding); + validate(ref_src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, CLArithmeticDivisionQuantizedFixture, framework::DatasetMode::PRECOMMIT, combine(combine(combine(combine(datasets::SmallShapes(), + ArithmeticDivisionQASYMM8Dataset), + framework::dataset::make("QuantizationInfo", { QuantizationInfo(5.f / 255.f, 20) })), + framework::dataset::make("QuantizationInfo", { QuantizationInfo(2.f / 255.f, 10) })), + framework::dataset::make("QuantizationInfo", { QuantizationInfo(1.f / 255.f, 5) })) + + ) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32, 0.01); +} +TEST_SUITE_END() +TEST_SUITE_END() + +TEST_SUITE(S16) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), framework::dataset::make("DataType", { DataType::U8, DataType::S16 })), + shape, data_type) +{ + // Create tensors + CLTensor ref_src1 = create_tensor(shape, data_type); + CLTensor ref_src2 = create_tensor(shape, DataType::S16); + CLTensor dst = create_tensor(shape, DataType::S16); + + // Create and Configure function + CLArithmeticDivision add; + add.configure(&ref_src1, &ref_src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); + validate(ref_src1.info()->padding(), padding); + validate(ref_src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, CLArithmeticDivisionFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapes(), ArithmeticDivisionS16Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference); +} + +FIXTURE_DATA_TEST_CASE(RunLarge, CLArithmeticDivisionFixture, framework::DatasetMode::NIGHTLY, combine(datasets::LargeShapes(), ArithmeticDivisionS16Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference); +} +TEST_SUITE_END() + TEST_SUITE(Float) TEST_SUITE(FP16) -FIXTURE_DATA_TEST_CASE(RunSmall, CLArithmeticDivisionFixture, framework::DatasetMode::ALL, combine(datasets::SmallShapes(), framework::dataset::make("DataType", DataType::F16))) +FIXTURE_DATA_TEST_CASE(RunSmall, CLArithmeticDivisionFixture, framework::DatasetMode::ALL, combine(datasets::SmallShapes(), ArithmeticDivisionFP16Dataset)) { // Validate output - validate(CLAccessor(_target), _reference, tolerance_fp16); + validate(CLAccessor(_target), _reference, tolerance_fp16, 0.01); } -TEST_SUITE_END() // FP16 +TEST_SUITE_END() TEST_SUITE(FP32) -DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, concat(datasets::SmallShapes(), datasets::LargeShapes()), shape) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), + shape) { // Create tensors CLTensor ref_src1 = create_tensor(shape, DataType::F32); @@ -100,27 +225,27 @@ DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, concat(datasets::Smal CLTensor dst = create_tensor(shape, DataType::F32); // Create and Configure function - CLArithmeticDivision div; - div.configure(&ref_src1, &ref_src2, &dst); + CLArithmeticDivision add; + add.configure(&ref_src1, &ref_src2, &dst); // Validate valid region const ValidRegion valid_region = shape_to_valid_region(shape); validate(dst.info()->valid_region(), valid_region); // Validate padding - const PaddingSize padding = PaddingCalculator(shape.x(), 16).required_padding(); + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); validate(ref_src1.info()->padding(), padding); validate(ref_src2.info()->padding(), padding); validate(dst.info()->padding(), padding); } -FIXTURE_DATA_TEST_CASE(RunSmall, CLArithmeticDivisionFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapes(), framework::dataset::make("DataType", DataType::F32))) +FIXTURE_DATA_TEST_CASE(RunSmall, CLArithmeticDivisionFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapes(), ArithmeticDivisionFP32Dataset)) { // Validate output validate(CLAccessor(_target), _reference, tolerance_fp32); } -FIXTURE_DATA_TEST_CASE(RunLarge, CLArithmeticDivisionFixture, framework::DatasetMode::NIGHTLY, combine(datasets::LargeShapes(), framework::dataset::make("DataType", DataType::F32))) +FIXTURE_DATA_TEST_CASE(RunLarge, CLArithmeticDivisionFixture, framework::DatasetMode::NIGHTLY, combine(datasets::LargeShapes(), ArithmeticDivisionFP32Dataset)) { // Validate output validate(CLAccessor(_target), _reference, tolerance_fp32); @@ -130,23 +255,23 @@ template using CLArithmeticDivisionBroadcastFixture = ArithmeticDivisionBroadcastValidationFixture; FIXTURE_DATA_TEST_CASE(RunSmallBroadcast, CLArithmeticDivisionBroadcastFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapesBroadcast(), - framework::dataset::make("DataType", DataType::F32))) + ArithmeticDivisionFP32Dataset)) { // Validate output validate(CLAccessor(_target), _reference, tolerance_fp32); } FIXTURE_DATA_TEST_CASE(RunLargeBroadcast, CLArithmeticDivisionBroadcastFixture, framework::DatasetMode::NIGHTLY, combine(datasets::LargeShapesBroadcast(), - framework::dataset::make("DataType", DataType::F32))) + ArithmeticDivisionFP32Dataset)) { // Validate output validate(CLAccessor(_target), _reference, tolerance_fp32); } -TEST_SUITE_END() // FP32 -TEST_SUITE_END() // Float +TEST_SUITE_END() +TEST_SUITE_END() -TEST_SUITE_END() // ArithmeticDivision -TEST_SUITE_END() // CL +TEST_SUITE_END() +TEST_SUITE_END() } // namespace validation } // namespace test } // namespace arm_compute diff --git a/tests/validation/CL/ArithmeticSubtraction.cpp b/tests/validation/CL/ArithmeticSubtraction.cpp index cd13f42ec4..2cf410f373 100644 --- a/tests/validation/CL/ArithmeticSubtraction.cpp +++ b/tests/validation/CL/ArithmeticSubtraction.cpp @@ -24,7 +24,7 @@ #include "arm_compute/core/Types.h" #include "arm_compute/runtime/CL/CLTensor.h" #include "arm_compute/runtime/CL/CLTensorAllocator.h" -#include "arm_compute/runtime/CL/functions/CLArithmeticSubtraction.h" +#include "arm_compute/runtime/CL/functions/CLElementwiseOperations.h" #include "tests/CL/CLAccessor.h" #include "tests/PaddingCalculator.h" #include "tests/datasets/ConvertPolicyDataset.h" @@ -43,6 +43,7 @@ namespace validation { namespace { +constexpr unsigned int num_elems_processed_per_iteration = 16; /** Input data sets **/ const auto ArithmeticSubtractionU8Dataset = combine(combine(framework::dataset::make("DataType", DataType::U8), framework::dataset::make("DataType", DataType::U8)), framework::dataset::make("DataType", @@ -64,26 +65,26 @@ TEST_SUITE(ArithmeticSubtraction) // *INDENT-OFF* // clang-format off DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip( - framework::dataset::make("Input1Info", { TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), - TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), - TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), // Window shrink - TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), // Invalid data type combination - TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::F32), // Mismatching shapes - }), - framework::dataset::make("Input2Info",{ TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), - TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), - TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), - TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S16), - TensorInfo(TensorShape(48U, 11U, 2U), 1, DataType::F32), - })), - framework::dataset::make("OutputInfo",{ TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S16), - TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), - TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), - TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), - TensorInfo(TensorShape(48U, 11U, 2U), 1, DataType::F32), - })), - framework::dataset::make("Expected", { true, true, false, false, false})), - input1_info, input2_info, output_info, expected) + framework::dataset::make("Input1Info", { TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), // Window shrink + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), // Invalid data type combination + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::F32), // Mismatching shapes + }), + framework::dataset::make("Input2Info",{ TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S16), + TensorInfo(TensorShape(48U, 11U, 2U), 1, DataType::F32), + })), + framework::dataset::make("OutputInfo",{ TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S16), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(48U, 11U, 2U), 1, DataType::F32), + })), + framework::dataset::make("Expected", { true, true, false, false, false})), + input1_info, input2_info, output_info, expected) { ARM_COMPUTE_EXPECT(bool(CLArithmeticSubtraction::validate(&input1_info.clone()->set_is_resizable(false), &input2_info.clone()->set_is_resizable(false), &output_info.clone()->set_is_resizable(false), ConvertPolicy::WRAP)) == expected, framework::LogLevel::ERRORS); } @@ -103,15 +104,15 @@ DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(framework::da CLTensor dst = create_tensor(shape, DataType::U8); // Create and Configure function - CLArithmeticSubtraction sub; - sub.configure(&ref_src1, &ref_src2, &dst, policy); + CLArithmeticSubtraction add; + add.configure(&ref_src1, &ref_src2, &dst, policy); // Validate valid region const ValidRegion valid_region = shape_to_valid_region(shape); validate(dst.info()->valid_region(), valid_region); // Validate padding - const PaddingSize padding = PaddingCalculator(shape.x(), 16).required_padding(); + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); validate(ref_src1.info()->padding(), padding); validate(ref_src2.info()->padding(), padding); validate(dst.info()->padding(), padding); @@ -123,7 +124,7 @@ FIXTURE_DATA_TEST_CASE(RunSmall, CLArithmeticSubtractionFixture, framew // Validate output validate(CLAccessor(_target), _reference); } -TEST_SUITE_END() // U8 +TEST_SUITE_END() template using CLArithmeticSubtractionQuantizedFixture = ArithmeticSubtractionValidationQuantizedFixture; @@ -147,7 +148,7 @@ DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(framework::da validate(dst.info()->valid_region(), valid_region); // Validate padding - const PaddingSize padding = PaddingCalculator(shape.x(), 16).required_padding(); + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); validate(ref_src1.info()->padding(), padding); validate(ref_src2.info()->padding(), padding); validate(dst.info()->padding(), padding); @@ -165,8 +166,8 @@ FIXTURE_DATA_TEST_CASE(RunSmall, CLArithmeticSubtractionQuantizedFixture(shape, DataType::S16); // Create and Configure function - CLArithmeticSubtraction sub; - sub.configure(&ref_src1, &ref_src2, &dst, policy); + CLArithmeticSubtraction add; + add.configure(&ref_src1, &ref_src2, &dst, policy); // Validate valid region const ValidRegion valid_region = shape_to_valid_region(shape); validate(dst.info()->valid_region(), valid_region); // Validate padding - const PaddingSize padding = PaddingCalculator(shape.x(), 16).required_padding(); + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); validate(ref_src1.info()->padding(), padding); validate(ref_src2.info()->padding(), padding); validate(dst.info()->padding(), padding); @@ -206,7 +207,7 @@ FIXTURE_DATA_TEST_CASE(RunLarge, CLArithmeticSubtractionFixture, framew // Validate output validate(CLAccessor(_target), _reference); } -TEST_SUITE_END() // S16 +TEST_SUITE_END() TEST_SUITE(Float) TEST_SUITE(FP16) @@ -216,7 +217,7 @@ FIXTURE_DATA_TEST_CASE(RunSmall, CLArithmeticSubtractionFixture, framework // Validate output validate(CLAccessor(_target), _reference); } -TEST_SUITE_END() // FP16 +TEST_SUITE_END() TEST_SUITE(FP32) DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), framework::dataset::make("ConvertPolicy", { ConvertPolicy::SATURATE, ConvertPolicy::WRAP })), @@ -228,15 +229,15 @@ DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(framework::da CLTensor dst = create_tensor(shape, DataType::F32); // Create and Configure function - CLArithmeticSubtraction sub; - sub.configure(&ref_src1, &ref_src2, &dst, policy); + CLArithmeticSubtraction add; + add.configure(&ref_src1, &ref_src2, &dst, policy); // Validate valid region const ValidRegion valid_region = shape_to_valid_region(shape); validate(dst.info()->valid_region(), valid_region); // Validate padding - const PaddingSize padding = PaddingCalculator(shape.x(), 16).required_padding(); + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); validate(ref_src1.info()->padding(), padding); validate(ref_src2.info()->padding(), padding); validate(dst.info()->padding(), padding); @@ -274,11 +275,11 @@ FIXTURE_DATA_TEST_CASE(RunLargeBroadcast, CLArithmeticSubtractionBroadcastFixtur // Validate output validate(CLAccessor(_target), _reference); } -TEST_SUITE_END() // FP32 -TEST_SUITE_END() // Float +TEST_SUITE_END() +TEST_SUITE_END() -TEST_SUITE_END() // ArithmeticSubtraction -TEST_SUITE_END() // CL +TEST_SUITE_END() +TEST_SUITE_END() } // namespace validation } // namespace test -} // namespace arm_compute \ No newline at end of file +} // namespace arm_compute diff --git a/tests/validation/CL/ElementwiseMax.cpp b/tests/validation/CL/ElementwiseMax.cpp new file mode 100644 index 0000000000..894688fe2c --- /dev/null +++ b/tests/validation/CL/ElementwiseMax.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/CL/CLTensor.h" +#include "arm_compute/runtime/CL/CLTensorAllocator.h" +#include "arm_compute/runtime/CL/functions/CLElementwiseOperations.h" +#include "tests/CL/CLAccessor.h" +#include "tests/PaddingCalculator.h" +#include "tests/datasets/ConvertPolicyDataset.h" +#include "tests/datasets/ShapeDatasets.h" +#include "tests/framework/Asserts.h" +#include "tests/framework/Macros.h" +#include "tests/framework/datasets/Datasets.h" +#include "tests/validation/Validation.h" +#include "tests/validation/fixtures/ElementwiseOperationsFixture.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace +{ +RelativeTolerance tolerance_fp32(0.000001f); +RelativeTolerance tolerance_fp16(0.001f); + +constexpr unsigned int num_elems_processed_per_iteration = 16; +/** Input data sets **/ +const auto ElementwiseMaxU8Dataset = combine(combine(framework::dataset::make("DataType", DataType::U8), framework::dataset::make("DataType", DataType::U8)), framework::dataset::make("DataType", + DataType::U8)); +const auto ElementwiseMaxQASYMM8Dataset = combine(combine(framework::dataset::make("DataType", DataType::QASYMM8), framework::dataset::make("DataType", DataType::QASYMM8)), + framework::dataset::make("DataType", + DataType::QASYMM8)); +const auto ElementwiseMaxS16Dataset = combine(combine(framework::dataset::make("DataType", { DataType::U8, DataType::S16 }), framework::dataset::make("DataType", DataType::S16)), + framework::dataset::make("DataType", DataType::S16)); +const auto ElementwiseMaxFP16Dataset = combine(combine(framework::dataset::make("DataType", DataType::F16), framework::dataset::make("DataType", DataType::F16)), + framework::dataset::make("DataType", DataType::F16)); +const auto ElementwiseMaxFP32Dataset = combine(combine(framework::dataset::make("DataType", DataType::F32), framework::dataset::make("DataType", DataType::F32)), + framework::dataset::make("DataType", DataType::F32)); +} // namespace + +TEST_SUITE(CL) +TEST_SUITE(ElementwiseMax) + +// *INDENT-OFF* +// clang-format off +DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip( + framework::dataset::make("Input1Info", { TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), // Window shrink + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), // Invalid data type combination + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::F32), // Mismatching shapes + }), + framework::dataset::make("Input2Info",{ TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S16), + TensorInfo(TensorShape(48U, 11U, 2U), 1, DataType::F32), + })), + framework::dataset::make("OutputInfo",{ TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S16), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(48U, 11U, 2U), 1, DataType::F32), + })), + framework::dataset::make("Expected", { true, true, false, false, false})), + input1_info, input2_info, output_info, expected) +{ + ARM_COMPUTE_EXPECT(bool(CLElementwiseMax::validate(&input1_info.clone()->set_is_resizable(false), &input2_info.clone()->set_is_resizable(false), &output_info.clone()->set_is_resizable(false))) == expected, framework::LogLevel::ERRORS); +} +// clang-format on +// *INDENT-ON* + +template +using CLElementwiseMaxFixture = ElementwiseMaxValidationFixture; + +TEST_SUITE(U8) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), + shape) +{ + // Create tensors + CLTensor ref_src1 = create_tensor(shape, DataType::U8); + CLTensor ref_src2 = create_tensor(shape, DataType::U8); + CLTensor dst = create_tensor(shape, DataType::U8); + + // Create and Configure function + CLElementwiseMax add; + add.configure(&ref_src1, &ref_src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); + validate(ref_src1.info()->padding(), padding); + validate(ref_src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, CLElementwiseMaxFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapes(), ElementwiseMaxU8Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference); +} +TEST_SUITE_END() + +template +using CLElementwiseMaxQuantizedFixture = ElementwiseMaxValidationQuantizedFixture; + +TEST_SUITE(Quantized) +TEST_SUITE(QASYMM8) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), + shape) +{ + // Create tensors + CLTensor ref_src1 = create_tensor(shape, DataType::QASYMM8); + CLTensor ref_src2 = create_tensor(shape, DataType::QASYMM8); + CLTensor dst = create_tensor(shape, DataType::QASYMM8); + + // Create and Configure function + CLElementwiseMax add; + add.configure(&ref_src1, &ref_src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); + validate(ref_src1.info()->padding(), padding); + validate(ref_src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, CLElementwiseMaxQuantizedFixture, framework::DatasetMode::PRECOMMIT, combine(combine(combine(combine(datasets::SmallShapes(), + ElementwiseMaxQASYMM8Dataset), + framework::dataset::make("QuantizationInfo", { QuantizationInfo(5.f / 255.f, 20) })), + framework::dataset::make("QuantizationInfo", { QuantizationInfo(2.f / 255.f, 10) })), + framework::dataset::make("QuantizationInfo", { QuantizationInfo(1.f / 255.f, 5) })) + + ) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32, 0.01); +} +TEST_SUITE_END() +TEST_SUITE_END() + +TEST_SUITE(S16) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), framework::dataset::make("DataType", { DataType::U8, DataType::S16 })), + shape, data_type) +{ + // Create tensors + CLTensor ref_src1 = create_tensor(shape, data_type); + CLTensor ref_src2 = create_tensor(shape, DataType::S16); + CLTensor dst = create_tensor(shape, DataType::S16); + + // Create and Configure function + CLElementwiseMax add; + add.configure(&ref_src1, &ref_src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); + validate(ref_src1.info()->padding(), padding); + validate(ref_src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, CLElementwiseMaxFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapes(), ElementwiseMaxS16Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference); +} + +FIXTURE_DATA_TEST_CASE(RunLarge, CLElementwiseMaxFixture, framework::DatasetMode::NIGHTLY, combine(datasets::LargeShapes(), ElementwiseMaxS16Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference); +} +TEST_SUITE_END() + +TEST_SUITE(Float) +TEST_SUITE(FP16) +FIXTURE_DATA_TEST_CASE(RunSmall, CLElementwiseMaxFixture, framework::DatasetMode::ALL, combine(datasets::SmallShapes(), ElementwiseMaxFP16Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp16, 0.01); +} +TEST_SUITE_END() + +TEST_SUITE(FP32) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), + shape) +{ + // Create tensors + CLTensor ref_src1 = create_tensor(shape, DataType::F32); + CLTensor ref_src2 = create_tensor(shape, DataType::F32); + CLTensor dst = create_tensor(shape, DataType::F32); + + // Create and Configure function + CLElementwiseMax add; + add.configure(&ref_src1, &ref_src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); + validate(ref_src1.info()->padding(), padding); + validate(ref_src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, CLElementwiseMaxFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapes(), ElementwiseMaxFP32Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32); +} + +FIXTURE_DATA_TEST_CASE(RunLarge, CLElementwiseMaxFixture, framework::DatasetMode::NIGHTLY, combine(datasets::LargeShapes(), ElementwiseMaxFP32Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32); +} + +template +using CLElementwiseMaxBroadcastFixture = ElementwiseMaxBroadcastValidationFixture; + +FIXTURE_DATA_TEST_CASE(RunSmallBroadcast, CLElementwiseMaxBroadcastFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapesBroadcast(), + ElementwiseMaxFP32Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32); +} + +FIXTURE_DATA_TEST_CASE(RunLargeBroadcast, CLElementwiseMaxBroadcastFixture, framework::DatasetMode::NIGHTLY, combine(datasets::LargeShapesBroadcast(), + ElementwiseMaxFP32Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32); +} +TEST_SUITE_END() +TEST_SUITE_END() + +TEST_SUITE_END() +TEST_SUITE_END() +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/CL/ElementwiseMin.cpp b/tests/validation/CL/ElementwiseMin.cpp new file mode 100644 index 0000000000..05abfc853f --- /dev/null +++ b/tests/validation/CL/ElementwiseMin.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/CL/CLTensor.h" +#include "arm_compute/runtime/CL/CLTensorAllocator.h" +#include "arm_compute/runtime/CL/functions/CLElementwiseOperations.h" +#include "tests/CL/CLAccessor.h" +#include "tests/PaddingCalculator.h" +#include "tests/datasets/ConvertPolicyDataset.h" +#include "tests/datasets/ShapeDatasets.h" +#include "tests/framework/Asserts.h" +#include "tests/framework/Macros.h" +#include "tests/framework/datasets/Datasets.h" +#include "tests/validation/Validation.h" +#include "tests/validation/fixtures/ElementwiseOperationsFixture.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace +{ +RelativeTolerance tolerance_fp32(0.000001f); +RelativeTolerance tolerance_fp16(0.001f); + +constexpr unsigned int num_elems_processed_per_iteration = 16; +/** Input data sets **/ +const auto ElementwiseMinU8Dataset = combine(combine(framework::dataset::make("DataType", DataType::U8), framework::dataset::make("DataType", DataType::U8)), framework::dataset::make("DataType", + DataType::U8)); +const auto ElementwiseMinQASYMM8Dataset = combine(combine(framework::dataset::make("DataType", DataType::QASYMM8), framework::dataset::make("DataType", DataType::QASYMM8)), + framework::dataset::make("DataType", + DataType::QASYMM8)); +const auto ElementwiseMinS16Dataset = combine(combine(framework::dataset::make("DataType", { DataType::U8, DataType::S16 }), framework::dataset::make("DataType", DataType::S16)), + framework::dataset::make("DataType", DataType::S16)); +const auto ElementwiseMinFP16Dataset = combine(combine(framework::dataset::make("DataType", DataType::F16), framework::dataset::make("DataType", DataType::F16)), + framework::dataset::make("DataType", DataType::F16)); +const auto ElementwiseMinFP32Dataset = combine(combine(framework::dataset::make("DataType", DataType::F32), framework::dataset::make("DataType", DataType::F32)), + framework::dataset::make("DataType", DataType::F32)); +} // namespace + +TEST_SUITE(CL) +TEST_SUITE(ElementwiseMin) + +// *INDENT-OFF* +// clang-format off +DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip( + framework::dataset::make("Input1Info", { TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), // Window shrink + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), // Invalid data type combination + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::F32), // Mismatching shapes + }), + framework::dataset::make("Input2Info",{ TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S16), + TensorInfo(TensorShape(48U, 11U, 2U), 1, DataType::F32), + })), + framework::dataset::make("OutputInfo",{ TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S16), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(48U, 11U, 2U), 1, DataType::F32), + })), + framework::dataset::make("Expected", { true, true, false, false, false})), + input1_info, input2_info, output_info, expected) +{ + ARM_COMPUTE_EXPECT(bool(CLElementwiseMin::validate(&input1_info.clone()->set_is_resizable(false), &input2_info.clone()->set_is_resizable(false), &output_info.clone()->set_is_resizable(false))) == expected, framework::LogLevel::ERRORS); +} +// clang-format on +// *INDENT-ON* + +template +using CLElementwiseMinFixture = ElementwiseMinValidationFixture; + +TEST_SUITE(U8) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), + shape) +{ + // Create tensors + CLTensor ref_src1 = create_tensor(shape, DataType::U8); + CLTensor ref_src2 = create_tensor(shape, DataType::U8); + CLTensor dst = create_tensor(shape, DataType::U8); + + // Create and Configure function + CLElementwiseMin add; + add.configure(&ref_src1, &ref_src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); + validate(ref_src1.info()->padding(), padding); + validate(ref_src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, CLElementwiseMinFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapes(), ElementwiseMinU8Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference); +} +TEST_SUITE_END() + +template +using CLElementwiseMinQuantizedFixture = ElementwiseMinValidationQuantizedFixture; + +TEST_SUITE(Quantized) +TEST_SUITE(QASYMM8) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), + shape) +{ + // Create tensors + CLTensor ref_src1 = create_tensor(shape, DataType::QASYMM8); + CLTensor ref_src2 = create_tensor(shape, DataType::QASYMM8); + CLTensor dst = create_tensor(shape, DataType::QASYMM8); + + // Create and Configure function + CLElementwiseMin add; + add.configure(&ref_src1, &ref_src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); + validate(ref_src1.info()->padding(), padding); + validate(ref_src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, CLElementwiseMinQuantizedFixture, framework::DatasetMode::PRECOMMIT, combine(combine(combine(combine(datasets::SmallShapes(), + ElementwiseMinQASYMM8Dataset), + framework::dataset::make("QuantizationInfo", { QuantizationInfo(5.f / 255.f, 20) })), + framework::dataset::make("QuantizationInfo", { QuantizationInfo(2.f / 255.f, 10) })), + framework::dataset::make("QuantizationInfo", { QuantizationInfo(1.f / 255.f, 5) })) + + ) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32, 0.01); +} +TEST_SUITE_END() +TEST_SUITE_END() + +TEST_SUITE(S16) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), framework::dataset::make("DataType", { DataType::U8, DataType::S16 })), + shape, data_type) +{ + // Create tensors + CLTensor ref_src1 = create_tensor(shape, data_type); + CLTensor ref_src2 = create_tensor(shape, DataType::S16); + CLTensor dst = create_tensor(shape, DataType::S16); + + // Create and Configure function + CLElementwiseMin add; + add.configure(&ref_src1, &ref_src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); + validate(ref_src1.info()->padding(), padding); + validate(ref_src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, CLElementwiseMinFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapes(), ElementwiseMinS16Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference); +} + +FIXTURE_DATA_TEST_CASE(RunLarge, CLElementwiseMinFixture, framework::DatasetMode::NIGHTLY, combine(datasets::LargeShapes(), ElementwiseMinS16Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference); +} +TEST_SUITE_END() + +TEST_SUITE(Float) +TEST_SUITE(FP16) +FIXTURE_DATA_TEST_CASE(RunSmall, CLElementwiseMinFixture, framework::DatasetMode::ALL, combine(datasets::SmallShapes(), ElementwiseMinFP16Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp16, 0.01); +} +TEST_SUITE_END() + +TEST_SUITE(FP32) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), + shape) +{ + // Create tensors + CLTensor ref_src1 = create_tensor(shape, DataType::F32); + CLTensor ref_src2 = create_tensor(shape, DataType::F32); + CLTensor dst = create_tensor(shape, DataType::F32); + + // Create and Configure function + CLElementwiseMin add; + add.configure(&ref_src1, &ref_src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); + validate(ref_src1.info()->padding(), padding); + validate(ref_src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, CLElementwiseMinFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapes(), ElementwiseMinFP32Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32); +} + +FIXTURE_DATA_TEST_CASE(RunLarge, CLElementwiseMinFixture, framework::DatasetMode::NIGHTLY, combine(datasets::LargeShapes(), ElementwiseMinFP32Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32); +} + +template +using CLElementwiseMinBroadcastFixture = ElementwiseMinBroadcastValidationFixture; + +FIXTURE_DATA_TEST_CASE(RunSmallBroadcast, CLElementwiseMinBroadcastFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapesBroadcast(), + ElementwiseMinFP32Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32); +} + +FIXTURE_DATA_TEST_CASE(RunLargeBroadcast, CLElementwiseMinBroadcastFixture, framework::DatasetMode::NIGHTLY, combine(datasets::LargeShapesBroadcast(), + ElementwiseMinFP32Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32); +} +TEST_SUITE_END() +TEST_SUITE_END() + +TEST_SUITE_END() +TEST_SUITE_END() +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/CL/ElementwiseSquaredDiff.cpp b/tests/validation/CL/ElementwiseSquaredDiff.cpp new file mode 100644 index 0000000000..c00f95b885 --- /dev/null +++ b/tests/validation/CL/ElementwiseSquaredDiff.cpp @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "arm_compute/core/Types.h" +#include "arm_compute/runtime/CL/CLTensor.h" +#include "arm_compute/runtime/CL/CLTensorAllocator.h" +#include "arm_compute/runtime/CL/functions/CLElementwiseOperations.h" +#include "tests/CL/CLAccessor.h" +#include "tests/PaddingCalculator.h" +#include "tests/datasets/ConvertPolicyDataset.h" +#include "tests/datasets/ShapeDatasets.h" +#include "tests/framework/Asserts.h" +#include "tests/framework/Macros.h" +#include "tests/framework/datasets/Datasets.h" +#include "tests/validation/Validation.h" +#include "tests/validation/fixtures/ElementwiseOperationsFixture.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace +{ +RelativeTolerance tolerance_fp32(0.000001f); +RelativeTolerance tolerance_fp16(0.001f); + +constexpr unsigned int num_elems_processed_per_iteration = 16; +/** Input data sets **/ +const auto ElementwiseSquaredDiffU8Dataset = combine(combine(framework::dataset::make("DataType", DataType::U8), framework::dataset::make("DataType", DataType::U8)), + framework::dataset::make("DataType", + DataType::U8)); +const auto ElementwiseSquaredDiffQASYMM8Dataset = combine(combine(framework::dataset::make("DataType", DataType::QASYMM8), framework::dataset::make("DataType", DataType::QASYMM8)), + framework::dataset::make("DataType", + DataType::QASYMM8)); +const auto ElementwiseSquaredDiffS16Dataset = combine(combine(framework::dataset::make("DataType", { DataType::U8, DataType::S16 }), framework::dataset::make("DataType", DataType::S16)), + framework::dataset::make("DataType", DataType::S16)); +const auto ElementwiseSquaredDiffFP16Dataset = combine(combine(framework::dataset::make("DataType", DataType::F16), framework::dataset::make("DataType", DataType::F16)), + framework::dataset::make("DataType", DataType::F16)); +const auto ElementwiseSquaredDiffFP32Dataset = combine(combine(framework::dataset::make("DataType", DataType::F32), framework::dataset::make("DataType", DataType::F32)), + framework::dataset::make("DataType", DataType::F32)); +} // namespace + +TEST_SUITE(CL) +TEST_SUITE(ElementwiseSquaredDiff) + +// *INDENT-OFF* +// clang-format off +DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip( + framework::dataset::make("Input1Info", { TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), // Window shrink + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), // Invalid data type combination + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::F32), // Mismatching shapes + }), + framework::dataset::make("Input2Info",{ TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S16), + TensorInfo(TensorShape(48U, 11U, 2U), 1, DataType::F32), + })), + framework::dataset::make("OutputInfo",{ TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::S16), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(32U, 13U, 2U), 1, DataType::U8), + TensorInfo(TensorShape(48U, 11U, 2U), 1, DataType::F32), + })), + framework::dataset::make("Expected", { true, true, false, false, false})), + input1_info, input2_info, output_info, expected) +{ + ARM_COMPUTE_EXPECT(bool(CLElementwiseSquaredDiff::validate(&input1_info.clone()->set_is_resizable(false), &input2_info.clone()->set_is_resizable(false), &output_info.clone()->set_is_resizable(false))) == expected, framework::LogLevel::ERRORS); +} +// clang-format on +// *INDENT-ON* + +template +using CLElementwiseSquaredDiffFixture = ElementwiseSquaredDiffValidationFixture; + +TEST_SUITE(U8) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), + shape) +{ + // Create tensors + CLTensor ref_src1 = create_tensor(shape, DataType::U8); + CLTensor ref_src2 = create_tensor(shape, DataType::U8); + CLTensor dst = create_tensor(shape, DataType::U8); + + // Create and Configure function + CLElementwiseSquaredDiff add; + add.configure(&ref_src1, &ref_src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); + validate(ref_src1.info()->padding(), padding); + validate(ref_src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, CLElementwiseSquaredDiffFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapes(), ElementwiseSquaredDiffU8Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference); +} +TEST_SUITE_END() + +template +using CLElementwiseSquaredDiffQuantizedFixture = ElementwiseSquaredDiffValidationQuantizedFixture; + +TEST_SUITE(Quantized) +TEST_SUITE(QASYMM8) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), + shape) +{ + // Create tensors + CLTensor ref_src1 = create_tensor(shape, DataType::QASYMM8); + CLTensor ref_src2 = create_tensor(shape, DataType::QASYMM8); + CLTensor dst = create_tensor(shape, DataType::QASYMM8); + + // Create and Configure function + CLElementwiseSquaredDiff add; + add.configure(&ref_src1, &ref_src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); + validate(ref_src1.info()->padding(), padding); + validate(ref_src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, CLElementwiseSquaredDiffQuantizedFixture, framework::DatasetMode::PRECOMMIT, combine(combine(combine(combine(datasets::SmallShapes(), + ElementwiseSquaredDiffQASYMM8Dataset), + framework::dataset::make("QuantizationInfo", { QuantizationInfo(5.f / 255.f, 20) })), + framework::dataset::make("QuantizationInfo", { QuantizationInfo(2.f / 255.f, 10) })), + framework::dataset::make("QuantizationInfo", { QuantizationInfo(1.f / 255.f, 5) })) + + ) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32, 0.01); +} +TEST_SUITE_END() +TEST_SUITE_END() + +TEST_SUITE(S16) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), framework::dataset::make("DataType", { DataType::U8, DataType::S16 })), + shape, data_type) +{ + // Create tensors + CLTensor ref_src1 = create_tensor(shape, data_type); + CLTensor ref_src2 = create_tensor(shape, DataType::S16); + CLTensor dst = create_tensor(shape, DataType::S16); + + // Create and Configure function + CLElementwiseSquaredDiff add; + add.configure(&ref_src1, &ref_src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); + validate(ref_src1.info()->padding(), padding); + validate(ref_src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, CLElementwiseSquaredDiffFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapes(), ElementwiseSquaredDiffS16Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference); +} + +FIXTURE_DATA_TEST_CASE(RunLarge, CLElementwiseSquaredDiffFixture, framework::DatasetMode::NIGHTLY, combine(datasets::LargeShapes(), ElementwiseSquaredDiffS16Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference); +} +TEST_SUITE_END() + +TEST_SUITE(Float) +TEST_SUITE(FP16) +FIXTURE_DATA_TEST_CASE(RunSmall, CLElementwiseSquaredDiffFixture, framework::DatasetMode::ALL, combine(datasets::SmallShapes(), ElementwiseSquaredDiffFP16Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp16, 0.01); +} +TEST_SUITE_END() + +TEST_SUITE(FP32) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, framework::dataset::concat(datasets::SmallShapes(), datasets::LargeShapes()), + shape) +{ + // Create tensors + CLTensor ref_src1 = create_tensor(shape, DataType::F32); + CLTensor ref_src2 = create_tensor(shape, DataType::F32); + CLTensor dst = create_tensor(shape, DataType::F32); + + // Create and Configure function + CLElementwiseSquaredDiff add; + add.configure(&ref_src1, &ref_src2, &dst); + + // Validate valid region + const ValidRegion valid_region = shape_to_valid_region(shape); + validate(dst.info()->valid_region(), valid_region); + + // Validate padding + const PaddingSize padding = PaddingCalculator(shape.x(), num_elems_processed_per_iteration).required_padding(); + validate(ref_src1.info()->padding(), padding); + validate(ref_src2.info()->padding(), padding); + validate(dst.info()->padding(), padding); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, CLElementwiseSquaredDiffFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapes(), ElementwiseSquaredDiffFP32Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32); +} + +FIXTURE_DATA_TEST_CASE(RunLarge, CLElementwiseSquaredDiffFixture, framework::DatasetMode::NIGHTLY, combine(datasets::LargeShapes(), ElementwiseSquaredDiffFP32Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32); +} + +template +using CLElementwiseSquaredDiffBroadcastFixture = ElementwiseSquaredDiffBroadcastValidationFixture; + +FIXTURE_DATA_TEST_CASE(RunSmallBroadcast, CLElementwiseSquaredDiffBroadcastFixture, framework::DatasetMode::PRECOMMIT, combine(datasets::SmallShapesBroadcast(), + ElementwiseSquaredDiffFP32Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32); +} + +FIXTURE_DATA_TEST_CASE(RunLargeBroadcast, CLElementwiseSquaredDiffBroadcastFixture, framework::DatasetMode::NIGHTLY, combine(datasets::LargeShapesBroadcast(), + ElementwiseSquaredDiffFP32Dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32); +} +TEST_SUITE_END() +TEST_SUITE_END() + +TEST_SUITE_END() +TEST_SUITE_END() +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/fixtures/ElementwiseOperationsFixture.h b/tests/validation/fixtures/ElementwiseOperationsFixture.h new file mode 100644 index 0000000000..b051c858c2 --- /dev/null +++ b/tests/validation/fixtures/ElementwiseOperationsFixture.h @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef ARM_COMPUTE_TEST_ELEMENTWISE_OPERATIONS_FIXTURE +#define ARM_COMPUTE_TEST_ELEMENTWISE_OPERATIONS_FIXTURE + +#include "arm_compute/core/TensorShape.h" +#include "arm_compute/core/Types.h" +#include "tests/AssetsLibrary.h" +#include "tests/Globals.h" +#include "tests/IAccessor.h" +#include "tests/framework/Asserts.h" +#include "tests/framework/Fixture.h" +#include "tests/validation/Helpers.h" +#include "tests/validation/reference/ElementwiseOperations.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +template +class ArithmeticOperationsGenericFixture : public framework::Fixture +{ +public: + template + void setup(ArithmeticOperation op, const TensorShape &shape0, const TensorShape &shape1, + DataType data_type0, DataType data_type1, DataType output_data_type, + QuantizationInfo qinfo0, QuantizationInfo qinfo1, QuantizationInfo qinfo_out) + { + _op = op; + _target = compute_target(shape0, shape1, data_type0, data_type1, output_data_type, qinfo0, qinfo1, qinfo_out); + _reference = compute_reference(shape0, shape1, data_type0, data_type1, output_data_type, qinfo0, qinfo1, qinfo_out); + } + +protected: + template + void fill(U &&tensor, int i) + { + library->fill_tensor_uniform(tensor, i); + } + + TensorType compute_target(const TensorShape &shape0, const TensorShape &shape1, DataType data_type0, DataType data_type1, DataType output_data_type, + QuantizationInfo qinfo0, QuantizationInfo qinfo1, QuantizationInfo qinfo_out) + { + // Create tensors + TensorType ref_src1 = create_tensor(shape0, data_type0, 1, qinfo0); + TensorType ref_src2 = create_tensor(shape1, data_type1, 1, qinfo1); + TensorType dst = create_tensor(TensorShape::broadcast_shape(shape0, shape1), output_data_type, 1, qinfo_out); + + // Create and configure function + FunctionType elem_op; + elem_op.configure(&ref_src1, &ref_src2, &dst); + + ARM_COMPUTE_EXPECT(ref_src1.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(ref_src2.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(dst.info()->is_resizable(), framework::LogLevel::ERRORS); + + // Allocate tensors + ref_src1.allocator()->allocate(); + ref_src2.allocator()->allocate(); + dst.allocator()->allocate(); + + ARM_COMPUTE_EXPECT(!ref_src1.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(!ref_src2.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(!dst.info()->is_resizable(), framework::LogLevel::ERRORS); + + // Fill tensors + fill(AccessorType(ref_src1), 0); + fill(AccessorType(ref_src2), 1); + + // Compute function + elem_op.run(); + + return dst; + } + + SimpleTensor compute_reference(const TensorShape &shape0, const TensorShape &shape1, + DataType data_type0, DataType data_type1, DataType output_data_type, + QuantizationInfo qinfo0, QuantizationInfo qinfo1, QuantizationInfo qinfo_out) + { + // Create reference + SimpleTensor ref_src1{ shape0, data_type0, 1, qinfo0 }; + SimpleTensor ref_src2{ shape1, data_type1, 1, qinfo1 }; + SimpleTensor ref_dst{ TensorShape::broadcast_shape(shape0, shape1), output_data_type, 1, qinfo_out }; + + // Fill reference + fill(ref_src1, 0); + fill(ref_src2, 1); + + return reference::arithmetic_operation(_op, ref_src1, ref_src2, ref_dst); + } + + TensorType _target{}; + SimpleTensor _reference{}; + ArithmeticOperation _op{ ArithmeticOperation::ADD }; +}; + +template +class ArithmeticDivisionBroadcastValidationFixture : public ArithmeticOperationsGenericFixture +{ +public: + template + void setup(const TensorShape &shape0, const TensorShape &shape1, DataType data_type0, DataType data_type1, DataType output_data_type) + { + ArithmeticOperationsGenericFixture::setup(ArithmeticOperation::DIV, shape0, shape1, + data_type0, data_type1, output_data_type, + QuantizationInfo(), QuantizationInfo(), QuantizationInfo()); + } +}; + +template +class ArithmeticDivisionValidationFixture : public ArithmeticOperationsGenericFixture +{ +public: + template + void setup(const TensorShape &shape, DataType data_type0, DataType data_type1, DataType output_data_type) + { + ArithmeticOperationsGenericFixture::setup(ArithmeticOperation::DIV, shape, shape, + data_type0, data_type1, output_data_type, + QuantizationInfo(), QuantizationInfo(), QuantizationInfo()); + } +}; + +template +class ArithmeticDivisionValidationQuantizedFixture : public ArithmeticOperationsGenericFixture +{ +public: + template + void setup(const TensorShape &shape, DataType data_type0, DataType data_type1, DataType output_data_type, + QuantizationInfo qinfo0, QuantizationInfo qinfo1, QuantizationInfo qinfo_out) + + { + ArithmeticOperationsGenericFixture::setup(ArithmeticOperation::DIV, shape, shape, + data_type0, data_type1, output_data_type, + qinfo0, qinfo1, qinfo_out); + } +}; + +template +class ElementwiseMaxBroadcastValidationFixture : public ArithmeticOperationsGenericFixture +{ +public: + template + void setup(const TensorShape &shape0, const TensorShape &shape1, DataType data_type0, DataType data_type1, DataType output_data_type) + { + ArithmeticOperationsGenericFixture::setup(ArithmeticOperation::MAX, shape0, shape1, + data_type0, data_type1, output_data_type, + QuantizationInfo(), QuantizationInfo(), QuantizationInfo()); + } +}; + +template +class ElementwiseMaxValidationFixture : public ArithmeticOperationsGenericFixture +{ +public: + template + void setup(const TensorShape &shape, DataType data_type0, DataType data_type1, DataType output_data_type) + { + ArithmeticOperationsGenericFixture::setup(ArithmeticOperation::MAX, shape, shape, + data_type0, data_type1, output_data_type, + QuantizationInfo(), QuantizationInfo(), QuantizationInfo()); + } +}; + +template +class ElementwiseMaxValidationQuantizedFixture : public ArithmeticOperationsGenericFixture +{ +public: + template + void setup(const TensorShape &shape, DataType data_type0, DataType data_type1, DataType output_data_type, + QuantizationInfo qinfo0, QuantizationInfo qinfo1, QuantizationInfo qinfo_out) + + { + ArithmeticOperationsGenericFixture::setup(ArithmeticOperation::MAX, shape, shape, + data_type0, data_type1, output_data_type, + qinfo0, qinfo1, qinfo_out); + } +}; + +template +class ElementwiseMinBroadcastValidationFixture : public ArithmeticOperationsGenericFixture +{ +public: + template + void setup(const TensorShape &shape0, const TensorShape &shape1, DataType data_type0, DataType data_type1, DataType output_data_type) + { + ArithmeticOperationsGenericFixture::setup(ArithmeticOperation::MIN, shape0, shape1, + data_type0, data_type1, output_data_type, + QuantizationInfo(), QuantizationInfo(), QuantizationInfo()); + } +}; + +template +class ElementwiseMinValidationFixture : public ArithmeticOperationsGenericFixture +{ +public: + template + void setup(const TensorShape &shape, DataType data_type0, DataType data_type1, DataType output_data_type) + { + ArithmeticOperationsGenericFixture::setup(ArithmeticOperation::MIN, shape, shape, + data_type0, data_type1, output_data_type, + QuantizationInfo(), QuantizationInfo(), QuantizationInfo()); + } +}; + +template +class ElementwiseMinValidationQuantizedFixture : public ArithmeticOperationsGenericFixture +{ +public: + template + void setup(const TensorShape &shape, DataType data_type0, DataType data_type1, DataType output_data_type, + QuantizationInfo qinfo0, QuantizationInfo qinfo1, QuantizationInfo qinfo_out) + + { + ArithmeticOperationsGenericFixture::setup(ArithmeticOperation::MIN, shape, shape, + data_type0, data_type1, output_data_type, + qinfo0, qinfo1, qinfo_out); + } +}; + +template +class ElementwiseSquaredDiffBroadcastValidationFixture : public ArithmeticOperationsGenericFixture +{ +public: + template + void setup(const TensorShape &shape0, const TensorShape &shape1, DataType data_type0, DataType data_type1, DataType output_data_type) + { + ArithmeticOperationsGenericFixture::setup(ArithmeticOperation::SQUARED_DIFF, shape0, shape1, + data_type0, data_type1, output_data_type, + QuantizationInfo(), QuantizationInfo(), QuantizationInfo()); + } +}; + +template +class ElementwiseSquaredDiffValidationFixture : public ArithmeticOperationsGenericFixture +{ +public: + template + void setup(const TensorShape &shape, DataType data_type0, DataType data_type1, DataType output_data_type) + { + ArithmeticOperationsGenericFixture::setup(ArithmeticOperation::SQUARED_DIFF, shape, shape, + data_type0, data_type1, output_data_type, + QuantizationInfo(), QuantizationInfo(), QuantizationInfo()); + } +}; + +template +class ElementwiseSquaredDiffValidationQuantizedFixture : public ArithmeticOperationsGenericFixture +{ +public: + template + void setup(const TensorShape &shape, DataType data_type0, DataType data_type1, DataType output_data_type, + QuantizationInfo qinfo0, QuantizationInfo qinfo1, QuantizationInfo qinfo_out) + + { + ArithmeticOperationsGenericFixture::setup(ArithmeticOperation::SQUARED_DIFF, shape, shape, + data_type0, data_type1, output_data_type, + qinfo0, qinfo1, qinfo_out); + } +}; +} // namespace validation +} // namespace test +} // namespace arm_compute +#endif /* ARM_COMPUTE_TEST_ARITHMETIC_OPERATIONS_FIXTURE */ diff --git a/tests/validation/reference/ElementwiseOperations.cpp b/tests/validation/reference/ElementwiseOperations.cpp new file mode 100644 index 0000000000..fe0467fe5e --- /dev/null +++ b/tests/validation/reference/ElementwiseOperations.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "ElementwiseOperations.h" + +#include "arm_compute/core/Types.h" +#include "tests/validation/Helpers.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace reference +{ +namespace +{ +template +T arithm_op(ArithmeticOperation op, T src1, T src2, ConvertPolicy convert_policy) +{ + using intermediate_type = typename common_promoted_signed_type::intermediate_type; + + intermediate_type val; + + if(op == ArithmeticOperation::ADD) + { + val = static_cast(src1) + static_cast(src2); + } + else if(op == ArithmeticOperation::SUB) + { + val = static_cast(src1) - static_cast(src2); + } + else if(op == ArithmeticOperation::MIN) + { + val = std::min(static_cast(src1), static_cast(src2)); + } + else if(op == ArithmeticOperation::MAX) + { + val = std::max(static_cast(src1), static_cast(src2)); + } + else if(op == ArithmeticOperation::SQUARED_DIFF) + { + intermediate_type tmp = (static_cast(src1) - static_cast(src2)); + val = tmp * tmp; + } + else if(op == ArithmeticOperation::DIV) + { + val = (static_cast(src1) / static_cast(src2)); + } + else + { + ARM_COMPUTE_ERROR("Not handled"); + } + + T result; + if(op == ArithmeticOperation::ADD || op == ArithmeticOperation::SUB) + { + result = (convert_policy == ConvertPolicy::SATURATE) ? saturate_cast(val) : static_cast(val); + } + else + { + result = static_cast(val); + } + return result; +} + +template +struct BroadcastUnroll +{ + template + static void unroll(ArithmeticOperation op, const SimpleTensor &src1, const SimpleTensor &src2, SimpleTensor &dst, + ConvertPolicy convert_policy, Coordinates &id_src1, Coordinates &id_src2, Coordinates &id_dst) + { + const bool src1_is_broadcast = (src1.shape()[dim - 1] != dst.shape()[dim - 1]); + const bool src2_is_broadcast = (src2.shape()[dim - 1] != dst.shape()[dim - 1]); + + id_src1.set(dim - 1, 0); + id_src2.set(dim - 1, 0); + id_dst.set(dim - 1, 0); + + for(size_t i = 0; i < dst.shape()[dim - 1]; ++i, ++id_dst[dim - 1]) + { + BroadcastUnroll < dim - 1 >::unroll(op, src1, src2, dst, convert_policy, id_src1, id_src2, id_dst); + + id_src1[dim - 1] += !src1_is_broadcast; + id_src2[dim - 1] += !src2_is_broadcast; + } + } +}; + +template <> +struct BroadcastUnroll<0> +{ + template + static void unroll(ArithmeticOperation op, const SimpleTensor &src1, const SimpleTensor &src2, SimpleTensor &dst, + ConvertPolicy convert_policy, Coordinates &id_src1, Coordinates &id_src2, Coordinates &id_dst) + { + dst[coord2index(dst.shape(), id_dst)] = arithm_op(op, src1[coord2index(src1.shape(), id_src1)], src2[coord2index(src2.shape(), id_src2)], convert_policy); + } +}; +} // namespace + +template +SimpleTensor arithmetic_operation(ArithmeticOperation op, const SimpleTensor &src1, const SimpleTensor &src2, SimpleTensor &dst, ConvertPolicy convert_policy) +{ + Coordinates id_src1, id_src2, id_dst; + + BroadcastUnroll::unroll(op, src1, src2, dst, convert_policy, id_src1, id_src2, id_dst); + + return dst; +} + +template <> +SimpleTensor arithmetic_operation(ArithmeticOperation op, const SimpleTensor &src1, const SimpleTensor &src2, SimpleTensor &dst, ConvertPolicy convert_policy) +{ + if(dst.data_type() == DataType::QASYMM8) + { + SimpleTensor src1_tmp = convert_from_asymmetric(src1); + SimpleTensor src2_tmp = convert_from_asymmetric(src2); + SimpleTensor dst_tmp(TensorShape::broadcast_shape(src1.shape(), src2.shape()), dst.data_type()); + + Coordinates id_src1, id_src2, id_dst; + + BroadcastUnroll::unroll(op, src1_tmp, src2_tmp, dst_tmp, convert_policy, id_src1, id_src2, id_dst); + + dst = convert_to_asymmetric(dst_tmp, dst.quantization_info()); + return dst; + } + else + { + // DataType::U8 + Coordinates id_src1, id_src2, id_dst; + + BroadcastUnroll::unroll(op, src1, src2, dst, convert_policy, id_src1, id_src2, id_dst); + + return dst; + } +} + +template SimpleTensor arithmetic_operation(ArithmeticOperation op, const SimpleTensor &src1, const SimpleTensor &src2, SimpleTensor &dst, + ConvertPolicy convert_policy); +template SimpleTensor arithmetic_operation(ArithmeticOperation op, const SimpleTensor &src1, const SimpleTensor &src2, SimpleTensor &dst, + ConvertPolicy convert_policy); +template SimpleTensor arithmetic_operation(ArithmeticOperation op, const SimpleTensor &src1, const SimpleTensor &src2, SimpleTensor &dst, ConvertPolicy convert_policy); +template SimpleTensor arithmetic_operation(ArithmeticOperation op, const SimpleTensor &src1, const SimpleTensor &src2, SimpleTensor &dst, ConvertPolicy convert_policy); + +template +SimpleTensor arithmetic_operation(ArithmeticOperation op, const SimpleTensor &src1, const SimpleTensor &src2, DataType dst_data_type, ConvertPolicy convert_policy) +{ + ARM_COMPUTE_ERROR_ON_MSG(dst_data_type == DataType::QASYMM8, "For QASYMM8, the quantized output tensor should be passed directly."); + + SimpleTensor dst(TensorShape::broadcast_shape(src1.shape(), src2.shape()), dst_data_type); + arithmetic_operation(op, src1, src2, dst, convert_policy); + return dst; +} + +template SimpleTensor arithmetic_operation(ArithmeticOperation op, const SimpleTensor &src1, const SimpleTensor &src2, DataType dst_data_type, + ConvertPolicy convert_policy); +template SimpleTensor arithmetic_operation(ArithmeticOperation op, const SimpleTensor &src1, const SimpleTensor &src2, DataType dst_data_type, ConvertPolicy convert_policy); +template SimpleTensor arithmetic_operation(ArithmeticOperation op, const SimpleTensor &src1, const SimpleTensor &src2, DataType dst_data_type, ConvertPolicy convert_policy); +template SimpleTensor arithmetic_operation(ArithmeticOperation op, const SimpleTensor &src1, const SimpleTensor &src2, DataType dst_data_type, ConvertPolicy convert_policy); + +} // namespace reference +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/reference/ElementwiseOperations.h b/tests/validation/reference/ElementwiseOperations.h new file mode 100644 index 0000000000..7518ec86d5 --- /dev/null +++ b/tests/validation/reference/ElementwiseOperations.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __ARM_COMPUTE_TEST_ELEMENTWISE_OPERATIONS_H__ +#define __ARM_COMPUTE_TEST_ELEMENTWISE_OPERATIONS_H__ + +#include "tests/SimpleTensor.h" +#include "tests/validation/Helpers.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace reference +{ +template +SimpleTensor arithmetic_operation(ArithmeticOperation op, const SimpleTensor &src1, const SimpleTensor &src2, SimpleTensor &dst, ConvertPolicy convert_policy = ConvertPolicy::WRAP); + +template +SimpleTensor arithmetic_operation(ArithmeticOperation op, const SimpleTensor &src1, const SimpleTensor &src2, DataType dst_data_type, ConvertPolicy convert_policy = ConvertPolicy::WRAP); +} // namespace reference +} // namespace validation +} // namespace test +} // namespace arm_compute +#endif /* __ARM_COMPUTE_TEST_ELEMENTWISE_OPERATIONS_H__ */ diff --git a/utils/TypePrinter.h b/utils/TypePrinter.h index 2b81192a44..27560e6b07 100644 --- a/utils/TypePrinter.h +++ b/utils/TypePrinter.h @@ -1322,6 +1322,55 @@ inline std::string to_string(const ConvertPolicy &policy) return str.str(); } +/** Formatted output of the ArithmeticOperation type. + * + * @param[out] os Output stream. + * @param[in] op Operation to output. + * + * @return Modified output stream. + */ +inline ::std::ostream &operator<<(::std::ostream &os, const ArithmeticOperation &op) +{ + switch(op) + { + case ArithmeticOperation::ADD: + os << "ADD"; + break; + case ArithmeticOperation::SUB: + os << "SUB"; + break; + case ArithmeticOperation::DIV: + os << "DIV"; + break; + case ArithmeticOperation::MAX: + os << "MAX"; + break; + case ArithmeticOperation::MIN: + os << "MIN"; + break; + case ArithmeticOperation::SQUARED_DIFF: + os << "SQUARED_DIFF"; + break; + default: + ARM_COMPUTE_ERROR("NOT_SUPPORTED!"); + } + + return os; +} + +/** Formatted output of the Arithmetic Operation + * + * @param[in] op Type to output. + * + * @return Formatted string. + */ +inline std::string to_string(const ArithmeticOperation &op) +{ + std::stringstream str; + str << op; + return str.str(); +} + /** Formatted output of the Reduction Operations. * * @param[out] os Output stream. -- cgit v1.2.1