ArmNN
 25.11
Loading...
Searching...
No Matches
SplitOperator.cpp
Go to the documentation of this file.
1//
2// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5// Copyright © 2020 The TensorFlow Authors. All Rights Reserved.
6// SPDX-License-Identifier: Apache-2.0
7//
8
9#include "SplitOperator.hpp"
11
12// This function is paraphrased from:
13// tensorflow/compiler/mlir/tosa/transforms/legalize_common.cc from function convertSplitOp
14TosaSerializationBasicBlock* ConvertSplitToTosaOperator(const Layer* layer,
15 const std::vector<const TensorInfo*>& inputs,
16 const std::vector<const TensorInfo*>& outputs,
17 const SplitterDescriptor* splitDescriptor)
18{
19 ARMNN_THROW_INVALIDARG_MSG_IF_FALSE( inputs.size() == 1,
20 "ConvertSplitToTosaOperator: Split must have only one input" );
21
22 ARMNN_THROW_INVALIDARG_MSG_IF_FALSE( outputs.size() >= 1,
23 "ConvertSplitToTosaOperator: Split must have at least one output" );
24
25 if (!inputs[0]->GetShape().AreAllDimensionsSpecified())
26 {
27 throw armnn::Exception("ConvertSplitToTosaOperator: Dynamic input dimensions are unsupported.");
28 }
29
30 std::string inputName = std::string("input_");
31 std::vector<std::string> outputNames;
32 std::string blockName = std::string("Op_SPLIT_block_") + GetUniqueTosaMappingID();
33
34 unsigned int numSplit = splitDescriptor->GetNumViews();
35 // If a layer is present then the block will be used for execution, so input and output names need to be determined
36 // using the previous and following layers so the graph is connected correctly. For validation this doesn't matter.
37 if(layer != nullptr)
38 {
39 inputName = GenerateUniqueInputName(layer->GetInputSlot(0));
40
41 for (unsigned int i=0; i < numSplit; ++i)
42 {
43 // Determine unique output(s) tensor name.
44 std::string outputName = GenerateUniqueOutputName(*layer, i);
45 outputNames.push_back(outputName);
46 }
47 }
48 else
49 {
50 for (unsigned int i=0; i < numSplit; ++i)
51 {
52 // Determine unique output(s) tensor name.
53 std::string outputName = "output" + std::to_string(i) + "_";
54 outputNames.push_back(outputName);
55 }
56 }
57
58 // Configure input and output tensors
59 std::set<unsigned int> splitAxis = ComputeSplitAxis(*splitDescriptor, inputs[0]->GetShape());
60 if (splitAxis.size() != 1)
61 {
62 throw InvalidArgumentException("Cannot derive split axis from SplitterDescriptor");
63 }
64 uint32_t axis = *splitAxis.begin();
65
66 std::vector<TosaSerializationOperator*> ops;
67 std::vector<int32_t> beginVals(inputs[0]->GetNumDimensions(), 0);
68 for (unsigned int i = 0; i < numSplit; ++i)
69 {
70 std::vector<int32_t> sizeVals = GetTosaTensorShape(outputs[i]->GetShape());
71 TosaSliceAttribute attribute(beginVals, sizeVals);
72 auto* op = new TosaSerializationOperator(Op_SLICE,
73 Attribute_SliceAttribute,
74 &attribute,
75 {inputName},
76 {outputNames[i]});
77
78 ops.push_back(op);
79
80 // Update the axis begin value for the next split operation, to be the correct size axis value.
81 beginVals[axis] += sizeVals[axis];
82 }
83
84 std::vector<TosaSerializationTensor*> tensors;
85 // Only add input tensors if connected layer is an input layer.
86 // As intermediate or constant tensors will be created separately.
87 // There also can't be duplicate tensor.
88 if(inputName.find("input_") != std::string::npos)
89 {
90 std::vector<int32_t> inputShape = GetTosaTensorShape(inputs[0]->GetShape());
91 DType inputDType = ArmNNToDType(inputs[0]->GetDataType());
92
93 tensors.push_back(new TosaSerializationTensor(inputName, inputShape, inputDType, {}));
94 }
95
96 DType outputDType = ArmNNToDType(outputs[0]->GetDataType());
97 for (unsigned int i = 0; i < numSplit; ++i)
98 {
99 std::vector<int32_t> outputShape = GetTosaTensorShape(outputs[i]->GetShape());
100 tensors.push_back(new TosaSerializationTensor(outputNames[i], outputShape, outputDType, {}));
101 }
102
103 // operatorInputNames/operatorOutputNames ends up being the same as
104 // blockInputNames/blockOutputNames for one-to-one ArmNN to TOSA mappings
105 return new TosaSerializationBasicBlock(blockName, // name
106 mainName, // region name
107 ops, // operators
108 tensors, // tensors
109 {inputName}, // inputs
110 outputNames); // outputs
111}
#define ARMNN_THROW_INVALIDARG_MSG_IF_FALSE(_cond, _str)
TosaSerializationBasicBlock * ConvertSplitToTosaOperator(const Layer *layer, const std::vector< const TensorInfo * > &inputs, const std::vector< const TensorInfo * > &outputs, const SplitterDescriptor *splitDescriptor)
std::string GenerateUniqueOutputName(const Layer &layer, uint32_t layerSlot=0)
const std::string mainName
DType ArmNNToDType(const DataType &type)
std::string GenerateUniqueInputName(const armnn::InputSlot &slot)
std::string GetUniqueTosaMappingID()
std::vector< int32_t > GetTosaTensorShape(const TensorShape &shape)
Base class for all ArmNN exceptions so that users can filter to just those.
const InputSlot & GetInputSlot(unsigned int index) const override
Get a const input slot handle by slot index.
Definition Layer.hpp:337
std::set< unsigned int > ComputeSplitAxis(const armnn::SplitterDescriptor &desc, const TensorShape &input)
Calculates the axis values for split operation.
ViewsDescriptor SplitterDescriptor
uint32_t GetNumViews() const
Get the number of views.