From 10403ec9f214d4fec2b85866882a9bc707852412 Mon Sep 17 00:00:00 2001 From: Tracy Narine Date: Tue, 28 Nov 2023 11:55:08 +0000 Subject: IVGCVSW-7344 Add LeakyRelu Activation support to TOSA Reference Backend * Adding a one to many FP32 tosa mapping for Leaky Relu * Added a few utilities that are needed * Added new tests Signed-off-by: Tracy Narine Change-Id: If1d7c57a523961581777a244416a7346a9310803 --- .../test/ActivationEndToEndTestImpl.hpp | 31 ++- src/backends/reference/test/RefEndToEndTests.cpp | 28 +- src/backends/tosaCommon/TosaMappings.cpp | 14 +- .../operatorMappings/ActivationOperator.cpp | 284 +++++++++++++++++++++ .../operatorMappings/ActivationOperator.hpp | 20 ++ .../tosaCommon/operatorMappings/CMakeLists.txt | 5 +- .../operatorMappings/TosaCommonOperators.hpp | 3 +- .../operatorMappings/TosaOperatorUtils.hpp | 12 +- .../operatorMappings/TosaRescaleOperatorUtils.hpp | 130 ++++++++++ .../tosaCommon/test/OneToManyMappingTests.cpp | 54 +++- src/backends/tosaReference/TosaRefLayerSupport.cpp | 6 +- .../tosaReference/test/TosaRefEndToEndTests.cpp | 2 +- .../test/TosaRefLayerSupportTests.cpp | 42 ++- 13 files changed, 616 insertions(+), 15 deletions(-) create mode 100644 src/backends/tosaCommon/operatorMappings/ActivationOperator.cpp create mode 100644 src/backends/tosaCommon/operatorMappings/ActivationOperator.hpp create mode 100644 src/backends/tosaCommon/operatorMappings/TosaRescaleOperatorUtils.hpp diff --git a/src/backends/backendsCommon/test/ActivationEndToEndTestImpl.hpp b/src/backends/backendsCommon/test/ActivationEndToEndTestImpl.hpp index c6d49b1df6..996e7605d2 100644 --- a/src/backends/backendsCommon/test/ActivationEndToEndTestImpl.hpp +++ b/src/backends/backendsCommon/test/ActivationEndToEndTestImpl.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2020-2021,2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2020-2021,2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #pragma once @@ -171,4 +171,31 @@ void HardSwishEndToEndTest(const std::vector& backends) descriptor); } -} // anonymous namespace \ No newline at end of file +/** Executes an end to end test for Leaky Relu activation with specific input and expected-output data + * + * @tparam ArmnnType The armnn data type for the input and expected-output data + * @param backends The backends on which to run the test + */ +template +void LeakyReluEndToEndTest(const std::vector& backends, const float qScale=1.0f, const int32_t qOffset=0) +{ + std::vector floatInputData{ -2.0f, -1.0f, -0.0f, 0.0f, + 1.0f, 2.0f, 3.0f, 4.0f }; + + std::vector floatExpectedOutputData{ -0.02f, -0.01f, -0.0f, 0.0f, + 1.0f, 2.0f, 3.0f, 4.0f }; + + armnn::TensorInfo inputInfo({ 2, 2, 2, 1 }, ArmnnType, qScale, qOffset, true); + armnn::TensorInfo outputInfo({ 2, 2, 2, 1 }, ArmnnType, qScale, qOffset); + + armnn::ActivationDescriptor descriptor(ActivationFunction::LeakyReLu, static_cast(0.01)); + + ActivationEndToEndImpl(backends, + floatInputData, + floatExpectedOutputData, + inputInfo, + outputInfo, + descriptor); +} + +} // anonymous namespace diff --git a/src/backends/reference/test/RefEndToEndTests.cpp b/src/backends/reference/test/RefEndToEndTests.cpp index 78852bc645..63f76b17b1 100644 --- a/src/backends/reference/test/RefEndToEndTests.cpp +++ b/src/backends/reference/test/RefEndToEndTests.cpp @@ -1,5 +1,5 @@ // -// Copyright © 2017-2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2017-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -1044,6 +1044,32 @@ TEST_CASE("RefHardSwishEndToEndTestQSymmS16") HardSwishEndToEndTest(defaultBackends); } +// LeakyRelu +TEST_CASE("RefLeakyReluActivationFloat32") +{ + LeakyReluEndToEndTest(defaultBackends); +} + +TEST_CASE("RefLeakyReluActivationFloat16") +{ + LeakyReluEndToEndTest(defaultBackends, 0.3f, 5); +} + +TEST_CASE("RefLeakyReluActivationInt8") +{ + LeakyReluEndToEndTest(defaultBackends, 0.6f, 7); +} + +TEST_CASE("RefLeakyReluActivationUInt8") +{ + LeakyReluEndToEndTest(defaultBackends, 0.12f, 15); +} + +TEST_CASE("RefLeakyReluActivationInt16") +{ + LeakyReluEndToEndTest(defaultBackends, 0.15f, 55); +} + // LogSoftmax TEST_CASE("RefLogSoftmaxEndToEndTest") { diff --git a/src/backends/tosaCommon/TosaMappings.cpp b/src/backends/tosaCommon/TosaMappings.cpp index 6c6bff4087..1ebb68b3c9 100644 --- a/src/backends/tosaCommon/TosaMappings.cpp +++ b/src/backends/tosaCommon/TosaMappings.cpp @@ -1,5 +1,5 @@ // -// Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2022-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -23,6 +23,18 @@ TosaSerializationBasicBlock* GetTosaMapping(const Layer* layer, { switch (type) { + case LayerType::Activation: + { + auto activationDesc = PolymorphicDowncast(&descriptor); + if (activationDesc->m_Function == ActivationFunction::LeakyReLu) + { + return ConvertActivationToTosaOperator(layer, inputs, outputs, activationDesc); + } + else + { + return CreateEmptyTosaSerializationBasicBlock(); + } + } case LayerType::Addition: case LayerType::Multiplication: case LayerType::Subtraction: diff --git a/src/backends/tosaCommon/operatorMappings/ActivationOperator.cpp b/src/backends/tosaCommon/operatorMappings/ActivationOperator.cpp new file mode 100644 index 0000000000..4009ae834a --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/ActivationOperator.cpp @@ -0,0 +1,284 @@ +// +// Copyright © 2024 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// +// +// Copyright © 2020 The TensorFlow Authors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// + +#include "ActivationOperator.hpp" +#include "TosaRescaleOperatorUtils.hpp" + +#include + +// This function is paraphrased from: +// tensorflow/compiler/mlir/tosa/transforms/legalize_tfl.cc from function ConvertTFLLeakyReluOp +TosaSerializationBasicBlock* ConvertActivationToTosaOperator(const Layer* layer, + const std::vector& inputs, + const std::vector& outputs, + const ActivationDescriptor* activationDescriptor) +{ + if (inputs.size() != 1) + { + throw armnn::Exception("ConvertActivationToTosaOperator: 1 input tensors required."); + } + + if (outputs.size() != 1) + { + throw armnn::Exception("ConvertActivationToTosaOperator: 1 output tensor required."); + } + + std::string inputName = std::string("input0_"); + std::string outputNameAlpha = std::string("intermediate1_") + GetUniqueTosaMappingID(); + std::string outputNameMul = std::string("intermediate2_") + GetUniqueTosaMappingID(); + std::string outputName = std::string("output0_"); + std::string blockName = std::string("Op_ACTIVATION_block_") + GetUniqueTosaMappingID(); + + // If a layer is present then the block will be used for execution, so input and output names need to be determined + // using the previous and following layers so the graph is connected correctly. For validation this doesn't matter. + if (layer != nullptr) + { + // Get the layers connected to the input slots and determine unique tensors names. + Layer& connectedInputLayer = layer->GetInputSlot(0).GetConnectedOutputSlot()->GetOwningLayer(); + inputName = GenerateUniqueName(connectedInputLayer, 0); + + // Determine unique output tensor name. + outputName = GenerateUniqueOutputName(*layer, 0); + } + + std::vector tensors; + + // Only add input tensors if connected layer is an input layer. + // As intermediate or constant tensors will be created separately. + // There also can't be duplicate tensor. + std::vector inputShape0; + DType inputDType0 = DType::DType_UNKNOWN; + if(inputName.find("input0_") != std::string::npos) + { + inputShape0 = GetTosaTensorShape(inputs[0]->GetShape()); + inputDType0 = ArmNNToDType(inputs[0]->GetDataType()); + tensors.push_back(new TosaSerializationTensor(inputName, inputShape0, inputDType0, {})); + } + + std::vector outputShape0 = GetTosaTensorShape(outputs[0]->GetShape()); + DType outputDType0 = ArmNNToDType(outputs[0]->GetDataType()); + tensors.push_back(new TosaSerializationTensor(outputName, outputShape0, outputDType0, {})); + +#if TOSA_FWD_COMPAT_VERSION(0, 60, 0) + std::string outputNameMAXMIN= std::string("intermediate3_") + GetUniqueTosaMappingID(); + + if (inputDType0 == DType::DType_FP32) + { + // const_alpha + TosaSerializationOperator* alphaOp = nullptr; + TosaSerializationTensor* alphaTensor = nullptr; + CreateConstTosaOperator(outputNameAlpha, + activationDescriptor->m_A, + inputDType0, + inputShape0, + alphaOp, + alphaTensor); + tensors.push_back(alphaTensor); + + // mul + int32_t shift = 0; + TosaMulAttribute mulAttribute(shift); + TosaSerializationOperator* mulOp = new TosaSerializationOperator(Op_MUL, + Attribute_MulAttribute, + &mulAttribute, + {inputName, outputNameAlpha}, + {outputNameMul}); + tensors.push_back(new TosaSerializationTensor(outputNameMul, inputShape0, inputDType0, {})); + + TosaSerializationOperator* op = nullptr; + if (activationDescriptor->m_A <= 1.0) + { + op = new TosaSerializationOperator(Op_MAXIMUM, + Attribute_NONE, + nullptr, + {inputName, outputNameMul}, + {outputName}); + } + else + { + op = new TosaSerializationOperator(Op_MINIMUM, + Attribute_NONE, + nullptr, + {inputName, outputNameMul}, + {outputName}); + + } + + // operatorInputNames/operatorOutputNames ends up being the same as + // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings + return new TosaSerializationBasicBlock(blockName, // name + mainName, // region name + {alphaOp, mulOp, op}, // operators + tensors, // tensors + {inputName}, // inputs + {outputName}); // outputs + } + else + { + std::string outputNameRescaleAlpha = std::string("intermediate3_") + GetUniqueTosaMappingID(); + std::string outputNameRescaleIdentity = std::string("intermediate4_") + GetUniqueTosaMappingID(); + std::string outputNameRescaleMaxMin = std::string("intermediate5_") + GetUniqueTosaMappingID(); + + DType rescale_type = DType::DType_INT32; + float alpha = activationDescriptor->m_A; + double scale_alpha = inputs[0]->GetQuantizationScale() * alpha / outputs[0]->GetQuantizationScale(); + double scale_identity = inputs[0]->GetQuantizationScale() / outputs[0]->GetQuantizationScale(); + int32_t input_zp = inputs[0]->GetQuantizationOffset(); + int32_t output_zp = outputs[0]->GetQuantizationOffset(); + + // Value op_rescale_alpha_in = + // buildRescale(rewriter, op, rescale_type, input, scale_alpha, + // input_qtype.getZeroPoint(), 0, true, true); + TosaSerializationOperator* rescaleAlphaOp = nullptr; + TosaSerializationTensor* rescaleAlphaTensor = nullptr; + CreateRescaleTosaOperator(inputName, + outputNameRescaleAlpha, + rescale_type, + inputShape0, + scale_alpha, + input_zp, + 0, + true, + true, + &rescaleAlphaOp, + &rescaleAlphaTensor); + tensors.push_back(rescaleAlphaTensor); + + // Value op_rescale_identity_in = + // buildRescale(rewriter, op, rescale_type, input, scale_identity, + // input_qtype.getZeroPoint(), 0, true, true); + TosaSerializationOperator* rescaleIdentityOp = nullptr; + TosaSerializationTensor* rescaleIdentityTensor = nullptr; + CreateRescaleTosaOperator(inputName, + outputNameRescaleIdentity, + rescale_type, + inputShape0, + scale_identity, + input_zp, + 0, + true, + true, + &rescaleIdentityOp, + &rescaleIdentityTensor); + tensors.push_back(rescaleIdentityTensor); + + // Value result_int32; + // if (alpha <= 1.0) { + // auto max_op = CreateOpAndInfer( + // rewriter, op->getLoc(), rescale_type, op_rescale_identity_in, + // op_rescale_alpha_in); + // result_int32 = max_op.getResult(); + // } else { + // auto min_op = CreateOpAndInfer( + // rewriter, op->getLoc(), rescale_type, op_rescale_identity_in, + // op_rescale_alpha_in); + // result_int32 = min_op.getResult(); + // } + TosaSerializationOperator* op = nullptr; + if (alpha <= 1.0) + { + op = new TosaSerializationOperator(Op_MAXIMUM, + Attribute_NONE, + nullptr, + {outputNameRescaleAlpha, outputNameRescaleIdentity}, + {outputNameRescaleMaxMin}); + } + else + { + op = new TosaSerializationOperator(Op_MINIMUM, + Attribute_NONE, + nullptr, + {outputNameRescaleAlpha, outputNameRescaleIdentity}, + {outputNameRescaleMaxMin}); + + } + tensors.push_back(new TosaSerializationTensor(outputNameRescaleMaxMin, inputShape0, rescale_type, {})); + + // Value output = buildRescaleFromInt32(rewriter, op, output_type, result_int32, + // 1.0, output_qtype.getZeroPoint()); + TosaSerializationOperator* rescaleOutputOp = nullptr; + CreateFromInt32RescaleTosaOperator(outputNameRescaleMaxMin, + outputName, + outputDType0, + outputShape0, + 1.0, + output_zp, + &rescaleOutputOp, + nullptr); + + // operatorInputNames/operatorOutputNames ends up being the same as + // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings + return new TosaSerializationBasicBlock(blockName, // name + mainName, // region name + {rescaleAlphaOp, rescaleIdentityOp, op, rescaleOutputOp}, // operators + tensors, // tensors + {inputName}, // inputs + {outputName}); // outputs + } +#else + std::string outputNameZero = std::string("intermediate3_") + GetUniqueTosaMappingID(); + std::string outputNameGE = std::string("intermediate4_") + GetUniqueTosaMappingID(); + + // const_zero + TosaSerializationOperator* zeroOp = nullptr; + TosaSerializationTensor* zeroTensor = nullptr; + CreateConstTosaOperator(outputNameZero, + 0.0f, + inputDType0, + inputShape0, + zeroOp, + zeroTensor); + tensors.push_back(zeroTensor); + + // const_alpha + TosaSerializationOperator* alphaOp = nullptr; + TosaSerializationTensor* alphaTensor = nullptr; + CreateConstTosaOperator(outputNameAlpha, + activationDescriptor->m_A, + inputDType0, + inputShape0, + alphaOp, + alphaTensor); + tensors.push_back(alphaTensor); + + // mul + int32_t shift = 0; + TosaMulAttribute mulAttribute(shift); + TosaSerializationOperator* mulOp = new TosaSerializationOperator(Op_MUL, + Attribute_MulAttribute, + &mulAttribute, + {inputName, outputNameAlpha}, + {outputNameMul}); + tensors.push_back(new TosaSerializationTensor(outputNameMul, inputShape0, inputDType0, {})); + + // greater_equal + TosaSerializationOperator* geOp = new TosaSerializationOperator(Op_GREATER_EQUAL, + Attribute_NONE, + nullptr, + {inputName, outputNameZero}, + {outputNameGE}); + tensors.push_back(new TosaSerializationTensor(outputNameGE, outputShape0, DType::DType_BOOL, {})); + + // select + TosaSerializationOperator* selOp = new TosaSerializationOperator(Op_SELECT, + Attribute_NONE, + nullptr, + {outputNameGE, inputName, outputNameMul}, + {outputName}); + + // operatorInputNames/operatorOutputNames ends up being the same as + // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings + return new TosaSerializationBasicBlock(blockName, // name + mainName, // region name + {zeroOp, alphaOp, mulOp, geOp, selOp}, // operators + tensors, // tensors + {inputName}, // inputs + {outputName}); // outputs +#endif +} diff --git a/src/backends/tosaCommon/operatorMappings/ActivationOperator.hpp b/src/backends/tosaCommon/operatorMappings/ActivationOperator.hpp new file mode 100644 index 0000000000..7519f0c155 --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/ActivationOperator.hpp @@ -0,0 +1,20 @@ +// +// Copyright © 2024 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include + +#include + +#include "TosaOperatorUtils.hpp" + +using namespace armnn; +using namespace tosa; + +TosaSerializationBasicBlock* ConvertActivationToTosaOperator(const Layer* layer, + const std::vector& inputs, + const std::vector& outputs, + const ActivationDescriptor* activationDescriptor); diff --git a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt index b69463487b..58a64574d6 100644 --- a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt +++ b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt @@ -1,9 +1,11 @@ # -# Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved. +# Copyright © 2022-2024 Arm Ltd and Contributors. All rights reserved. # SPDX-License-Identifier: MIT # list(APPEND armnnTosaBackendOperators_sources + ActivationOperator.hpp + ActivationOperator.cpp AvgPool2DIgnoreValueOperator.hpp AvgPool2DIgnoreValueOperator.cpp ConcatOperator.hpp @@ -29,6 +31,7 @@ list(APPEND armnnTosaBackendOperators_sources SplitOperator.hpp SplitOperator.cpp TosaOperatorUtils.hpp + TosaRescaleOperatorUtils.hpp TransposeConv2dOperator.hpp TransposeConv2dOperator.cpp TransposeOperator.hpp diff --git a/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp b/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp index 3d88b6563f..fd0574f30d 100644 --- a/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp +++ b/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp @@ -1,10 +1,11 @@ // -// Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2022-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #pragma once +#include "ActivationOperator.hpp" #include "AvgPool2DIgnoreValueOperator.hpp" #include "ConcatOperator.hpp" #include "ConstantOperator.hpp" diff --git a/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp b/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp index e43f6ca027..05ccef4a9c 100644 --- a/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp +++ b/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2022-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -12,6 +12,7 @@ #include "common/include/ProfilingGuid.hpp" #include +#include using namespace armnn; using namespace tosa; @@ -355,7 +356,6 @@ inline std::vector ConvertConstantTensorDataToBuffer(const std::shared_ return uint8Data; } - inline std::vector CreateConstTosaData(const void* value, DType dtype, const std::vector& shape) @@ -452,4 +452,10 @@ inline void CreateConstTosaOperator(const std::string& outputName, tensor = new TosaSerializationTensor(outputName, shape, dtype, uint8Data); ARMNN_THROW_MSG_IF_FALSE(tensor, armnn::Exception, "CreateConstTosaOperator: failed to created tensor"); -} \ No newline at end of file +} + +// Macro to conditionally compile Tosa code +#define TOSA_FWD_COMPAT_VERSION(_major, _minor, _patch) \ + (TOSA_REFERENCE_MODEL_VERSION_MAJOR >= _major) && \ + (TOSA_REFERENCE_MODEL_VERSION_MINOR >= _minor) && \ + (TOSA_REFERENCE_MODEL_VERSION_PATCH >= _patch) diff --git a/src/backends/tosaCommon/operatorMappings/TosaRescaleOperatorUtils.hpp b/src/backends/tosaCommon/operatorMappings/TosaRescaleOperatorUtils.hpp new file mode 100644 index 0000000000..a0432846f5 --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/TosaRescaleOperatorUtils.hpp @@ -0,0 +1,130 @@ +// +// Copyright © 2024 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include + +#pragma once + +inline void CreateRescaleTosaOperator(const std::string& inputName, + const std::string& outputName, + DType output_type, + const std::vector& shape, + int32_t scale_multiplier, + int32_t scale_shift, + int32_t input_zp, + int32_t output_zp, + bool double_round, + bool scale32, + TosaSerializationOperator** op, + TosaSerializationTensor** tensor) +{ + if (!op) + { + throw armnn::Exception("CreateRescaleTosaOperator: nullptr op"); + } + + std::vector multipliers{scale_multiplier}; + std::vector shifts{scale_shift}; + TosaRescaleAttribute attribute(input_zp, + output_zp, + multipliers, + shifts, + scale32, + double_round, + false); + + // op + *op = new TosaSerializationOperator(Op_RESCALE, Attribute_RescaleAttribute, &attribute, {inputName}, {outputName}); + if (!(*op)) + { + throw armnn::Exception("CreateRescaleTosaOperator: failed to created operator"); + } + if (tensor != nullptr) + { + // tensor + *tensor = new TosaSerializationTensor(outputName, shape, output_type, {}); + if (! (*tensor)) + { + throw armnn::Exception("CreateRescaleTosaOperator: failed to created tensor"); + } + } +} + +inline void CreateRescaleTosaOperator(const std::string& inputName, + const std::string& outputName, + DType output_type, + const std::vector& shape, + double scale, + int32_t input_zp, + int32_t output_zp, + bool double_round, + bool scale32, + TosaSerializationOperator** op, + TosaSerializationTensor** tensor) +{ + // The code that follows is based on the behaviour specified in + // https://www.mlplatform.org/tosa/tosa_spec.html#_precision_scaling + + auto GetScaleParams = [](double scale, double& m, int32_t& n) + { + m = 0; + n = 0; + + double lastErr = 1e06; + + const int32_t numExponents = 62; + const double start = 1.0; + const double end = 2.0; + + // Slow iterative approach but running in Reference only + for (int32_t i = 0; i < numExponents; ++i) + { + double exp = 1.0 / (1 << i); + double currentM = scale / exp; // Find current m given value = currentM * exp + if ((currentM >= start) && (currentM < end)) + { + double value = currentM * exp; + double err = std::abs(scale - value); + if (err < lastErr) + { + // Take the m, n that minimize the error + n = i; + m = currentM; + lastErr = err; + } + } + } + }; + + auto GetMultiplierShiftByScale = [GetScaleParams](bool scale32, double scale, int32_t& multiplier, int32_t& shift) + { + double m = 0; + int32_t n = 0; + + GetScaleParams(scale, m, n); + + multiplier = (scale32) ? (1 << 30) * static_cast(m) : (1 << 14) * static_cast(m); + shift = (scale32) ? (30 + n) : (14 + n); + }; + + int32_t multiplier; + int32_t shift; + GetMultiplierShiftByScale(scale32, scale, multiplier, shift); + CreateRescaleTosaOperator(inputName, outputName, output_type, shape, multiplier, shift, + input_zp, output_zp, double_round, scale32, op, tensor); +} + +inline void CreateFromInt32RescaleTosaOperator(const std::string& inputName, + const std::string& outputName, + DType output_type, + const std::vector& shape, + double output_scale, + int32_t output_zp, + TosaSerializationOperator** op, + TosaSerializationTensor** tensor) +{ + CreateRescaleTosaOperator(inputName, outputName, output_type, shape, + output_scale, 0, output_zp, true, true, op, tensor); +} diff --git a/src/backends/tosaCommon/test/OneToManyMappingTests.cpp b/src/backends/tosaCommon/test/OneToManyMappingTests.cpp index f439b044a4..dde4d79392 100644 --- a/src/backends/tosaCommon/test/OneToManyMappingTests.cpp +++ b/src/backends/tosaCommon/test/OneToManyMappingTests.cpp @@ -1,12 +1,12 @@ // -// Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2022-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #include "AvgPool2DIgnoreValueChecker.hpp" #include "QuantizeChecker.hpp" #include "SplitChecker.hpp" - +#include #include using namespace armnn; @@ -190,4 +190,52 @@ TEST_CASE("GetTosaMappingFromLayer_SplitLayer") outShape, descriptor); } -} \ No newline at end of file + +// Activation + +static std::vector tosaDefaultBackends = { "TosaRef" }; + +TEST_CASE("GetTosaMapping_ActivationFloat32") +{ + LeakyReluEndToEndTest(tosaDefaultBackends); +} + +TEST_CASE("GetTosaMapping_ActivationFloat16") +{ + try + { + LeakyReluEndToEndTest(tosaDefaultBackends); + } + catch (armnn::Exception& e) + { + CHECK_EQ(std::string(e.what()), "Failed to assign a backend to each layer"); + } +} + +TEST_CASE("GetTosaMapping_ActivationInt32") +{ + LeakyReluEndToEndTest(tosaDefaultBackends, 0.15f, 0); +} + +TEST_CASE("GetTosaMapping_ActivationInt16") +{ + LeakyReluEndToEndTest(tosaDefaultBackends, 0.35f, 0); +} + +TEST_CASE("GetTosaMapping_ActivationInt8") +{ + LeakyReluEndToEndTest(tosaDefaultBackends, 0.75f, 0); +} + +TEST_CASE("GetTosaMapping_ActivationUInt8") +{ + try + { + LeakyReluEndToEndTest(tosaDefaultBackends); + } + catch (armnn::Exception& e) + { + CHECK_EQ(std::string(e.what()), "Failed to assign a backend to each layer"); + } +} +} diff --git a/src/backends/tosaReference/TosaRefLayerSupport.cpp b/src/backends/tosaReference/TosaRefLayerSupport.cpp index a38c431e09..dac06676bf 100644 --- a/src/backends/tosaReference/TosaRefLayerSupport.cpp +++ b/src/backends/tosaReference/TosaRefLayerSupport.cpp @@ -1,5 +1,5 @@ // -// Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2022-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -34,6 +34,10 @@ bool TosaRefLayerSupport::IsLayerSupported(const LayerType& type, switch (type) { + case LayerType::Activation: + inputInfos.push_back(&infos[0]); + outputInfos.push_back(&infos[1]); + break; case LayerType::Input: case LayerType::Output: return true; diff --git a/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp b/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp index 63fa69d8a9..7a3edaf212 100644 --- a/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp +++ b/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp @@ -25,7 +25,7 @@ TEST_SUITE("TosaRefEndToEnd") { -std::vector tosaDefaultBackends = { "TosaRef" }; +static std::vector tosaDefaultBackends = { "TosaRef" }; // Addition TEST_CASE("TosaRefAdditionEndtoEndTestFloat32") diff --git a/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp b/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp index 6f038ab69b..2da2875a76 100644 --- a/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp +++ b/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp @@ -1,5 +1,5 @@ // -// Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2022-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -287,6 +287,46 @@ TEST_CASE("IsLayerSupportedTosaReferenceAvgPooling2d_IgnoreValue") CHECK(supported); } +TEST_CASE("IsLayerSupportedTosaReferenceLeakyReLuActivation") +{ + TensorInfo inputInfo1({1,1,3,4}, DataType::Float32); + TensorInfo inputInfo2({1,1,3,4}, DataType::Float32); + TensorInfo outputInfo({1,1,3,4}, DataType::Float32); + + TosaRefLayerSupport supportChecker; + std::string reasonIfNotSupported; + ActivationDescriptor descriptor; + descriptor.m_Function = ActivationFunction::LeakyReLu; + auto supported = supportChecker.IsLayerSupported(LayerType::Activation, + {inputInfo1, inputInfo2, outputInfo}, + descriptor, + EmptyOptional(), + EmptyOptional(), + reasonIfNotSupported); + + CHECK(supported); +} + +TEST_CASE("IsLayerSupportedTosaReferenceActivationUnsupported") +{ + TensorInfo inputInfo1({1,1,3,4}, DataType::Float32); + TensorInfo inputInfo2({1,1,3,4}, DataType::Float32); + TensorInfo outputInfo({1,1,3,4}, DataType::Float32); + + TosaRefLayerSupport supportChecker; + std::string reasonIfNotSupported; + ActivationDescriptor descriptor; + descriptor.m_Function = ActivationFunction::HardSwish; + auto supported = supportChecker.IsLayerSupported(LayerType::Activation, + {inputInfo1, inputInfo2, outputInfo}, + descriptor, + EmptyOptional(), + EmptyOptional(), + reasonIfNotSupported); + + CHECK(!supported); +} + TEST_CASE("IsLayerSupportedTosaReferenceMaxPooling2dUnsupported") { TensorShape inShape = {1,1,3,4}; -- cgit v1.2.1