blob: 0956d1688eb88ae816972465845d4c8749b6d092 [file] [log] [blame]
Francis Murtaghc4fb0dd2023-03-16 17:01:56 +00001//
2// Copyright © 2023 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
Teresa Charlinecebb0f2023-04-27 21:37:56 +01005
6#pragma once
7
8#include <OpaqueDelegateUtils.hpp>
9
10namespace armnnOpaqueDelegate
11{
12
13TfLiteStatus VisitUnpackOperator(DelegateData& delegateData,
14 TfLiteOpaqueContext* tfLiteContext,
15 TfLiteOpaqueNode* tfLiteNode,
16 int nodeIndex,
17 int32_t operatorCode)
18{
19 // Check inputs
20 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
21
22 const int* inputTensors;
23 int numInputs;
24 if (TfLiteOpaqueNodeInputs(tfLiteNode, &inputTensors, &numInputs) != kTfLiteOk)
25 {
26 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
27 tfLiteContext,
28 "TfLiteArmnnOpaqueDelegate: Unable to gather input tensor indices from node #%d: ",
29 nodeIndex);
30 return kTfLiteError;
31 }
32 const TfLiteOpaqueTensor* tfLiteInputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext,
33 inputTensors[0]);
34 if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
35 {
36 return kTfLiteError;
37 }
38
39 auto* tfLiteNodeParameters = reinterpret_cast<TfLiteUnpackParams*>(TfLiteOpaqueNodeGetBuiltinData(tfLiteNode));
40 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor);
41
42 // Get Unpack Axis
43 const unsigned int unpackAxis = NonNegative(tfLiteNodeParameters->axis, nodeIndex);
44
45 if (unpackAxis >= inputTensorInfo.GetNumDimensions())
46 {
47 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
48 tfLiteContext,
49 "TfLiteArmnnOpaqueDelegate: The unpack axis #%d cannot be greater than or equal to "
50 "the number of input dimensions #%d in operator #%d node #%d",
51 unpackAxis, inputTensorInfo.GetNumDimensions(), operatorCode, nodeIndex);
52 return kTfLiteError;
53 }
54
55 // Get Unpack Num
56 unsigned int unpackNum = NonNegative(tfLiteNodeParameters->num, nodeIndex);
57
58 // If num is not defined, automatically infer from the length of the dimension axis.
59 if(unpackNum == 0)
60 {
61 unpackNum = inputTensorInfo.GetShape()[unpackAxis];
62 }
63
64 // If unpack number cannot be inferred and is still zero, return kTfLiteError.
65 if(unpackNum == 0)
66 {
67 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
68 tfLiteContext,
69 "TfLiteArmnnOpaqueDelegate: Number to unpack must greater than zero in operator #%d node #%d: ",
70 operatorCode, nodeIndex);
71 return kTfLiteError;
72 }
73
74 // Check outputs
75 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, unpackNum, nodeIndex));
76
77 auto inputDimSize = inputTensorInfo.GetNumDimensions();
78 std::vector<unsigned int> unpackDimSizes(inputDimSize);
79
80 // Add current input shape to unpackDimSizes
81 for (unsigned int i = 0; i < inputDimSize; ++i)
82 {
83 unpackDimSizes[i] = inputTensorInfo.GetShape()[i];
84 }
85
86 if (unpackDimSizes[unpackAxis] != unpackNum)
87 {
88 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
89 tfLiteContext,
90 "TfLiteArmnnOpaqueDelegate: Number to unpack must be the same as length "
91 "of the dimension to unpack along in operator #%d node #%d: ",
92 operatorCode, nodeIndex);
93 return kTfLiteError;
94 }
95
96 unpackDimSizes[unpackAxis] /= unpackNum;
97
98 armnn::SplitterDescriptor splitDesc(unpackNum, static_cast<unsigned int>(unpackDimSizes.size()));
99 for (unsigned int j = 0; j < unpackNum; ++j)
100 {
101 // Set the size of the views.
102 for (unsigned int dimIdx = 0; dimIdx < unpackDimSizes.size(); ++dimIdx)
103 {
104 splitDesc.SetViewSize(j, dimIdx, unpackDimSizes[dimIdx]);
105 }
106 splitDesc.SetViewOriginCoord(j, unpackAxis, unpackDimSizes[unpackAxis] * j);
107 }
108
109 // Gather output indices and use to get output tensors.
110 const int* outputTensors;
111 int numOutputs;
112 if (TfLiteOpaqueNodeOutputs(tfLiteNode, &outputTensors, &numOutputs) != kTfLiteOk)
113 {
114 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
115 tfLiteContext,
116 "TfLiteArmnnOpaqueDelegate: Unable to gather output tensor indices from node #%d: ",
117 nodeIndex);
118 return kTfLiteError;
119 }
120
121 // Validate all outputs and get TensorInfo
122 std::vector<armnn::TensorInfo> outputs;
123 for (unsigned int i = 0; i < unpackNum; ++i)
124 {
125 const TfLiteOpaqueTensor* tfLiteOutputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext,
126 outputTensors[i]);
127 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
128 {
129 return kTfLiteError;
130 }
131
132 outputs.push_back(GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true));
133 }
134
135 const std::vector<std::reference_wrapper<armnn::TensorInfo>> outputTensorInfos(outputs.begin(), outputs.end());
136
137 // Determine the shape of the Splitter layer outputs for validation
138 armnn::TensorShape splitOutShape = armnn::TensorShape(static_cast<unsigned int>(unpackDimSizes.size()),
139 unpackDimSizes.data());
140
141 std::vector<armnn::TensorInfo> splitterOutputs;
142 for (unsigned int outputIndex = 0; outputIndex < outputTensorInfos.size(); ++outputIndex)
143 {
144 splitterOutputs.push_back(armnn::TensorInfo(splitOutShape,
145 outputTensorInfos[outputIndex].get().GetDataType(),
146 outputTensorInfos[outputIndex].get().GetQuantizationScale(),
147 outputTensorInfos[outputIndex].get().GetQuantizationOffset()));
148 }
149 std::vector<std::reference_wrapper<armnn::TensorInfo>> splitterOutputTensorInfos(splitterOutputs.begin(),
150 splitterOutputs.end());
151
152 armnn::BackendId setBackendSplit;
153 if (!delegateData.m_Network)
154 {
155 // Check if splitter is supported
156 bool isSupported = false;
157 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("UNPACK",
158 tfLiteContext,
159 IsSplitterSupported,
160 delegateData.m_Backends,
161 isSupported,
162 setBackendSplit,
163 inputTensorInfo,
164 splitterOutputTensorInfos,
165 splitDesc);
166 return isSupported ? kTfLiteOk : kTfLiteError;
167 }
168
169 // Create Reshape descriptor from the first outputTensorInfo to validate a single Reshape layer
170 // Use this descriptor later when creating every ReshapeLayer as all Reshape Layers should be the same
171 armnn::ReshapeDescriptor reshapeDescriptor;
172 reshapeDescriptor.m_TargetShape = outputTensorInfos[0].get().GetShape();
173
174 armnn::BackendId setBackendReshape;
175 if (!delegateData.m_Network)
176 {
177 bool isSupported = false;
178 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("RESHAPE",
179 tfLiteContext,
180 IsReshapeSupported,
181 delegateData.m_Backends,
182 isSupported,
183 setBackendReshape,
184 splitterOutputTensorInfos[0],
185 outputTensorInfos[0],
186 reshapeDescriptor);
187 return isSupported ? kTfLiteOk : kTfLiteError;
188 };
189
Mike Kellya2806502023-08-03 10:42:11 +0100190 auto layerName = GetName(armnn::LayerType::Splitter, nodeIndex, "Unpack");
Teresa Charlinecebb0f2023-04-27 21:37:56 +0100191 armnn::IConnectableLayer* splitterLayer = delegateData.m_Network->AddSplitterLayer(splitDesc,
Mike Kellya2806502023-08-03 10:42:11 +0100192 layerName.c_str());
Teresa Charlinecebb0f2023-04-27 21:37:56 +0100193 splitterLayer->SetBackendId(setBackendSplit);
194 ARMNN_ASSERT(splitterLayer != nullptr);
195
196 for (unsigned int k = 0; k < splitterLayer->GetNumOutputSlots(); ++k)
197 {
198 splitterLayer->GetOutputSlot(k).SetTensorInfo(outputs[k]);
199 }
200
201 // Connect the input slots
202 auto inputIndex = static_cast<unsigned int>(inputTensors[0]);
203 delegateData.m_OutputSlotForNode[inputIndex]->Connect(splitterLayer->GetInputSlot(0));
204
205 // Create reshape to remove the unpacked dimension for unpack operator of each output from Splitter.
206 for (unsigned int outputIndex = 0; outputIndex < splitterLayer->GetNumOutputSlots(); ++outputIndex)
207 {
Mike Kellya2806502023-08-03 10:42:11 +0100208 auto reshapeLayerName = GetName(armnn::LayerType::Reshape, nodeIndex, "Unpack");
Teresa Charlinecebb0f2023-04-27 21:37:56 +0100209 armnn::IConnectableLayer* reshapeLayer = delegateData.m_Network->AddReshapeLayer(reshapeDescriptor,
210 reshapeLayerName.c_str());
211 reshapeLayer->SetBackendId(setBackendReshape);
212 ARMNN_ASSERT(reshapeLayer != nullptr);
213
214 splitterLayer->GetOutputSlot(outputIndex).SetTensorInfo(splitterOutputTensorInfos[outputIndex]);
215 splitterLayer->GetOutputSlot(outputIndex).Connect(reshapeLayer->GetInputSlot(0));
216
217 armnn::TensorInfo outputTensorInfo = outputTensorInfos[outputIndex];
218 reshapeLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
219
220 armnn::IOutputSlot& slot = reshapeLayer->GetOutputSlot(0);
221
222 delegateData.m_OutputSlotForNode[
223 static_cast<unsigned long>(static_cast<unsigned int>(outputTensors[outputIndex]))] = &slot;
224
225 }
226
227 return kTfLiteOk;
228}
229
230} // namespace armnnOpaqueDelegate