diff options
author | Kevin May <kevin.may@arm.com> | 2023-12-12 11:18:46 +0000 |
---|---|---|
committer | Kevin May <kevin.may@arm.com> | 2023-12-14 10:05:56 +0000 |
commit | 1bea6beb042635c7716ae43220ee19eedb2de9ff (patch) | |
tree | 638a60e9128e28efb613bee96e2de62f2c08b3fc /src/backends | |
parent | ce65588484ed1e553bdebf24123a30b5575f1bce (diff) | |
download | armnn-1bea6beb042635c7716ae43220ee19eedb2de9ff.tar.gz |
Add Split support to TOSA Reference Backend
* Resolves IVGCVSW-7918
Signed-off-by: Kevin May <kevin.may@arm.com>
Change-Id: Ic2afaa55f7ee88ce4c9b8ea696eef5f28663f8c6
Diffstat (limited to 'src/backends')
15 files changed, 687 insertions, 3 deletions
diff --git a/src/backends/backendsCommon/test/SplitterEndToEndTestImpl.hpp b/src/backends/backendsCommon/test/SplitterEndToEndTestImpl.hpp index da4c6a62be..5a4990f748 100644 --- a/src/backends/backendsCommon/test/SplitterEndToEndTestImpl.hpp +++ b/src/backends/backendsCommon/test/SplitterEndToEndTestImpl.hpp @@ -48,6 +48,7 @@ INetworkPtr CreateSplitterNetwork(const TensorShape& inputShape, splitterDimSizes[splitAxis] /= numSplit; SplitterDescriptor splitDesc(numSplit, inputShape.GetNumDimensions()); + splitDesc.SetAxis(static_cast<int32_t>(splitAxis)); for (unsigned int g = 0; g < numSplit; ++g) { // Set the size of the views. @@ -101,6 +102,35 @@ void Splitter1dEndToEnd(const std::vector<BackendId>& backends) } template<armnn::DataType ArmnnType> +void Splitter1dEndToEndFloat16(const std::vector<BackendId>& backends) +{ + using namespace armnn; + using namespace half_float::literal; + using Half = half_float::half; + + unsigned int splitAxis = 0; + unsigned int numSplit = 2; + const TensorShape& inputShape = { 4 }; + const std::vector<TensorShape> outputShapes{{ 2 }, { 2 }}; + + // Builds up the structure of the network + INetworkPtr net = CreateSplitterNetwork<ArmnnType>(inputShape, outputShapes, splitAxis, numSplit); + + CHECK(net); + + // Creates structures for input & output. + std::vector<Half> inputData{ 1._h, 2._h, 3._h, 4._h }; + + std::vector<Half> expectedOutput0{ 1._h, 2._h }; + std::vector<Half> expectedOutput1{ 3._h, 4._h }; + + std::map<int, std::vector<Half>> inputTensorData = { { 0, inputData } }; + std::map<int, std::vector<Half>> expectedOutputData = { { 0, expectedOutput0 }, {1, expectedOutput1} }; + + EndToEndLayerTestImpl<ArmnnType, ArmnnType>(std::move(net), inputTensorData, expectedOutputData, backends); +} + +template<armnn::DataType ArmnnType> void Splitter2dDim0EndToEnd(const std::vector<BackendId>& backends) { using namespace armnn; @@ -270,6 +300,55 @@ void Splitter3dDim1EndToEnd(const std::vector<BackendId>& backends) } template<armnn::DataType ArmnnType> +void Splitter3dDim1EndToEndFloat16(const std::vector<BackendId>& backends) +{ + using namespace armnn; + using namespace half_float::literal; + using Half = half_float::half; + + unsigned int splitAxis = 1; + unsigned int numSplit = 2; + const TensorShape& inputShape = { 2, 4, 3 }; + const std::vector<TensorShape> outputShapes{{ 2, 2, 3 }, { 2, 2, 3 }}; + + // Builds up the structure of the network + INetworkPtr net = CreateSplitterNetwork<ArmnnType>(inputShape, outputShapes, splitAxis, numSplit); + + CHECK(net); + + // Creates structures for input & output. + std::vector<Half> inputData{ + 1._h, 2._h, 3._h, + 4._h, 5._h, 6._h, + 7._h, 8._h, 9._h, + 10._h, 11._h, 12._h, + 13._h, 14._h, 15._h, + 16._h, 17._h, 18._h, + 19._h, 20._h, 21._h, + 22._h, 23._h, 24._h + }; + + std::vector<Half> expectedOutput0{ + 1._h, 2._h, 3._h, + 4._h, 5._h, 6._h, + 13._h, 14._h, 15._h, + 16._h, 17._h, 18._h + }; + std::vector<Half> expectedOutput1{ + 7._h, 8._h, 9._h, + 10._h, 11._h, 12._h, + 19._h, 20._h, 21._h, + 22._h, 23._h, 24._h + }; + + std::map<int, std::vector<Half>> inputTensorData = { { 0, inputData } }; + std::map<int, std::vector<Half>> expectedOutputData = { { 0, expectedOutput0 }, + { 1, expectedOutput1 } }; + + EndToEndLayerTestImpl<ArmnnType, ArmnnType>(std::move(net), inputTensorData, expectedOutputData, backends); +} + +template<armnn::DataType ArmnnType> void Splitter3dDim2EndToEnd(const std::vector<BackendId>& backends) { using namespace armnn; @@ -549,6 +628,87 @@ void Splitter4dDim2EndToEnd(const std::vector<BackendId>& backends) EndToEndLayerTestImpl<ArmnnType, ArmnnType>(std::move(net), inputTensorData, expectedOutputData, backends); } +template<armnn::DataType ArmnnType> +void Splitter4dDim2EndToEndFloat16(const std::vector<BackendId>& backends) +{ + using namespace armnn; + using namespace half_float::literal; + using Half = half_float::half; + + unsigned int splitAxis = 2; + unsigned int numSplit = 2; + const TensorShape& inputShape = { 2, 3, 4, 2 }; + const std::vector<TensorShape> outputShapes{{ 2, 3, 2, 2 }, { 2, 3, 2, 2 }}; + + // Builds up the structure of the network + INetworkPtr net = CreateSplitterNetwork<ArmnnType>(inputShape, outputShapes, splitAxis, numSplit); + + CHECK(net); + + // Creates structures for input & output. + std::vector<Half> inputData{ + 1._h, 2._h, + 3._h, 4._h, + 5._h, 6._h, + 7._h, 8._h, + 9._h, 10._h, + 11._h, 12._h, + 13._h, 14._h, + 15._h, 16._h, + 17._h, 18._h, + 19._h, 20._h, + 21._h, 22._h, + 23._h, 24._h, + 25._h, 26._h, + 27._h, 28._h, + 29._h, 30._h, + 31._h, 32._h, + 33._h, 34._h, + 35._h, 36._h, + 37._h, 38._h, + 39._h, 40._h, + 41._h, 42._h, + 43._h, 44._h, + 45._h, 46._h, + 47._h, 48._h + }; + + std::vector<Half> expectedOutput0{ + 1._h, 2._h, + 3._h, 4._h, + 9._h, 10._h, + 11._h, 12._h, + 17._h, 18._h, + 19._h, 20._h, + 25._h, 26._h, + 27._h, 28._h, + 33._h, 34._h, + 35._h, 36._h, + 41._h, 42._h, + 43._h, 44._h + }; + + std::vector<Half> expectedOutput1{ + 5._h, 6._h, + 7._h, 8._h, + 13._h, 14._h, + 15._h, 16._h, + 21._h, 22._h, + 23._h, 24._h, + 29._h, 30._h, + 31._h, 32._h, + 37._h, 38._h, + 39._h, 40._h, + 45._h, 46._h, + 47._h, 48._h + }; + + std::map<int, std::vector<Half>> inputTensorData = {{ 0,inputData }}; + std::map<int, std::vector<Half>> expectedOutputData = {{ 0, expectedOutput0 }, { 1, expectedOutput1 }}; + + EndToEndLayerTestImpl<ArmnnType, ArmnnType>(std::move(net), inputTensorData, expectedOutputData, backends); +} + template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>> void Splitter4dDim3EndToEnd(const std::vector<BackendId>& backends) { diff --git a/src/backends/reference/test/RefEndToEndTests.cpp b/src/backends/reference/test/RefEndToEndTests.cpp index 7e07c658fa..9fb6227663 100644 --- a/src/backends/reference/test/RefEndToEndTests.cpp +++ b/src/backends/reference/test/RefEndToEndTests.cpp @@ -1084,6 +1084,17 @@ TEST_CASE("RefSpaceToDepthNchwEndToEndTest2") SpaceToDepthNchwEndToEndTest2(defaultBackends); } +// Split +TEST_CASE("RefSplit1dEndtoEndTestSigned16") +{ + Splitter1dEndToEnd<DataType::QSymmS16>(defaultBackends); +} + +TEST_CASE("TosaRefSplit1dEndtoEndTestFloat16") +{ + Splitter1dEndToEndFloat16<DataType::Float16>(defaultBackends); +} + TEST_CASE("RefSplitter1dEndToEndTest") { Splitter1dEndToEnd<armnn::DataType::Float32>(defaultBackends); @@ -1114,6 +1125,16 @@ TEST_CASE("RefSplitter2dDim1EndToEndUint8Test") Splitter2dDim1EndToEnd<armnn::DataType::QAsymmU8>(defaultBackends); } +TEST_CASE("RefSplit3dDim1EndtoEndTestSigned16") +{ + Splitter3dDim1EndToEnd<DataType::QSymmS16>(defaultBackends); +} + +TEST_CASE("RefSplit3dDim1EndtoEndTestFloat16") +{ + Splitter3dDim1EndToEndFloat16<DataType::Float16>(defaultBackends); +} + TEST_CASE("RefSplitter3dDim0EndToEndTest") { Splitter3dDim0EndToEnd<armnn::DataType::Float32>(defaultBackends); @@ -1159,6 +1180,11 @@ TEST_CASE("RefSplitter4dDim2EndToEndTest") Splitter4dDim2EndToEnd<armnn::DataType::Float32>(defaultBackends); } +TEST_CASE("RefSplit4dDim2EndtoEndTestFloat16") +{ + Splitter4dDim2EndToEndFloat16<DataType::Float16>(defaultBackends); +} + TEST_CASE("RefSplitter4dDim3EndToEndTest") { Splitter4dDim3EndToEnd<armnn::DataType::Float32>(defaultBackends); diff --git a/src/backends/tosaCommon/TosaMappings.cpp b/src/backends/tosaCommon/TosaMappings.cpp index 6567026de0..0bdb1fec3d 100644 --- a/src/backends/tosaCommon/TosaMappings.cpp +++ b/src/backends/tosaCommon/TosaMappings.cpp @@ -84,6 +84,11 @@ TosaSerializationBasicBlock* GetTosaMapping(const Layer* layer, auto sliceDesc = PolymorphicDowncast<const SliceDescriptor*>(&descriptor); return ConvertSliceToTosaOperator(layer, inputs, outputs, sliceDesc); } + case LayerType::Splitter: + { + auto splitDesc = PolymorphicDowncast<const SplitterDescriptor*>(&descriptor); + return ConvertSplitToTosaOperator(layer, inputs, outputs, splitDesc); + } case LayerType::TransposeConvolution2d: { auto transposeConv2dDesc = PolymorphicDowncast<const TransposeConvolution2dDescriptor*>(&descriptor); diff --git a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt index c864544241..279b193db7 100644 --- a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt +++ b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt @@ -24,6 +24,8 @@ list(APPEND armnnTosaBackendOperators_sources ResizeOperator.cpp SliceOperator.hpp SliceOperator.cpp + SplitOperator.hpp + SplitOperator.cpp TosaOperatorUtils.hpp TransposeConv2dOperator.hpp TransposeConv2dOperator.cpp diff --git a/src/backends/tosaCommon/operatorMappings/SplitOperator.cpp b/src/backends/tosaCommon/operatorMappings/SplitOperator.cpp new file mode 100644 index 0000000000..5231f96c36 --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/SplitOperator.cpp @@ -0,0 +1,116 @@ +// +// Copyright © 2023 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 "SplitOperator.hpp" + +// This function is paraphrased from: +// tensorflow/compiler/mlir/tosa/transforms/legalize_common.cc from function convertSplitOp +TosaSerializationBasicBlock* ConvertSplitToTosaOperator(const Layer* layer, + const std::vector<const TensorInfo*>& inputs, + const std::vector<const TensorInfo*>& outputs, + const SplitterDescriptor* splitDescriptor) +{ + ARMNN_THROW_INVALIDARG_MSG_IF_FALSE( inputs.size() == 1, + "ConvertSplitToTosaOperator: Split must have only one input" ); + + ARMNN_THROW_INVALIDARG_MSG_IF_FALSE( outputs.size() < 1, + "ConvertSplitToTosaOperator: Split must have more than one output" ); + + if (!inputs[0]->GetShape().AreAllDimensionsSpecified()) + { + throw armnn::Exception("ConvertSplitToTosaOperator: Dynamic input dimensions are unsupported."); + } + + std::string inputName = std::string("input0_"); + std::vector<std::string> outputNames; + std::string blockName = std::string("Op_SPLIT_block_") + GetUniqueTosaMappingID(); + + unsigned int numSplit = splitDescriptor->GetNumViews(); + // 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& connectedLayer = layer->GetInputSlot(0).GetConnectedOutputSlot()->GetOwningLayer(); + inputName = GenerateUniqueName(connectedLayer, 0); + + for (unsigned int i=0; i < numSplit; ++i) + { + // Determine unique output(s) tensor name. + std::string outputName = GenerateUniqueOutputName(*layer, i); + outputNames.push_back(outputName); + } + } + else + { + for (unsigned int i=0; i < numSplit; ++i) + { + // Determine unique output(s) tensor name. + std::string outputName = "output" + std::to_string(i) + "_"; + outputNames.push_back(outputName); + } + } + + // Each slice op has a different beginning point. + // The size is the same for each slice op. + std::vector<int32_t> beginVals; + beginVals.reserve(inputs[0]->GetNumDimensions()); + std::vector<int32_t> sizeVals; + sizeVals.reserve(inputs[0]->GetNumDimensions()); + for (unsigned int j = 0; j < inputs[0]->GetNumDimensions(); ++j) + { + beginVals.emplace_back(0); + uint32_t dim = inputs[0]->GetShape()[j]; + sizeVals.emplace_back(dim); + } + + uint32_t axis = static_cast<uint32_t>(splitDescriptor->GetAxis()); + sizeVals[axis] = sizeVals[axis] / static_cast<int32_t>(numSplit); + + std::vector<TosaSerializationOperator*> ops; + for (unsigned int i=0; i < numSplit; ++i) + { + beginVals[axis] = static_cast<int>(i) * sizeVals[axis]; + TosaSliceAttribute attribute(beginVals, sizeVals); + auto* op = new TosaSerializationOperator(Op_SLICE, + Attribute_SliceAttribute, + &attribute, + {inputName}, + {outputNames[i]}); + + ops.push_back(op); + } + + std::vector<TosaSerializationTensor*> 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(inputName.find("input0_") != std::string::npos) + { + std::vector<int32_t> inputShape = GetTosaTensorShape(inputs[0]->GetShape()); + DType inputDType = ArmNNToDType(inputs[0]->GetDataType()); + + tensors.push_back(new TosaSerializationTensor(inputName, inputShape, inputDType, {})); + } + + std::vector<int32_t> outputShape = GetTosaTensorShape(outputs[0]->GetShape()); + DType outputDType = ArmNNToDType(outputs[0]->GetDataType()); + + for (unsigned int i=0; i < numSplit; ++i) + { + tensors.push_back(new TosaSerializationTensor(outputNames[i], outputShape, outputDType, {})); + } + // 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 + ops, // operators + tensors, // tensors + {inputName}, // inputs + outputNames); // outputs +}
\ No newline at end of file diff --git a/src/backends/tosaCommon/operatorMappings/SplitOperator.hpp b/src/backends/tosaCommon/operatorMappings/SplitOperator.hpp new file mode 100644 index 0000000000..93091cd9bd --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/SplitOperator.hpp @@ -0,0 +1,16 @@ +// +// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "TosaOperatorUtils.hpp" + +using namespace armnn; +using namespace tosa; + +TosaSerializationBasicBlock* ConvertSplitToTosaOperator(const Layer* layer, + const std::vector<const TensorInfo*>& inputs, + const std::vector<const TensorInfo*>& outputs, + const SplitterDescriptor* splitDescriptor);
\ No newline at end of file diff --git a/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp b/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp index 369b37f35e..749e8764aa 100644 --- a/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp +++ b/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp @@ -15,5 +15,6 @@ #include "ReshapeOperator.hpp" #include "ResizeOperator.hpp" #include "SliceOperator.hpp" +#include "SplitOperator.hpp" #include "TransposeConv2dOperator.hpp" #include "TransposeOperator.hpp" diff --git a/src/backends/tosaCommon/test/OneToManyMappingTests.cpp b/src/backends/tosaCommon/test/OneToManyMappingTests.cpp index b8d28f0405..94dd537a30 100644 --- a/src/backends/tosaCommon/test/OneToManyMappingTests.cpp +++ b/src/backends/tosaCommon/test/OneToManyMappingTests.cpp @@ -1,9 +1,10 @@ // -// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #include "AvgPool2DIgnoreValueChecker.hpp" +#include "SplitChecker.hpp" #include <armnn/IRuntime.hpp> using namespace armnn; @@ -82,4 +83,69 @@ TEST_CASE("GetTosaMappingFromLayer_AvgPool2DIgnoreValueLayer") intermediateShape, descriptor); } + +TEST_CASE("GetTosaMapping_SplitLayer") +{ + const unsigned int numViews = 3; + const unsigned int numDimensions = 4; + armnn::ViewsDescriptor descriptor(numViews, numDimensions); + descriptor.SetAxis(static_cast<int32_t>(1)); + + std::vector<std::vector<int32_t>> inShape = {{ 1, 18, 4, 4 }}; + std::vector<std::vector<int32_t>> outShape = {{ 1, 6, 4, 4 },{ 1, 6, 4, 4 },{ 1, 6, 4, 4 }}; + + armnn::TensorInfo inputTensorInfo({1, 18, 4, 4}, DataType::Float32); + armnn::TensorInfo outputTensorInfo({1, 6, 4, 4}, DataType::Float32); + + TosaSerializationBasicBlock* basicBlock = + GetTosaMapping(nullptr, LayerType::Splitter, {&inputTensorInfo}, {&outputTensorInfo}, descriptor); + + VerifySplit(basicBlock, + inShape, + outShape, + descriptor); +} + +TEST_CASE("GetTosaMappingFromLayer_SplitLayer") +{ + IRuntime::CreationOptions options; + IRuntimePtr runtime(IRuntime::Create(options)); + + // Builds up the structure of the network. + INetworkPtr net(INetwork::Create()); + + const unsigned int numViews = 3; + const unsigned int numDimensions = 4; + armnn::ViewsDescriptor descriptor(numViews, numDimensions); + descriptor.SetAxis(static_cast<int32_t>(1)); + + std::vector<std::vector<int32_t>> inShape = {{ 1, 18, 4, 4 }}; + std::vector<std::vector<int32_t>> outShape = {{ 1, 6, 4, 4 },{ 1, 6, 4, 4 },{ 1, 6, 4, 4 }}; + + IConnectableLayer* input0 = net->AddInputLayer(0, "input0"); + IConnectableLayer* split = net->AddSplitterLayer(descriptor, "split"); + IConnectableLayer* output0 = net->AddOutputLayer(0, "output0"); + IConnectableLayer* output1 = net->AddOutputLayer(1, "output1"); + IConnectableLayer* output2 = net->AddOutputLayer(2, "output2"); + + input0->GetOutputSlot(0).Connect(split->GetInputSlot(0)); + split->GetOutputSlot(0).Connect(output0->GetInputSlot(0)); + split->GetOutputSlot(1).Connect(output1->GetInputSlot(0)); + split->GetOutputSlot(2).Connect(output2->GetInputSlot(0)); + + armnn::TensorInfo inputTensorInfo({1, 18, 4, 4}, DataType::Float32); + armnn::TensorInfo outputTensorInfo({1, 6, 4, 4}, DataType::Float32); + + input0->GetOutputSlot(0).SetTensorInfo(inputTensorInfo); + split->GetOutputSlot(0).SetTensorInfo(outputTensorInfo); + split->GetOutputSlot(1).SetTensorInfo(outputTensorInfo); + split->GetOutputSlot(2).SetTensorInfo(outputTensorInfo); + + TosaSerializationBasicBlock* basicBlock = GetTosaMappingFromLayer(PolymorphicDowncast<Layer*>(split)); + + VerifySplit(basicBlock, + inShape, + outShape, + descriptor); +} }
\ No newline at end of file diff --git a/src/backends/tosaCommon/test/SplitChecker.hpp b/src/backends/tosaCommon/test/SplitChecker.hpp new file mode 100644 index 0000000000..edef4a1cf9 --- /dev/null +++ b/src/backends/tosaCommon/test/SplitChecker.hpp @@ -0,0 +1,77 @@ +// +// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "TosaTestUtils.hpp" + +using namespace armnn; +using namespace tosa; + +void VerifySplit(TosaSerializationBasicBlock* splitBlock, + std::vector<std::vector<int32_t>> inputShape, + std::vector<std::vector<int32_t>> outputShape, + const BaseDescriptor& splitDescriptor, + DType dataType = DType_FP32) +{ + uint32_t numInputs = static_cast<uint32_t>(inputShape.size()); + uint32_t numOutputs = static_cast<uint32_t>(outputShape.size()); + + std::string blockStr = "Op_SPLIT_block_"; + CHECK(splitBlock->GetName().find(blockStr) != std::string::npos); + CHECK(splitBlock->GetInputs().size() == numInputs); + CHECK(splitBlock->GetOutputs().size() == numOutputs); + CHECK(splitBlock->GetOperators().size() == 3); + CHECK(splitBlock->GetTensors().size() == 4); + + // + // Verify slice operator + // + + for (uint32_t i = 0; i < splitBlock->GetOperators().size(); i++) + { + TosaSerializationOperator *sliceOp = splitBlock->GetOperators().at(i); + uint32_t sliceOpOutputs = 1; + CHECK(sliceOp->GetInputTensorNames().size() == numInputs); + CHECK(sliceOp->GetOutputTensorNames().size() == sliceOpOutputs); + + std::basic_string<char> blockInputName = splitBlock->GetInputs()[0]; + std::basic_string<char> operatorInputName = sliceOp->GetInputTensorNames()[0]; + + std::string opInputStr = "input" + std::to_string(0) + "_"; + + CHECK(blockInputName == operatorInputName); + CHECK(splitBlock->GetTensorByName(blockInputName)); + CHECK(blockInputName.find(opInputStr) != std::string::npos); + + TosaSerializationTensor* inputTensor = splitBlock->GetTensorByName(operatorInputName); + CHECK(inputTensor->GetDtype() == dataType); + CHECK(inputTensor->GetData().size() == 0); + CHECK(inputTensor->GetShape() == inputShape[0]); + + std::basic_string<char> blockOutputName = splitBlock->GetOutputs()[i]; + std::basic_string<char> operatorOutputName = sliceOp->GetOutputTensorNames()[0]; + + std::string opOutputStr = "output" + std::to_string(i) + "_"; + + CHECK(blockOutputName == operatorOutputName); + CHECK(splitBlock->GetTensorByName(blockOutputName)); + CHECK(blockOutputName.find(opOutputStr) != std::string::npos); + + TosaSerializationTensor* outputTensor = splitBlock->GetTensorByName(operatorOutputName); + CHECK(outputTensor->GetDtype() == dataType); + CHECK(outputTensor->GetData().size() == 0); + CHECK(outputTensor->GetShape() == outputShape[0]); + + CHECK(sliceOp->GetAttributeType() == Attribute_SliceAttribute); + CHECK(sliceOp->GetOp() == Op_SLICE); + + VerifyTosaAttribute(splitDescriptor, + sliceOp->GetAttribute(), + inputShape[0], + outputShape[0], + LayerType::Splitter, + i); + } + +}
\ No newline at end of file diff --git a/src/backends/tosaCommon/test/TosaTestUtils.hpp b/src/backends/tosaCommon/test/TosaTestUtils.hpp index 87ff5ff532..05dd164b50 100644 --- a/src/backends/tosaCommon/test/TosaTestUtils.hpp +++ b/src/backends/tosaCommon/test/TosaTestUtils.hpp @@ -193,6 +193,38 @@ inline void VerifyTosaAttribute(const BaseDescriptor& descriptor, break; } + case LayerType::Splitter: + { + auto splitDesc = PolymorphicDowncast<const SplitterDescriptor*>(&descriptor); + TosaSliceAttribute sliceAttribute(attribute); + + // Each slice op has a different beginning point. + // The size is the same for each slice op. + std::vector<int32_t> beginVals; + beginVals.reserve(inputShape.size()); + std::vector<int32_t> sizeVals; + sizeVals.reserve(inputShape.size()); + for (unsigned int j = 0; j < inputShape.size(); ++j) + { + beginVals.emplace_back(0); + int32_t dim = inputShape[j]; + sizeVals.emplace_back(dim); + } + + uint32_t axis = static_cast<uint32_t>(splitDesc->GetAxis()); + sizeVals[axis] = sizeVals[axis] / static_cast<int32_t>(splitDesc->GetNumViews()); + beginVals[axis] = static_cast<int>(mappingOpNumber) * sizeVals[axis]; + CHECK(beginVals == sliceAttribute.start()); + CHECK(sizeVals == sliceAttribute.size()); + + CHECK(beginVals.size() == inputShape.size()); + CHECK(sizeVals.size() == inputShape.size()); + + CHECK(beginVals.size() == outputShape.size()); + CHECK(sizeVals.size() == outputShape.size()); + + break; + } case LayerType::TransposeConvolution2d: { auto transposeConv2dDesc = PolymorphicDowncast<const TransposeConvolution2dDescriptor*>(&descriptor); diff --git a/src/backends/tosaReference/TosaRefLayerSupport.cpp b/src/backends/tosaReference/TosaRefLayerSupport.cpp index 04be52d25b..ec6fc3b917 100644 --- a/src/backends/tosaReference/TosaRefLayerSupport.cpp +++ b/src/backends/tosaReference/TosaRefLayerSupport.cpp @@ -74,9 +74,20 @@ bool TosaRefLayerSupport::IsLayerSupported(const LayerType& type, case LayerType::Resize: case LayerType::Slice: case LayerType::Transpose: + { inputInfos.push_back(&infos[0]); outputInfos.push_back(&infos[1]); break; + } + case LayerType::Splitter: + { + inputInfos.push_back(&infos[0]); + for (unsigned int i = 1; i < infos.size(); ++i) + { + outputInfos.push_back(&infos[i]); + } + break; + } case LayerType::TransposeConvolution2d: { inputInfos.push_back(&infos[0]); // input diff --git a/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp b/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp index b35dacb1a1..ae90c66f39 100644 --- a/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp +++ b/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp @@ -14,6 +14,7 @@ #include "backendsCommon/test/ResizeEndToEndTestImpl.hpp" #include "backendsCommon/test/ElementwiseUnaryEndToEndTestImpl.hpp" #include "backendsCommon/test/SliceEndToEndTestImpl.hpp" +#include "backendsCommon/test/SplitterEndToEndTestImpl.hpp" #include "backendsCommon/test/SubtractionEndToEndTestImpl.hpp" #include "backendsCommon/test/TransposeConvolution2dEndToEndTestImpl.hpp" #include "backendsCommon/test/TransposeEndToEndTestImpl.hpp" @@ -202,6 +203,129 @@ TEST_CASE("TosaRefSliceEndtoEndTestFloat16") { SliceEndToEndFloat16<DataType::Float16>(tosaDefaultBackends); } + +// Split +TEST_CASE("TosaRefSplit1dEndtoEndTestBoolean") +{ + Splitter1dEndToEnd<DataType::Boolean>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit1dEndtoEndTestInt8") +{ + Splitter1dEndToEnd<DataType::QSymmS8>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit1dEndtoEndTestSigned16") +{ + Splitter1dEndToEnd<DataType::QSymmS16>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit1dEndtoEndTestInt32") +{ + Splitter1dEndToEnd<DataType::Signed32>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit1dEndtoEndTestFloat16") +{ + Splitter1dEndToEndFloat16<DataType::Float16>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit1dEndToEndFloat32") +{ + Splitter1dEndToEnd<DataType::Float32>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit2dDim0EndtoEndTestFloat32") +{ + Splitter2dDim0EndToEnd<DataType::Float32>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit2dDim1EndtoEndTestFloat32") +{ + Splitter2dDim1EndToEnd<DataType::Float32>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit3dDim0EndtoEndTestFloat32") +{ + Splitter3dDim0EndToEnd<DataType::Float32>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit3dDim1EndtoEndTestFloat32") +{ + Splitter3dDim1EndToEnd<DataType::Float32>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit3dDim1EndtoEndTestFloat16") +{ + Splitter3dDim1EndToEndFloat16<DataType::Float16>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit3dDim1EndtoEndTestBoolean") +{ + Splitter3dDim1EndToEnd<DataType::Boolean>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit3dDim1EndtoEndTestInt8") +{ + Splitter3dDim1EndToEnd<DataType::QSymmS8>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit3dDim1EndtoEndTestSigned16") +{ + Splitter3dDim1EndToEnd<DataType::QSymmS16>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit3dDim1EndtoEndTestInt32") +{ + Splitter3dDim1EndToEnd<DataType::Signed32>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit3dDim2EndtoEndTestInt8") +{ + Splitter3dDim2EndToEnd<DataType::QAsymmS8>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit4dDim0EndtoEndTestInt8") +{ + Splitter4dDim0EndToEnd<DataType::QSymmS8>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit4dDim1EndtoEndTestInt8") +{ + Splitter4dDim1EndToEnd<DataType::QSymmS8>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit4dDim2EndtoEndTestBoolean") +{ + Splitter4dDim2EndToEnd<DataType::Boolean>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit4dDim2EndtoEndTestInt8") +{ + Splitter4dDim2EndToEnd<DataType::QSymmS8>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit4dDim2EndtoEndTestInt16") +{ + Splitter4dDim2EndToEnd<DataType::QSymmS16>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit4dDim2EndtoEndTestInt32") +{ + Splitter4dDim2EndToEnd<DataType::Signed32>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit4dDim2EndtoEndTestFloat16") +{ + Splitter4dDim2EndToEndFloat16<DataType::Float16>(tosaDefaultBackends); +} + +TEST_CASE("TosaRefSplit4dDim3EndtoEndTestInt8") +{ + Splitter4dDim3EndToEnd<DataType::QSymmS8>(tosaDefaultBackends); +} + +// Subtraction TEST_CASE("TosaRefSubtractionEndtoEndTestFloat32") { SubtractionEndToEnd<DataType::Float32>(tosaDefaultBackends); diff --git a/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp b/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp index fb4c84f07c..6f038ab69b 100644 --- a/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp +++ b/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp @@ -484,6 +484,54 @@ TEST_CASE("IsLayerSupportedTosaReferenceSliceUnsupported") CHECK(!supported); } +TEST_CASE("IsLayerSupportedTosaReferenceSplit") +{ + TensorShape inShape = {1, 18, 4, 4}; + TensorShape outShape = {1, 6, 4, 4}; + TensorInfo in(inShape, DataType::Float32); + TensorInfo out(outShape, DataType::Float32); + + const unsigned int numViews = 3; + const unsigned int numDimensions = 4; + armnn::SplitterDescriptor descriptor(numViews, numDimensions); + descriptor.SetAxis(static_cast<int32_t>(1)); + + TosaRefLayerSupport supportChecker; + std::string reasonIfNotSupported; + auto supported = supportChecker.IsLayerSupported(LayerType::Splitter, + {in, out}, + descriptor, + EmptyOptional(), + EmptyOptional(), + reasonIfNotSupported); + + CHECK(supported); +} + +TEST_CASE("IsLayerSupportedTosaReferenceSplitUnsupported") +{ + TensorShape inShape = {1, 18, 4, 4}; + TensorShape outShape = {1, 6, 4, 4}; + TensorInfo in(inShape, DataType::Signed64); + TensorInfo out(outShape, DataType::Signed64); + + const unsigned int numViews = 3; + const unsigned int numDimensions = 4; + armnn::SplitterDescriptor descriptor(numViews, numDimensions); + descriptor.SetAxis(static_cast<int32_t>(1)); + + TosaRefLayerSupport supportChecker; + std::string reasonIfNotSupported; + auto supported = supportChecker.IsLayerSupported(LayerType::Splitter, + {in, out}, + descriptor, + EmptyOptional(), + EmptyOptional(), + reasonIfNotSupported); + + CHECK(!supported); +} + TEST_CASE("IsLayerSupportedTosaReferenceSubtraction") { TensorShape shape0 = {1,1,3,4}; diff --git a/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.cpp b/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.cpp index 5e4103aed1..26bd29cc03 100644 --- a/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.cpp +++ b/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.cpp @@ -1,5 +1,5 @@ // -// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // diff --git a/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.hpp b/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.hpp index 337e8f9572..1ea4d8d87b 100644 --- a/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.hpp +++ b/src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // |