// // 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_COMPAT_VERSION(0, 60, 0) std::string outputNameMAXMIN= std::string("intermediate3_") + GetUniqueTosaMappingID(); if (inputDType0 == DType::DType_FP32 || inputDType0 == DType::DType_FP16) { // 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; CreateRescaleTosaOperator(inputName, outputNameRescaleAlpha, scale_alpha, input_zp, 0, true, true, &rescaleAlphaOp); tensors.push_back(new TosaSerializationTensor(outputNameRescaleAlpha, GetTosaTensorShape(inputs[0]->GetShape()), rescale_type, {})); // Value op_rescale_identity_in = // buildRescale(rewriter, op, rescale_type, input, scale_identity, // input_qtype.getZeroPoint(), 0, true, true); TosaSerializationOperator* rescaleIdentityOp = nullptr; CreateRescaleTosaOperator(inputName, outputNameRescaleIdentity, scale_identity, input_zp, 0, true, true, &rescaleIdentityOp); tensors.push_back(new TosaSerializationTensor(outputNameRescaleIdentity, GetTosaTensorShape(inputs[0]->GetShape()), rescale_type, {})); // 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, GetTosaTensorShape(inputs[0]->GetShape()), rescale_type, {})); // Value output = buildRescaleFromInt32(rewriter, op, output_type, result_int32, // 1.0, output_qtype.getZeroPoint()); TosaSerializationOperator* rescaleOutputOp = nullptr; CreateFromInt32RescaleTosaOperator(outputNameRescaleMaxMin, outputName, 1.0, output_zp, &rescaleOutputOp); // 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 }