IVGCVSW-7342 Add Slice support to TOSA Reference Backend
Signed-off-by: Cathal Corbett <cathal.corbett@arm.com>
Change-Id: I8be286b69bebd4cd36033e3145632bb043938d16
diff --git a/src/backends/backendsCommon/test/CMakeLists.txt b/src/backends/backendsCommon/test/CMakeLists.txt
index 881e4d6..5fcc8b5 100644
--- a/src/backends/backendsCommon/test/CMakeLists.txt
+++ b/src/backends/backendsCommon/test/CMakeLists.txt
@@ -52,6 +52,7 @@
ReshapeEndToEndTestImpl.hpp
ResizeEndToEndTestImpl.hpp
RuntimeTestImpl.hpp
+ SliceEndToEndTestImpl.hpp
SpaceToDepthEndToEndTestImpl.cpp
SpaceToDepthEndToEndTestImpl.hpp
SplitterEndToEndTestImpl.hpp
diff --git a/src/backends/backendsCommon/test/SliceEndToEndTestImpl.hpp b/src/backends/backendsCommon/test/SliceEndToEndTestImpl.hpp
new file mode 100644
index 0000000..811ce27
--- /dev/null
+++ b/src/backends/backendsCommon/test/SliceEndToEndTestImpl.hpp
@@ -0,0 +1,99 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#pragma once
+
+#include <armnn/INetwork.hpp>
+
+#include <CommonTestUtils.hpp>
+#include <ResolveType.hpp>
+
+#include <doctest/doctest.h>
+
+namespace
+{
+
+template<typename armnn::DataType DataType>
+armnn::INetworkPtr CreateSliceNetwork(const armnn::TensorShape& inputShape,
+ const armnn::TensorShape& outputShape,
+ const armnn::SliceDescriptor& descriptor,
+ const float qScale = 1.0f,
+ const int32_t qOffset = 0)
+{
+ using namespace armnn;
+
+ INetworkPtr network(INetwork::Create());
+
+ TensorInfo inputTensorInfo(inputShape, DataType, qScale, qOffset, true);
+ TensorInfo outputTensorInfo(outputShape, DataType, qScale, qOffset);
+
+
+ IConnectableLayer* slice = network->AddSliceLayer(descriptor, "slice");
+ IConnectableLayer* input = network->AddInputLayer(0, "input");
+ IConnectableLayer* output = network->AddOutputLayer(0, "output");
+
+ Connect(input, slice, inputTensorInfo, 0, 0);
+ Connect(slice, output, outputTensorInfo, 0, 0);
+
+ return network;
+}
+
+template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
+void SliceEndToEnd(const std::vector<armnn::BackendId>& backends)
+{
+ using namespace armnn;
+
+ const TensorShape& inputShape = { 3, 2, 3 };
+ const TensorShape& outputShape = { 2, 1, 3 };
+
+ SliceDescriptor descriptor;
+ descriptor.m_Begin = { 1, 0, 0 };
+ descriptor.m_Size = { 2, 1, 3 };
+
+ INetworkPtr network = CreateSliceNetwork<ArmnnType>(inputShape, outputShape, descriptor);
+
+ CHECK(network);
+
+ std::vector<T> inputData{ 1, 1, 1, 2, 2, 2,
+ 3, 3, 3, 4, 4, 4,
+ 5, 5, 5, 6, 6, 6 };
+ std::vector<T> expectedOutput{ 3, 3, 3,
+ 5, 5, 5 };
+
+ std::map<int, std::vector<T>> inputTensorData = { { 0, inputData } };
+ std::map<int, std::vector<T>> expectedOutputData = { { 0, expectedOutput } };
+
+ EndToEndLayerTestImpl<ArmnnType, ArmnnType>(std::move(network), inputTensorData, expectedOutputData, backends);
+}
+
+template<armnn::DataType ArmnnType>
+void SliceEndToEndFloat16(const std::vector<armnn::BackendId>& backends)
+{
+ using namespace armnn;
+ using namespace half_float::literal;
+ using Half = half_float::half;
+
+ const TensorShape& inputShape = { 3, 2, 3 };
+ const TensorShape& outputShape = { 2, 1, 3 };
+
+ SliceDescriptor descriptor;
+ descriptor.m_Begin = { 1, 0, 0 };
+ descriptor.m_Size = { 2, 1, 3 };
+
+ INetworkPtr network = CreateSliceNetwork<ArmnnType>(inputShape, outputShape, descriptor);
+ CHECK(network);
+
+ std::vector<Half> inputData{ 1._h, 1._h, 1._h, 2._h, 2._h, 2._h,
+ 3._h, 3._h, 3._h, 4._h, 4._h, 4._h,
+ 5._h, 5._h, 5._h, 6._h, 6._h, 6._h };
+ std::vector<Half> expectedOutput{ 3._h, 3._h, 3._h,
+ 5._h, 5._h, 5._h };
+
+ std::map<int, std::vector<Half>> inputTensorData = { { 0, inputData } };
+ std::map<int, std::vector<Half>> expectedOutputData = { { 0, expectedOutput } };
+
+ EndToEndLayerTestImpl<ArmnnType, ArmnnType>(std::move(network), inputTensorData, expectedOutputData, backends);
+}
+
+} // anonymous namespace
\ No newline at end of file
diff --git a/src/backends/tosaCommon/TosaMappings.cpp b/src/backends/tosaCommon/TosaMappings.cpp
index 318735d..15629ff 100644
--- a/src/backends/tosaCommon/TosaMappings.cpp
+++ b/src/backends/tosaCommon/TosaMappings.cpp
@@ -62,6 +62,11 @@
auto reshapeDesc = PolymorphicDowncast<const ReshapeDescriptor*>(&descriptor);
return ConvertReshapeToTosaOperator(layer, inputs, outputs, reshapeDesc);
}
+ case LayerType::Slice:
+ {
+ auto sliceDesc = PolymorphicDowncast<const SliceDescriptor*>(&descriptor);
+ return ConvertSliceToTosaOperator(layer, inputs, outputs, sliceDesc);
+ }
default:
{
return CreateEmptyTosaSerializationBasicBlock();
diff --git a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt
index 7733d01..cb1d68e 100644
--- a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt
+++ b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt
@@ -16,6 +16,8 @@
Pooling2DOperator.cpp
ReshapeOperator.hpp
ReshapeOperator.cpp
+ SliceOperator.hpp
+ SliceOperator.cpp
TosaOperatorUtils.hpp
)
diff --git a/src/backends/tosaCommon/operatorMappings/SliceOperator.cpp b/src/backends/tosaCommon/operatorMappings/SliceOperator.cpp
new file mode 100644
index 0000000..fc2e40a
--- /dev/null
+++ b/src/backends/tosaCommon/operatorMappings/SliceOperator.cpp
@@ -0,0 +1,57 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "SliceOperator.hpp"
+
+TosaSerializationBasicBlock* ConvertSliceToTosaOperator(const Layer* layer,
+ const std::vector<const TensorInfo*>& inputs,
+ const std::vector<const TensorInfo*>& outputs,
+ const SliceDescriptor* sliceDescriptor)
+{
+ std::string inputName = std::string("input0_");
+ std::string outputName = std::string("output0_");
+ std::string blockName = std::string("Op_SLICE_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 layer names.
+ Layer& connectedLayer = layer->GetInputSlot(0).GetConnectedOutputSlot()->GetOwningLayer();
+ inputName = GenerateUniqueName(connectedLayer, 0);
+
+ // Get the layer connected to the output slot and determine unique layer name.
+ Layer& connectedOutputLayer = layer->GetOutputSlot().GetConnection(0)->GetOwningLayer();
+ outputName = GenerateUniqueName(connectedOutputLayer, 0);
+ }
+
+ std::vector<int32_t> begin(sliceDescriptor->m_Begin.begin(), sliceDescriptor->m_Begin.end());
+ std::vector<int32_t> size(sliceDescriptor->m_Size.begin(), sliceDescriptor->m_Size.end());
+
+ TosaSliceAttribute attribute(begin, size);
+
+ auto* op = new TosaSerializationOperator(Op_SLICE,
+ Attribute_SliceAttribute,
+ &attribute,
+ {inputName},
+ {outputName});
+
+ std::vector<int32_t> inputShape = GetTosaTensorShape(inputs[0]->GetShape());
+ DType inputDType = ArmNNToDType(inputs[0]->GetDataType());
+
+ std::vector<int32_t> outputShape = GetTosaTensorShape(outputs[0]->GetShape());
+ DType outputDType = ArmNNToDType(outputs[0]->GetDataType());
+
+ auto* inputTensor = new TosaSerializationTensor(inputName, inputShape, inputDType, {});
+ auto* outputTensor = new TosaSerializationTensor(outputName, 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
+ {op}, // operators
+ {inputTensor, outputTensor}, // tensors
+ {inputName}, // inputs
+ {outputName}); // outputs
+}
\ No newline at end of file
diff --git a/src/backends/tosaCommon/operatorMappings/SliceOperator.hpp b/src/backends/tosaCommon/operatorMappings/SliceOperator.hpp
new file mode 100644
index 0000000..127cc81
--- /dev/null
+++ b/src/backends/tosaCommon/operatorMappings/SliceOperator.hpp
@@ -0,0 +1,20 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "TosaOperatorUtils.hpp"
+
+#include <Layer.hpp>
+
+#include <tosa_serialization_handler.h>
+
+using namespace armnn;
+using namespace tosa;
+
+TosaSerializationBasicBlock* ConvertSliceToTosaOperator(const Layer* layer,
+ const std::vector<const TensorInfo*>& inputs,
+ const std::vector<const TensorInfo*>& outputs,
+ const SliceDescriptor* sliceDescriptor);
\ No newline at end of file
diff --git a/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp b/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp
index 0711095..a3597f0 100644
--- a/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp
+++ b/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp
@@ -10,4 +10,5 @@
#include "Conv2dOperator.hpp"
#include "AvgPool2DIgnoreValueOperator.hpp"
#include "Pooling2DOperator.hpp"
-#include "ReshapeOperator.hpp"
\ No newline at end of file
+#include "ReshapeOperator.hpp"
+#include "SliceOperator.hpp"
\ No newline at end of file
diff --git a/src/backends/tosaCommon/test/OneToOneMappingTests.cpp b/src/backends/tosaCommon/test/OneToOneMappingTests.cpp
index b1fa684..07ffae4 100644
--- a/src/backends/tosaCommon/test/OneToOneMappingTests.cpp
+++ b/src/backends/tosaCommon/test/OneToOneMappingTests.cpp
@@ -375,6 +375,69 @@
LayerType::Reshape);
}
+TEST_CASE("GetTosaMapping_SliceLayer")
+{
+ TensorInfo inputInfo = TensorInfo({ 3, 2, 3 }, DataType::Float32);
+ TensorInfo outputInfo = TensorInfo({ 2, 1, 3 }, DataType::Float32);
+
+ std::vector<std::vector<int32_t>> inputShape = {{ 3, 2, 3 }};
+ std::vector<std::vector<int32_t>> outputShape = {{ 2, 1, 3 }};
+
+ SliceDescriptor descriptor;
+ descriptor.m_Begin = { 3 };
+ descriptor.m_Size = { 3 };
+
+ TosaSerializationBasicBlock* basicBlock =
+ GetTosaMapping(nullptr, LayerType::Slice, {&inputInfo}, {&outputInfo}, descriptor);
+ AssertTosaOneToOneMappingBasicBlock(basicBlock,
+ inputShape,
+ outputShape,
+ Op_SLICE,
+ Attribute_SliceAttribute,
+ descriptor,
+ LayerType::Slice);
+}
+
+TEST_CASE("GetTosaMappingFromLayer_SliceLayer")
+{
+ IRuntime::CreationOptions options;
+ IRuntimePtr runtime(IRuntime::Create(options));
+
+ // Builds up the structure of the network.
+ INetworkPtr net(INetwork::Create());
+
+ TensorInfo inputInfo = TensorInfo({ 3, 2, 3 }, DataType::Float32);
+ TensorInfo outputInfo = TensorInfo({ 2, 1, 3 }, DataType::Float32);
+
+ std::vector<std::vector<int32_t>> inputShape = {{ 3, 2, 3 }};
+ std::vector<std::vector<int32_t>> outputShape = {{ 2, 1, 3 }};
+
+ SliceDescriptor descriptor;
+ descriptor.m_Begin = { 1, 0, 0 };
+ descriptor.m_Size = { 2, 1, 3 };
+
+ IConnectableLayer* input = net->AddInputLayer(0, "input");
+ IConnectableLayer* slice = net->AddSliceLayer(descriptor, "slice");
+ IConnectableLayer* output = net->AddOutputLayer(0, "output");
+
+ input->GetOutputSlot(0).Connect(slice->GetInputSlot(0));
+ slice->GetOutputSlot(0).Connect(output->GetInputSlot(0));
+
+ input->GetOutputSlot(0).SetTensorInfo(inputInfo);
+ slice->GetOutputSlot(0).SetTensorInfo(outputInfo);
+
+ TosaSerializationBasicBlock* basicBlock =
+ GetTosaMappingFromLayer(PolymorphicDowncast<Layer*>(slice));
+ AssertTosaOneToOneMappingBasicBlock(basicBlock,
+ inputShape,
+ outputShape,
+ Op_SLICE,
+ Attribute_SliceAttribute,
+ descriptor,
+ LayerType::Slice);
+}
+
+
TEST_CASE("GetTosaMapping_Unimplemented")
{
TosaSerializationBasicBlock* basicBlock =
diff --git a/src/backends/tosaCommon/test/TosaTestUtils.hpp b/src/backends/tosaCommon/test/TosaTestUtils.hpp
index 5c10a6d..93b9e7d 100644
--- a/src/backends/tosaCommon/test/TosaTestUtils.hpp
+++ b/src/backends/tosaCommon/test/TosaTestUtils.hpp
@@ -122,6 +122,27 @@
1,
std::multiplies<int32_t>());
CHECK(numInputElements == numAttributeShapeElements);
+
+ break;
+ }
+ case LayerType::Slice:
+ {
+ auto sliceDesc = PolymorphicDowncast<const SliceDescriptor*>(&descriptor);
+ TosaSliceAttribute reshapeAttribute(attribute);
+
+ std::vector<int32_t> begin(sliceDesc->m_Begin.begin(), sliceDesc->m_Begin.end());
+ std::vector<int32_t> size(sliceDesc->m_Size.begin(), sliceDesc->m_Size.end());
+
+ CHECK(begin == reshapeAttribute.start());
+ CHECK(size == reshapeAttribute.size());
+
+ CHECK(begin.size() == inputShape.size());
+ CHECK(size.size() == inputShape.size());
+
+ CHECK(begin.size() == outputShape.size());
+ CHECK(size.size() == outputShape.size());
+
+ break;
}
default:
break;
diff --git a/src/backends/tosaReference/TosaRefLayerSupport.cpp b/src/backends/tosaReference/TosaRefLayerSupport.cpp
index 5cda85a..daa27f6 100644
--- a/src/backends/tosaReference/TosaRefLayerSupport.cpp
+++ b/src/backends/tosaReference/TosaRefLayerSupport.cpp
@@ -303,6 +303,24 @@
return RunTosaLayerChecksSingleDataType(
op, inputs, outputs, supportedAttributes, supportedTypes, reasonIfUnsupported);
}
+ case tosa::Op_SLICE:
+ {
+ std::vector<Attribute> supportedAttributes = { Attribute_SliceAttribute };
+
+ std::vector<DType> supportedTypes =
+ {
+ DType_FP16,
+ DType_FP32,
+ DType_INT8,
+ DType_INT16,
+ DType_INT32,
+ DType_BOOL
+ };
+
+ // Check the attribute, data types and bounds for inputs and outputs.
+ return RunTosaLayerChecksSingleDataType(
+ op, inputs, outputs, supportedAttributes, supportedTypes, reasonIfUnsupported);
+ }
default:
SetValueChecked(reasonIfUnsupported, "Operation is currently unsupported by the TOSA Reference Backend.");
return false;
@@ -351,6 +369,7 @@
}
case LayerType::Pooling2d:
case LayerType::Reshape:
+ case LayerType::Slice:
// Setup inputs and outputs
inputInfos.push_back(&infos[0]);
outputInfos.push_back(&infos[1]);
diff --git a/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp b/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp
index aaf8a67..2f12310 100644
--- a/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp
+++ b/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp
@@ -9,6 +9,7 @@
#include "backendsCommon/test/Convolution2dEndToEndTestImpl.hpp"
#include "backendsCommon/test/Pooling2dEndToEndTestImpl.hpp"
#include "backendsCommon/test/ReshapeEndToEndTestImpl.hpp"
+#include "backendsCommon/test/SliceEndToEndTestImpl.hpp"
#include <doctest/doctest.h>
@@ -91,4 +92,20 @@
ReshapeEndToEndFloat16<DataType::Float16>(tosaDefaultBackends);
}
+// Slice
+TEST_CASE("TosaRefSliceEndtoEndTestFloat32")
+{
+ SliceEndToEnd<DataType::Float32>(tosaDefaultBackends);
+}
+
+TEST_CASE("TosaRefSliceEndtoEndTestInt32")
+{
+ SliceEndToEnd<DataType::Signed32>(tosaDefaultBackends);
+}
+
+TEST_CASE("TosaRefSliceEndtoEndTestFloat16")
+{
+ SliceEndToEndFloat16<DataType::Float16>(tosaDefaultBackends);
+}
+
}
\ No newline at end of file
diff --git a/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp b/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp
index 86b01d8..a1bab83 100644
--- a/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp
+++ b/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp
@@ -329,4 +329,56 @@
"has an unsupported data type: DType_UNKNOWN") != std::string::npos);
}
+TEST_CASE("IsLayerSupportedTosaReferenceSlice")
+{
+ TensorShape inShape = {3,2,3};
+ TensorShape outShape = {2,1,3};
+ TensorInfo in(inShape, DataType::Float32);
+ TensorInfo out(outShape, DataType::Float32);
+
+ SliceDescriptor descriptor;
+ descriptor.m_Begin = {1,0,0 };
+ descriptor.m_Size = {2,1,3 };
+
+ TosaRefLayerSupport supportChecker;
+ std::string reasonIfNotSupported;
+ auto supported = supportChecker.IsLayerSupported(LayerType::Slice,
+ {in, out},
+ descriptor,
+ EmptyOptional(),
+ EmptyOptional(),
+ reasonIfNotSupported);
+
+ CHECK(supported);
+}
+
+TEST_CASE("IsLayerSupportedTosaReferenceSliceUnsupported")
+{
+ TensorShape inShape = {3,2,3};
+ TensorShape outShape = {2,1,3};
+ TensorInfo in(inShape, DataType::Signed64);
+ TensorInfo out(outShape, DataType::Signed64);
+
+ SliceDescriptor descriptor;
+ descriptor.m_Begin = {1,0,0};
+ descriptor.m_Size = {2,1,3};
+
+ TosaRefLayerSupport supportChecker;
+ std::string reasonIfNotSupported;
+ auto supported = supportChecker.IsLayerSupported(LayerType::Slice,
+ {in, out},
+ descriptor,
+ EmptyOptional(),
+ EmptyOptional(),
+ reasonIfNotSupported);
+
+ CHECK(!supported);
+ REQUIRE(reasonIfNotSupported.find(
+ "TOSA Reference Operator: Op_SLICE for input: input0_") != std::string::npos);
+ REQUIRE(reasonIfNotSupported.find(
+ "TOSA Reference Operator: Op_SLICE for output: output0_") != std::string::npos);
+ REQUIRE(reasonIfNotSupported.find(
+ "has an unsupported data type: DType_UNKNOWN") != std::string::npos);
+}
+
}