aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin May <kevin.may@arm.com>2023-12-12 11:18:46 +0000
committerKevin May <kevin.may@arm.com>2023-12-14 10:05:56 +0000
commit1bea6beb042635c7716ae43220ee19eedb2de9ff (patch)
tree638a60e9128e28efb613bee96e2de62f2c08b3fc
parentce65588484ed1e553bdebf24123a30b5575f1bce (diff)
downloadarmnn-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
-rw-r--r--src/backends/backendsCommon/test/SplitterEndToEndTestImpl.hpp160
-rw-r--r--src/backends/reference/test/RefEndToEndTests.cpp26
-rw-r--r--src/backends/tosaCommon/TosaMappings.cpp5
-rw-r--r--src/backends/tosaCommon/operatorMappings/CMakeLists.txt2
-rw-r--r--src/backends/tosaCommon/operatorMappings/SplitOperator.cpp116
-rw-r--r--src/backends/tosaCommon/operatorMappings/SplitOperator.hpp16
-rw-r--r--src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp1
-rw-r--r--src/backends/tosaCommon/test/OneToManyMappingTests.cpp68
-rw-r--r--src/backends/tosaCommon/test/SplitChecker.hpp77
-rw-r--r--src/backends/tosaCommon/test/TosaTestUtils.hpp32
-rw-r--r--src/backends/tosaReference/TosaRefLayerSupport.cpp11
-rw-r--r--src/backends/tosaReference/test/TosaRefEndToEndTests.cpp124
-rw-r--r--src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp48
-rw-r--r--src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.cpp2
-rw-r--r--src/backends/tosaReference/workloads/TosaRefPreCompiledWorkload.hpp2
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
//