From 9a33946fd0d5e14be6f957b5a985438fa69684d6 Mon Sep 17 00:00:00 2001 From: Nikhil Raj Date: Mon, 5 Dec 2022 11:24:35 +0000 Subject: IVGCVSW-7172 Add ElementwiseBinary (Subtraction & Multiplication) support to TOSA Reference Backend * Removed AdditionOperator and moved to new ElementwiseBinaryOperator. Signed-off-by: Nikhil Raj Signed-off-by: Matthew Sloyan Change-Id: I8ce20f7575d68334aadcd176827bca3db53d0052 --- src/backends/tosaCommon/TosaMappings.cpp | 4 +- .../operatorMappings/AdditionOperator.cpp | 72 -------------- .../operatorMappings/AdditionOperator.hpp | 20 ---- .../tosaCommon/operatorMappings/CMakeLists.txt | 4 +- .../operatorMappings/ElementwiseBinaryOperator.cpp | 103 +++++++++++++++++++++ .../operatorMappings/ElementwiseBinaryOperator.hpp | 20 ++++ .../operatorMappings/TosaCommonOperators.hpp | 6 +- .../tosaCommon/test/OneToOneMappingTests.cpp | 48 ++++++++++ 8 files changed, 179 insertions(+), 98 deletions(-) delete mode 100644 src/backends/tosaCommon/operatorMappings/AdditionOperator.cpp delete mode 100644 src/backends/tosaCommon/operatorMappings/AdditionOperator.hpp create mode 100644 src/backends/tosaCommon/operatorMappings/ElementwiseBinaryOperator.cpp create mode 100644 src/backends/tosaCommon/operatorMappings/ElementwiseBinaryOperator.hpp (limited to 'src/backends/tosaCommon') diff --git a/src/backends/tosaCommon/TosaMappings.cpp b/src/backends/tosaCommon/TosaMappings.cpp index 1452e4aefd..b0f8fd9c2d 100644 --- a/src/backends/tosaCommon/TosaMappings.cpp +++ b/src/backends/tosaCommon/TosaMappings.cpp @@ -24,8 +24,10 @@ TosaSerializationBasicBlock* GetTosaMapping(const Layer* layer, switch (type) { case LayerType::Addition: + case LayerType::Multiplication: + case LayerType::Subtraction: { - return ConvertAdditionToTosaOperator(layer, inputs, outputs); + return ConvertElementwiseBinaryToTosaOperator(layer, type, inputs, outputs); } case LayerType::Concat: { diff --git a/src/backends/tosaCommon/operatorMappings/AdditionOperator.cpp b/src/backends/tosaCommon/operatorMappings/AdditionOperator.cpp deleted file mode 100644 index 7014886d92..0000000000 --- a/src/backends/tosaCommon/operatorMappings/AdditionOperator.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// -// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. -// SPDX-License-Identifier: MIT -// - -#include "AdditionOperator.hpp" - -TosaSerializationBasicBlock* ConvertAdditionToTosaOperator(const Layer* layer, - const std::vector& inputs, - const std::vector& outputs) -{ - std::string input0Name = std::string("input0_"); - std::string input1Name = std::string("input1_"); - std::string outputName = std::string("output0_"); - std::string blockName = std::string("Op_ADD_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& connectedLayer0 = layer->GetInputSlot(0).GetConnectedOutputSlot()->GetOwningLayer(); - input0Name = GenerateUniqueName(connectedLayer0, 0); - - Layer& connectedLayer1 = layer->GetInputSlot(1).GetConnectedOutputSlot()->GetOwningLayer(); - input1Name = GenerateUniqueName(connectedLayer1, 1); - - // Determine unique output tensor name. - outputName = GenerateUniqueOutputName(*layer, 0); - } - - auto* op = new TosaSerializationOperator(Op_ADD, - Attribute_NONE, - nullptr, - {input0Name, input1Name}, - {outputName}); - - - 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. - if(input0Name.find("input0_") != std::string::npos) - { - std::vector inputShape0 = GetTosaTensorShape(inputs[0]->GetShape()); - DType inputDType0 = ArmNNToDType(inputs[0]->GetDataType()); - - tensors.push_back(new TosaSerializationTensor(input0Name, inputShape0, inputDType0, {})); - } - - if(input1Name.find("input1_") != std::string::npos) - { - std::vector inputShape1 = GetTosaTensorShape(inputs[1]->GetShape()); - DType inputDType1 = ArmNNToDType(inputs[1]->GetDataType()); - - tensors.push_back(new TosaSerializationTensor(input1Name, inputShape1, inputDType1, {})); - } - - std::vector outputShape0 = GetTosaTensorShape(outputs[0]->GetShape()); - DType outputDType0 = ArmNNToDType(outputs[0]->GetDataType()); - - tensors.push_back(new TosaSerializationTensor(outputName, outputShape0, outputDType0, {})); - - // operatorInputNames/operatorOutputNames ends up being the same as - // blockInputNames/blockOutputNames for one-to-one ArmNN to TOSA mappings - return new TosaSerializationBasicBlock(blockName, // name - {op}, // operators - tensors, // tensors - {input0Name, input1Name}, // inputs - {outputName}); // outputs -} \ No newline at end of file diff --git a/src/backends/tosaCommon/operatorMappings/AdditionOperator.hpp b/src/backends/tosaCommon/operatorMappings/AdditionOperator.hpp deleted file mode 100644 index 5eb7441531..0000000000 --- a/src/backends/tosaCommon/operatorMappings/AdditionOperator.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. -// SPDX-License-Identifier: MIT -// - -#pragma once - -#include "TosaOperatorUtils.hpp" - -#include - -#include - -using namespace armnn; -using namespace tosa; - -TosaSerializationBasicBlock* ConvertAdditionToTosaOperator(const Layer* layer, - const std::vector& inputs, - const std::vector& outputs); - diff --git a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt index 2443dc0585..6e897aaa8c 100644 --- a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt +++ b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt @@ -4,8 +4,6 @@ # list(APPEND armnnTosaBackendOperators_sources - AdditionOperator.hpp - AdditionOperator.cpp AvgPool2DIgnoreValueOperator.hpp AvgPool2DIgnoreValueOperator.cpp ConcatOperator.hpp @@ -14,6 +12,8 @@ list(APPEND armnnTosaBackendOperators_sources ConstantOperator.cpp Conv2dOperator.hpp Conv2dOperator.cpp + ElementwiseBinaryOperator.hpp + ElementwiseBinaryOperator.cpp Pooling2DOperator.hpp Pooling2DOperator.cpp ReshapeOperator.hpp diff --git a/src/backends/tosaCommon/operatorMappings/ElementwiseBinaryOperator.cpp b/src/backends/tosaCommon/operatorMappings/ElementwiseBinaryOperator.cpp new file mode 100644 index 0000000000..9909e66a7d --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/ElementwiseBinaryOperator.cpp @@ -0,0 +1,103 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "ElementwiseBinaryOperator.hpp" + +TosaSerializationBasicBlock* ConvertElementwiseBinaryToTosaOperator(const Layer* layer, + const LayerType type, + const std::vector& inputs, + const std::vector& outputs) +{ + std::string input0Name = std::string("input0_"); + std::string input1Name = std::string("input1_"); + std::string outputName = std::string("output0_"); + std::string blockName; + + // 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 tensor names. + Layer& connectedLayer0 = layer->GetInputSlot(0).GetConnectedOutputSlot()->GetOwningLayer(); + input0Name = GenerateUniqueName(connectedLayer0, 0); + + Layer& connectedLayer1 = layer->GetInputSlot(1).GetConnectedOutputSlot()->GetOwningLayer(); + input1Name = GenerateUniqueName(connectedLayer1, 1); + + // Determine unique output tensor name. + outputName = GenerateUniqueOutputName(*layer, 0); + } + + TosaSerializationOperator* op = nullptr; + switch(type) + { + case LayerType::Addition: + { + op = new TosaSerializationOperator(Op_ADD, + Attribute_NONE, + nullptr, + {input0Name, input1Name}, + {outputName}); + blockName = std::string("Op_ADD_block_") + GetUniqueTosaMappingID(); + break; + } + case LayerType::Multiplication: + { + int32_t shift = 0; + TosaMulAttribute mulAttribute(shift); + op = new TosaSerializationOperator(Op_MUL, + Attribute_MulAttribute, + &mulAttribute, + {input0Name, input1Name}, + {outputName}); + blockName = std::string("Op_MUL_block_") + GetUniqueTosaMappingID(); + break; + } + case LayerType::Subtraction: + { + op = new TosaSerializationOperator(Op_SUB, + Attribute_NONE, + nullptr, + {input0Name, input1Name}, + {outputName}); + blockName = std::string("Op_SUB_block_") + GetUniqueTosaMappingID(); + break; + } + default: + throw armnn::Exception("ConvertElementwiseBinaryToTosaOperator: Unsupported layer type."); + } + ARMNN_ASSERT(op != nullptr); + + 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. + if(input0Name.find("input0_") != std::string::npos) + { + std::vector inputShape0 = GetTosaTensorShape(inputs[0]->GetShape()); + DType inputDType0 = ArmNNToDType(inputs[0]->GetDataType()); + tensors.push_back(new TosaSerializationTensor(input0Name, inputShape0, inputDType0, {})); + } + if(input1Name.find("input1_") != std::string::npos) + { + std::vector inputShape1 = GetTosaTensorShape(inputs[1]->GetShape()); + DType inputDType1 = ArmNNToDType(inputs[1]->GetDataType()); + tensors.push_back(new TosaSerializationTensor(input1Name, inputShape1, inputDType1, {})); + } + + std::vector outputShape0 = GetTosaTensorShape(outputs[0]->GetShape()); + DType outputDType0 = ArmNNToDType(outputs[0]->GetDataType()); + + tensors.push_back(new TosaSerializationTensor(outputName, outputShape0, outputDType0, {})); + + // operatorInputNames/operatorOutputNames ends up being the same as + // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings + return new TosaSerializationBasicBlock(blockName, // name + {op}, // operators + tensors, // tensors + {input0Name, input1Name}, // inputs + {outputName}); // outputs +} + diff --git a/src/backends/tosaCommon/operatorMappings/ElementwiseBinaryOperator.hpp b/src/backends/tosaCommon/operatorMappings/ElementwiseBinaryOperator.hpp new file mode 100644 index 0000000000..86031c6e06 --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/ElementwiseBinaryOperator.hpp @@ -0,0 +1,20 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "TosaOperatorUtils.hpp" + +#include + +#include + +using namespace armnn; +using namespace tosa; + +TosaSerializationBasicBlock* ConvertElementwiseBinaryToTosaOperator(const Layer* layer, + const LayerType type, + const std::vector& inputs, + const std::vector& outputs); \ No newline at end of file diff --git a/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp b/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp index 052c54c3af..7b117d8443 100644 --- a/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp +++ b/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp @@ -5,12 +5,12 @@ #pragma once -#include "AdditionOperator.hpp" +#include "AvgPool2DIgnoreValueOperator.hpp" #include "ConcatOperator.hpp" #include "ConstantOperator.hpp" #include "Conv2dOperator.hpp" -#include "AvgPool2DIgnoreValueOperator.hpp" +#include "ElementwiseBinaryOperator.hpp" #include "Pooling2DOperator.hpp" #include "ReshapeOperator.hpp" #include "SliceOperator.hpp" -#include "TransposeConv2dOperator.hpp" \ No newline at end of file +#include "TransposeConv2dOperator.hpp" diff --git a/src/backends/tosaCommon/test/OneToOneMappingTests.cpp b/src/backends/tosaCommon/test/OneToOneMappingTests.cpp index b3ab14a774..146a9cba37 100644 --- a/src/backends/tosaCommon/test/OneToOneMappingTests.cpp +++ b/src/backends/tosaCommon/test/OneToOneMappingTests.cpp @@ -253,6 +253,54 @@ TEST_CASE("GetTosaMappingFromLayer_Conv2dLayer") basicBlock, inputShape, outputShape, Op_CONV2D, Attribute_ConvAttribute, descriptor, LayerType::Convolution2d); } +TEST_CASE("GetTosaMapping_MultiplicationLayer") +{ + + const TensorInfo input0Info ({ 1, 2, 4, 2 }, DataType::Float32); + const TensorInfo input1Info ({ 1, 2, 4, 2 }, DataType::Float32); + const TensorInfo outputInfo ({ 1, 2, 4, 2 }, DataType::Float32); + + std::vector> inputShape = {{ 1, 2, 4, 2 }, { 1, 2, 4, 2 }}; + std::vector> outputShape = {{ 1, 2, 4, 2 }}; + + TosaSerializationBasicBlock* basicBlock = + GetTosaMapping(nullptr, LayerType::Multiplication, {&input0Info, &input1Info}, {&outputInfo}, BaseDescriptor()); + AssertTosaOneToOneMappingBasicBlock( basicBlock, inputShape, outputShape, + tosa::Op_MUL, tosa::Attribute_MulAttribute, BaseDescriptor(), LayerType::Multiplication); +} + +TEST_CASE("GetTosaMappingFromLayer_MultiplicationLayer") +{ + IRuntime::CreationOptions options; + IRuntimePtr runtime(IRuntime::Create(options)); + + // Builds up the structure of the network. + INetworkPtr net(INetwork::Create()); + + IConnectableLayer* input0 = net->AddInputLayer(0, "input0"); + IConnectableLayer* input1 = net->AddInputLayer(1, "input1"); + IConnectableLayer* add = net->AddMultiplicationLayer("multiplication"); + IConnectableLayer* output = net->AddOutputLayer(0, "output"); + + input0->GetOutputSlot(0).Connect(add->GetInputSlot(0)); + input1->GetOutputSlot(0).Connect(add->GetInputSlot(1)); + add->GetOutputSlot(0).Connect(output->GetInputSlot(0)); + + TensorInfo info = TensorInfo({ 2, 2 }, DataType::Float32, 0.0f, 0, true); + + input0->GetOutputSlot(0).SetTensorInfo(info); + input1->GetOutputSlot(0).SetTensorInfo(info); + add->GetOutputSlot(0).SetTensorInfo(info); + + std::vector> inputShape = {{ 2, 2 }, { 2, 2 }}; + std::vector> outputShape = {{ 2, 2 }}; + + TosaSerializationBasicBlock* basicBlock = + GetTosaMappingFromLayer(PolymorphicDowncast(add)); + AssertTosaOneToOneMappingBasicBlock( basicBlock, inputShape, outputShape, + tosa::Op_MUL, Attribute_MulAttribute, BaseDescriptor(), LayerType::Multiplication); +} + TEST_CASE("GetTosaMapping_AvgPool2DLayer") { Pooling2dDescriptor descriptor; -- cgit v1.2.1