blob: 2ad14c2fec9737f13e197ee1db598e2717e0c197 [file] [log] [blame]
Kevin May42477c12020-03-26 13:34:14 +00001//
Teresa Charlinee5872d2021-12-03 16:07:42 +00002// Copyright © 2020-2023 Arm Ltd and Contributors. All rights reserved.
Kevin May42477c12020-03-26 13:34:14 +00003// SPDX-License-Identifier: MIT
4//
5
6#pragma once
7
8#include "Utils.hpp"
9
10#include "ConversionUtils.hpp"
Matthew Sloyan9b088d92020-09-14 15:12:55 +010011
12#include <armnn/utility/NumericCast.hpp>
Kevin May42477c12020-03-26 13:34:14 +000013#include <armnnUtils/TensorUtils.hpp>
14
15#include <half/half.hpp>
16
17using Half = half_float::half;
18
19namespace armnn_driver
20{
21
22using namespace armnn;
23using namespace android::nn;
24
25template<typename HalPolicy,
Keith Davis8f22bed2022-04-29 10:57:27 +010026 typename HalOperation = typename HalPolicy::Operation,
27 typename HalModel = typename HalPolicy::Model>
28bool IsWeightsValid(const HalOperation& operation,
29 uint32_t inputIndex,
30 const HalModel& model)
31{
32 using HalOperand = typename HalPolicy::Operand;
33 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
34 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
35 if (!operand)
36 {
37 Fail("%s: failed to get input operand %i", __func__, inputIndex);
38 return false;
39 }
40
41 if (operand->lifetime != HalOperandLifeTime::CONSTANT_COPY
42 && operand->lifetime != HalOperandLifeTime::CONSTANT_REFERENCE
43 && operand->lifetime != HalOperandLifeTime::NO_VALUE)
44 {
45 return false;
46 }
47 return true;
48}
49
50template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +000051 typename HalOperation = typename HalPolicy::Operation,
52 typename HalModel = typename HalPolicy::Model>
53bool IsQSymmDequantizeForWeights(const HalOperation& operation, const HalModel& model)
54{
55 using HalOperand = typename HalPolicy::Operand;
56 using HalOperationType = typename HalPolicy::OperationType;
57
58 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, 0, model);
59 if (!operand)
60 {
61 return false;
62 }
63
64 if(!IsQSymm8(*operand))
65 {
66 // Only QSymm8 weights are dequantized on the fly by the driver
67 return false;
68 }
69
70 if (!IsOperandConstant<HalPolicy>(*operand))
71 {
72 // Non-const input is not accepted for weights
73 return false;
74 }
75
76 // Iterate through all the operations and find the operation feeding from the Dequantize output
77 const size_t outputIndex = operation.outputs[0];
78 for (uint32_t operationIdx = 0; operationIdx < getMainModel(model).operations.size(); ++operationIdx)
79 {
80 const auto& operationIt = getMainModel(model).operations[operationIdx];
81 switch (operationIt.type)
82 {
83 case HalOperationType::FULLY_CONNECTED:
84 if (outputIndex == operationIt.inputs[1]) // Weights are bound to slot 1
85 {
86 // If the output is going into the FC weights return true
87 return true;
88 }
89 break;
90 case HalOperationType::LSTM:
91 for (size_t k = 0; k < operationIt.inputs.size(); ++k)
92 {
93 if (outputIndex == operationIt.inputs[k])
94 {
95 // If the output is going into the LSTM weights return true
96 return true;
97 }
98 }
99 break;
100 default:
101 break;
102 }
103 }
104
105 return false;
106}
107
108template<typename HalPolicy,
109 typename HalOperation = typename HalPolicy::Operation,
110 typename HalModel = typename HalPolicy::Model>
111bool SetupAndTrackLayerOutputSlotAndOverrideTensorInfo(const HalOperation& operation,
112 uint32_t operationOutputIndex,
113 armnn::IConnectableLayer& layer,
114 uint32_t layerOutputIndex,
115 const HalModel& model,
116 ConversionData& data,
117 const armnn::TensorInfo tensor_info)
118{
119 using HalOperand = typename HalPolicy::Operand;
120
121 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, operationOutputIndex, model);
122 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
123 {
124 return false;
125 }
126
127 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
128
129 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
130 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
131
132 outputSlot.SetTensorInfo(tensor_info);
133
134 return true;
135}
136
137template<typename HalPolicy,
Sadik Armagan92b5fd12021-04-26 09:52:06 +0100138 typename HalOperation = typename HalPolicy::Operation,
139 typename HalModel = typename HalPolicy::Model>
140bool ConvertCast(const HalOperation& operation,
141 const HalModel& model,
142 ConversionData& data)
143{
144 using HalOperand = typename HalPolicy::Operand;
145
146 ALOGV("HalPolicy::ConvertCast()");
147
148 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
149
150 if (!input.IsValid())
151 {
152 return Fail("%s: Operation has invalid inputs", __func__);
153 }
154
155 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
156 if (!output)
157 {
158 return Fail("%s: Could not read output 0", __func__);
159 }
160
161 const TensorInfo& inputInfo = input.GetTensorInfo();
162 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
163
164 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100165 armnn::BackendId setBackend;
Sadik Armagan92b5fd12021-04-26 09:52:06 +0100166 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
167 {
168 FORWARD_LAYER_SUPPORT_FUNC(__func__,
169 IsCastSupported,
170 data.m_Backends,
171 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100172 setBackend,
Sadik Armagan92b5fd12021-04-26 09:52:06 +0100173 inputInfo,
174 outputInfo);
175 };
176
177 if(!IsDynamicTensor(outputInfo))
178 {
179 validateFunc(outputInfo, isSupported);
180 }
181 else
182 {
183 isSupported = AreDynamicTensorsSupported();
184 }
185
186 if (!isSupported)
187 {
188 return false;
189 }
190
191 IConnectableLayer* layer = data.m_Network->AddCastLayer();
Cathal Corbett8de96f72022-09-01 13:34:59 +0100192 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +0100193 if (!layer)
194 {
195 return Fail("%s: Could not add the CastLayer", __func__);
196 }
Sadik Armagan92b5fd12021-04-26 09:52:06 +0100197 input.Connect(layer->GetInputSlot(0));
198
199 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
200}
201
202template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +0000203 typename HalOperation = typename HalPolicy::Operation,
204 typename HalModel = typename HalPolicy::Model>
Teresa Charlin7f5b51e2021-09-01 14:19:38 +0100205bool ConvertChannelShuffle(const HalOperation& operation,
206 const HalModel& model,
207 ConversionData& data)
208{
209 using HalOperand = typename HalPolicy::Operand;
210 using HalOperandType = typename HalPolicy::OperandType;
211
212 ALOGV("HalPolicy::ConvertChannelShuffle()");
213
214 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
215 if (!input.IsValid())
216 {
217 return Fail("%s: Operation has invalid inputs", __func__);
218 }
219 auto inputDimensions = static_cast<int32_t>(input.GetTensorInfo().GetNumDimensions());
220
221 ChannelShuffleDescriptor descriptor;
222
223 int32_t groups;
224 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, groups, model, data))
225 {
226 return Fail("%s: Operation has invalid or unsupported number of groups operand", __func__);
227 }
228 descriptor.m_NumGroups = static_cast<uint32_t>(groups);
229
230 int32_t axis;
231 if (!GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, axis, model, data))
232 {
233 return Fail("%s: Operation has invalid or unsupported dimension channel shuffle operand", __func__);
234 }
235 if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
236 {
237 return Fail("%s: Operation has invalid dimension: %d. It is out of bounds [-%d, %d))", __func__, axis,
238 inputDimensions, inputDimensions);
239 }
240 int positiveAxis = (axis < 0) ? inputDimensions + axis : axis;
241 descriptor.m_Axis = static_cast<uint32_t>(positiveAxis);
242
243 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
244 if (!output)
245 {
246 return Fail("%s: Could not read output 0", __func__);
247 }
248
249 const TensorInfo& inputInfo = input.GetTensorInfo();
250 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
251
252 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100253 armnn::BackendId setBackend;
Teresa Charlin7f5b51e2021-09-01 14:19:38 +0100254 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
255 {
256 FORWARD_LAYER_SUPPORT_FUNC(__func__,
257 IsChannelShuffleSupported,
258 data.m_Backends,
259 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100260 setBackend,
Teresa Charlin7f5b51e2021-09-01 14:19:38 +0100261 inputInfo,
262 outputInfo,
263 descriptor);
264 };
265
266 if(!IsDynamicTensor(outputInfo))
267 {
268 validateFunc(outputInfo, isSupported);
269 }
270 else
271 {
272 isSupported = AreDynamicTensorsSupported();
273 }
274
275 if (!isSupported)
276 {
277 return false;
278 }
279
280 IConnectableLayer* layer = data.m_Network->AddChannelShuffleLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +0100281 layer->SetBackendId(setBackend);
Teresa Charlin7f5b51e2021-09-01 14:19:38 +0100282 assert(layer != nullptr);
283 input.Connect(layer->GetInputSlot(0));
284
285 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
286}
287
288template<typename HalPolicy,
289 typename HalOperation = typename HalPolicy::Operation,
290 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +0000291bool ConvertComparison_1_2(const HalOperation& operation,
292 const HalModel& model,
293 ConversionData& data,
294 ComparisonOperation comparisonOperation)
295{
296 using HalOperand = typename HalPolicy::Operand;
297
298 ALOGV("HalPolicy::ConvertComparison()");
299 ALOGV("comparisonOperation = %s", GetComparisonOperationAsCString(comparisonOperation));
300
301 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
302 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
303
304 if (!(input0.IsValid() && input1.IsValid()))
305 {
306 return Fail("%s: Operation has invalid inputs", __func__);
307 }
308
309 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
310 if (!output)
311 {
312 return Fail("%s: Could not read output 0", __func__);
313 }
314
315 const TensorInfo& inputInfo0 = input0.GetTensorInfo();
316 const TensorInfo& inputInfo1 = input1.GetTensorInfo();
317 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
318
Kevin May42477c12020-03-26 13:34:14 +0000319 ComparisonDescriptor descriptor(comparisonOperation);
320
321 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100322 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100323 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
324 {
325 FORWARD_LAYER_SUPPORT_FUNC(__func__,
326 IsComparisonSupported,
327 data.m_Backends,
328 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100329 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100330 inputInfo0,
331 inputInfo1,
332 outputInfo,
333 descriptor);
334
335 };
336
337 if(!IsDynamicTensor(outputInfo))
338 {
339 validateFunc(outputInfo, isSupported);
340 }
341 else
342 {
343 isSupported = AreDynamicTensorsSupported();
344 }
Kevin May42477c12020-03-26 13:34:14 +0000345
346 if (!isSupported)
347 {
348 return false;
349 }
350
351 IConnectableLayer* layer = data.m_Network->AddComparisonLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +0100352 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +0100353 if (!layer)
354 {
355 return Fail("%s: Could not add the ComparisonLayer", __func__);
356 }
Kevin May42477c12020-03-26 13:34:14 +0000357
358 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
359 if (!isReshapeSupported)
360 {
361 return false;
362 }
363
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100364 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000365}
366
367template<typename HalPolicy,
368 typename HalOperation = typename HalPolicy::Operation,
369 typename HalModel = typename HalPolicy::Model>
370bool ConvertConv2d_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
371{
372
373 using HalOperand = typename HalPolicy::Operand;
374 using HalOperandType = typename HalPolicy::OperandType;
375
376 ALOGV("HalPolicy::ConvertConv2d_1_2()");
377
378 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
379 if (!input.IsValid())
380 {
381 return Fail("%s: Operation has invalid inputs", __func__);
382 }
383
384 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
385 if (!output)
386 {
387 return Fail("%s: Could not read output 0", __func__);
388 }
389
390 const TensorInfo& inputInfo = input.GetTensorInfo();
391 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
392
Kevin May42477c12020-03-26 13:34:14 +0000393 Convolution2dDescriptor desc;
394 desc.m_DataLayout = DataLayout::NHWC;
395
396 // Determine whether padding is implicit or explicit
397 bool implicitPadding = operation.inputs.size() == 7 ||
398 (operation.inputs.size() >= 8 &&
399 GetInputOperand<HalPolicy>(operation, 7, model)->type == HalOperandType::BOOL);
400
401 if (implicitPadding)
402 {
403 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 7, model, data);
404 }
405 else if (operation.inputs.size() >= 10)
406 {
407 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
408 }
409
410 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
411
412 // ArmNN does not currently support non-fixed weights or bias
413 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
414 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
415 // the DataLayout is NCHW
Kevin May42477c12020-03-26 13:34:14 +0000416
Keith Davis8f22bed2022-04-29 10:57:27 +0100417
418 if (!IsWeightsValid<HalPolicy>(operation, 1, model) && desc.m_DataLayout == DataLayout::NCHW)
Kevin May42477c12020-03-26 13:34:14 +0000419 {
Keith Davis8f22bed2022-04-29 10:57:27 +0100420 return Fail("%s: Operation has unsupported weights HalOperandLifeTime", __func__);
Kevin May42477c12020-03-26 13:34:14 +0000421 }
422
Keith Davis8f22bed2022-04-29 10:57:27 +0100423 LayerInputHandle weightsInput = (desc.m_DataLayout == DataLayout::NCHW) ?
424 ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
425 ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
426
427 if (!weightsInput.IsValid())
Kevin May42477c12020-03-26 13:34:14 +0000428 {
Keith Davis8f22bed2022-04-29 10:57:27 +0100429 return Fail("%s: Operation has invalid inputs", __func__);
Kevin May42477c12020-03-26 13:34:14 +0000430 }
431
Keith Davis8f22bed2022-04-29 10:57:27 +0100432 LayerInputHandle biasInput = ConvertToLayerInputHandle<HalPolicy>(operation, 2, model, data); // 1D
433 if (!biasInput.IsValid())
434 {
435 return Fail("%s: Operation has invalid inputs", __func__);
436 }
437
438 biasInput.SanitizeQuantizationScale(weightsInput, input);
439 armnn::TensorInfo weightsInfo = weightsInput.GetTensorInfo();
440 armnn::TensorInfo biasInfo = biasInput.GetTensorInfo();
Kevin May42477c12020-03-26 13:34:14 +0000441
442 ActivationFn activation;
443
444 if (implicitPadding)
445 {
446 android::nn::PaddingScheme paddingScheme;
447 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
448 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
449 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
450 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data) ||
451 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 8, desc, model, data))
452 {
453 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
454 }
455
456 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
457 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
458 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
Keith Davis8f22bed2022-04-29 10:57:27 +0100459 const uint32_t kernelX = weightsInfo.GetShape()[widthIndex];
460 const uint32_t kernelY = weightsInfo.GetShape()[heightIndex];
Kevin May42477c12020-03-26 13:34:14 +0000461 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
462 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
463
464 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
465 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
466
467 }
468 else if (operation.inputs.size() >= 10)
469 {
470 // explicit padding
471 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
472 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
473 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
474 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
475 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
476 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
477 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data) ||
478 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 11, desc, model, data))
479 {
480 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
481 }
482 }
483 else
484 {
485 return Fail("%s: Unsupported number of operation inputs", __func__);
486 }
487
488 desc.m_BiasEnabled = true;
Keith Davis8f22bed2022-04-29 10:57:27 +0100489 Optional<TensorInfo> biases(biasInfo);
Kevin May42477c12020-03-26 13:34:14 +0000490
491 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100492 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100493 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
494 {
495 FORWARD_LAYER_SUPPORT_FUNC(__func__,
496 IsConvolution2dSupported,
497 data.m_Backends,
498 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100499 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100500 inputInfo,
501 outputInfo,
502 desc,
Keith Davis8f22bed2022-04-29 10:57:27 +0100503 weightsInfo,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100504 biases);
505 };
506
507 if(!IsDynamicTensor(outputInfo))
508 {
509 validateFunc(outputInfo, isSupported);
510 }
511 else
512 {
513 isSupported = AreDynamicTensorsSupported();
514 }
Kevin May42477c12020-03-26 13:34:14 +0000515
516 if (!isSupported)
517 {
518 return false;
519 }
520
Keith Davis8f22bed2022-04-29 10:57:27 +0100521 armnn::IConnectableLayer* startLayer = data.m_Network->AddConvolution2dLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +0100522 startLayer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +0000523
524 if (!startLayer)
525 {
526 return Fail("%s: AddConvolution2dLayer failed", __func__);
527 }
528
Kevin May42477c12020-03-26 13:34:14 +0000529 input.Connect(startLayer->GetInputSlot(0));
Keith Davis8f22bed2022-04-29 10:57:27 +0100530 weightsInput.Connect(startLayer->GetInputSlot(1));
531 biasInput.Connect(startLayer->GetInputSlot(2));
Kevin May42477c12020-03-26 13:34:14 +0000532
Kevin Mayfcf2a152020-09-08 16:06:32 +0100533 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
534 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +0000535}
536
537template<typename HalPolicy,
538 typename HalOperation = typename HalPolicy::Operation,
539 typename HalModel = typename HalPolicy::Model>
540bool ConvertDepthwiseConv2d_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
541{
542 using HalOperand = typename HalPolicy::Operand;
543 using HalOperandType = typename HalPolicy::OperandType;
544
545 ALOGV("HalPolicy::ConvertDepthwiseConv2d_1_2()");
546
547 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
548
549 if (!input.IsValid())
550 {
551 return Fail("%s: Operation has invalid inputs", __func__);
552 }
553
554 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
555
556 if (!output)
557 {
558 return Fail("%s: Could not read output 0", __func__);
559 }
560
561 const TensorInfo& inputInfo = input.GetTensorInfo();
562 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
563
Kevin May42477c12020-03-26 13:34:14 +0000564 // ArmNN does not currently support non-fixed weights or bias
565 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
566 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
Cathal Corbett915f2a72022-04-15 14:12:08 +0100567 if (!weightsOperand)
Kevin May42477c12020-03-26 13:34:14 +0000568 {
Cathal Corbett915f2a72022-04-15 14:12:08 +0100569 return Fail("%s: Could not read weights", __func__);
Kevin May42477c12020-03-26 13:34:14 +0000570 }
Cathal Corbett915f2a72022-04-15 14:12:08 +0100571 if (weightsOperand->dimensions[0] != 1)
Kevin May42477c12020-03-26 13:34:14 +0000572 {
573 return Fail("%s: Invalid weights; for depthwise convolution, dimension 0 must be 1 but it is %i",
574 __func__, weightsOperand->dimensions[0] );
575 }
576
577 DepthwiseConvolution2dDescriptor desc;
578 desc.m_DataLayout = DataLayout::NHWC;
579
580 // Determine whether padding is implicit or explicit
581 bool implicitPadding = operation.inputs.size() == 8 ||
582 (operation.inputs.size() >= 9 &&
583 GetInputOperand<HalPolicy>(operation, 8, model)->type == HalOperandType::BOOL);
584
585 // Look ahead to find the optional DataLayout, if present
586 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
587 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, dataLayoutFlagIndex, model, data);
588
589 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
Kevin May42477c12020-03-26 13:34:14 +0000590 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
591 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
592
Cathal Corbett915f2a72022-04-15 14:12:08 +0100593 LayerInputHandle weightsInput = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
594 if (!weightsInput.IsValid())
Kevin May42477c12020-03-26 13:34:14 +0000595 {
Cathal Corbett915f2a72022-04-15 14:12:08 +0100596 return Fail("%s: Operation has invalid inputs", __func__);
Kevin May42477c12020-03-26 13:34:14 +0000597 }
598
Cathal Corbett915f2a72022-04-15 14:12:08 +0100599 const HalOperand* biasOperand = GetInputOperand<HalPolicy>(operation, 2, model);
600 if (!biasOperand)
Kevin May42477c12020-03-26 13:34:14 +0000601 {
Cathal Corbett915f2a72022-04-15 14:12:08 +0100602 return Fail("%s: Could not read bias", __func__);
Kevin May42477c12020-03-26 13:34:14 +0000603 }
604
Cathal Corbett915f2a72022-04-15 14:12:08 +0100605 LayerInputHandle biasInput = ConvertToLayerInputHandle<HalPolicy>(operation, 2, model, data); // 1D
606 if (!biasInput.IsValid())
607 {
608 return Fail("%s: Operation has invalid inputs", __func__);
609 }
610
611 biasInput.SanitizeQuantizationScale(weightsInput, input);
612 armnn::TensorInfo weightsInfo = weightsInput.GetTensorInfo();
613 armnn::TensorInfo biasInfo = biasInput.GetTensorInfo();
Kevin May42477c12020-03-26 13:34:14 +0000614
615 ActivationFn activation;
616
617 if (implicitPadding)
618 {
619 android::nn::PaddingScheme paddingScheme;
620 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
621 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
622 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
623 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data) ||
624 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 9, desc, model, data))
625 {
626 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
627 }
628
Cathal Corbett915f2a72022-04-15 14:12:08 +0100629 const uint32_t kernelX = weightsInfo.GetShape()[2];
630 const uint32_t kernelY = weightsInfo.GetShape()[1];
Kevin May42477c12020-03-26 13:34:14 +0000631 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
632 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
633
634 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
635 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
636 }
637 else if (operation.inputs.size() >= 11)
638 {
639 // explicit padding
640 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
641 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
642 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
643 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
644 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
645 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
646 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data) ||
647 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 12, desc, model, data))
648 {
649 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
650 }
651 }
652 else
653 {
654 return Fail("%s: Unsupported number of operation inputs", __func__);
655 }
656
657 desc.m_BiasEnabled = true;
Cathal Corbett915f2a72022-04-15 14:12:08 +0100658 Optional<TensorInfo> biases(biasInfo);
Kevin May42477c12020-03-26 13:34:14 +0000659
660 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100661 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100662 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
663 {
664 FORWARD_LAYER_SUPPORT_FUNC(__func__,
665 IsDepthwiseConvolutionSupported,
666 data.m_Backends,
667 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100668 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100669 inputInfo,
670 outputInfo,
671 desc,
Cathal Corbett915f2a72022-04-15 14:12:08 +0100672 weightsInfo,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100673 biases);
674 };
675
676 if(!IsDynamicTensor(outputInfo))
677 {
678 validateFunc(outputInfo, isSupported);
679 }
680 else
681 {
682 isSupported = AreDynamicTensorsSupported();
683 }
Kevin May42477c12020-03-26 13:34:14 +0000684
685 if (!isSupported)
686 {
687 return false;
688 }
689
Cathal Corbett915f2a72022-04-15 14:12:08 +0100690 armnn::IConnectableLayer* startLayer = data.m_Network->AddDepthwiseConvolution2dLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +0100691 startLayer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +0000692
693 if (!startLayer)
694 {
695 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
696 }
697
Kevin May42477c12020-03-26 13:34:14 +0000698 input.Connect(startLayer->GetInputSlot(0));
699
Cathal Corbett915f2a72022-04-15 14:12:08 +0100700 // Connect weights and bias inputs
701 weightsInput.Connect(startLayer->GetInputSlot(1));
702 biasInput.Connect(startLayer->GetInputSlot(2));
703
Kevin Mayfcf2a152020-09-08 16:06:32 +0100704 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
705 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +0000706}
707
708template<typename HalPolicy,
709 typename HalOperation = typename HalPolicy::Operation,
710 typename HalModel = typename HalPolicy::Model>
711bool ConvertDequantize_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
712{
713 ALOGV("HalPolicy::ConvertDequantize()");
714
715 if (IsQSymmDequantizeForWeights<HalPolicy>(operation, model))
716 {
717 // NOTE: QSymm8 weights are dequantized internally by the driver,
718 // therefore this type of Dequantize is implicitly supported
719 return true;
720 }
721
722 return ::ConvertDequantize<HalPolicy>(operation, model, data);
723}
724
725template<typename HalPolicy,
726 typename HalOperation = typename HalPolicy::Operation,
727 typename HalModel = typename HalPolicy::Model>
728bool ConvertElementwiseUnary(const HalOperation& operation,
729 const HalModel& model,
730 ConversionData& data,
731 UnaryOperation unaryOperation)
732{
733 using HalOperand = typename HalPolicy::Operand;
734
735 ALOGV("HalPolicy::ConvertElementwiseUnary()");
736 ALOGV("unaryOperation = %s", GetUnaryOperationAsCString(unaryOperation));
737
738 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
739
740 if (!input.IsValid())
741 {
742 return Fail("%s: Operation has invalid input", __func__);
743 }
744
745 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
746 if (!output)
747 {
748 return Fail("%s: Could not read output 0", __func__);
749 }
750
751 const TensorInfo& inputInfo = input.GetTensorInfo();
752 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
753
Kevin May42477c12020-03-26 13:34:14 +0000754 ElementwiseUnaryDescriptor descriptor(unaryOperation);
755
756 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100757 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100758 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
759 {
760 FORWARD_LAYER_SUPPORT_FUNC(__func__,
761 IsElementwiseUnarySupported,
762 data.m_Backends,
763 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100764 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100765 inputInfo,
766 outputInfo,
767 descriptor);
768 };
769
770 if(!IsDynamicTensor(outputInfo))
771 {
772 validateFunc(outputInfo, isSupported);
773 }
774 else
775 {
776 isSupported = AreDynamicTensorsSupported();
777 }
Kevin May42477c12020-03-26 13:34:14 +0000778
779 if (!isSupported)
780 {
781 return false;
782 }
783
784 IConnectableLayer* layer = data.m_Network->AddElementwiseUnaryLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +0100785 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +0100786 if (!layer)
787 {
788 return Fail("%s: Could not add the ElementwiseUnaryLayer", __func__);
789 }
Kevin May42477c12020-03-26 13:34:14 +0000790 input.Connect(layer->GetInputSlot(0));
791
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100792 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000793}
794
795template<typename HalPolicy,
796 typename HalOperation = typename HalPolicy::Operation,
797 typename HalModel = typename HalPolicy::Model>
798bool ConvertExpandDims(const HalOperation& operation, const HalModel& model, ConversionData& data)
799{
800 using HalOperand = typename HalPolicy::Operand;
801 using HalOperandType = typename HalPolicy::OperandType;
802
803 ALOGV("HalPolicy::ConvertExpandDims()");
804
805 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
806
807 if (!input.IsValid())
808 {
809 return Fail("%s: Operation has invalid input", __func__);
810 }
811
812 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
813 if (!output)
814 {
815 return Fail("%s: Operation has invalid output", __func__);
816 }
817
818 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +0000819
820 int32_t axis;
821 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
822 {
823 return Fail("%s: failed to get axis input value", __func__);
824 }
825
826 TensorShape targetShape;
827
828 try
829 {
830 targetShape = armnnUtils::ExpandDims(input.GetTensorInfo().GetShape(), axis);
831 }
832 catch (const std::exception& e)
833 {
834 return Fail("%s: %s", __func__, e.what());
835 }
836
Kevin May42477c12020-03-26 13:34:14 +0000837 ReshapeDescriptor reshapeDescriptor;
838 reshapeDescriptor.m_TargetShape = targetShape;
839
840 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100841 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100842 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
843 {
844 FORWARD_LAYER_SUPPORT_FUNC(__func__,
845 IsReshapeSupported,
846 data.m_Backends,
847 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100848 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100849 input.GetTensorInfo(),
850 outputInfo,
851 reshapeDescriptor);
852 };
853
854 if(!IsDynamicTensor(outputInfo))
855 {
Nikhil Rajc5e0bb02021-04-02 10:02:16 +0100856 if (targetShape != outputInfo.GetShape())
857 {
858 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
859 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100860 validateFunc(outputInfo, isSupported);
861 }
862 else
863 {
864 isSupported = AreDynamicTensorsSupported();
865 }
Kevin May42477c12020-03-26 13:34:14 +0000866
867 if (!isSupported)
868 {
869 return false;
870 }
871
872 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +0100873 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +0100874 if (!layer)
875 {
876 return Fail("%s: Could not add the ReshapeLayer", __func__);
877 }
Kevin May42477c12020-03-26 13:34:14 +0000878 input.Connect(layer->GetInputSlot(0));
879
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100880 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000881}
882
883template<typename HalPolicy,
Teresa Charlinf931af92020-04-10 16:46:53 +0100884 typename HalOperation = typename HalPolicy::Operation,
885 typename HalModel = typename HalPolicy::Model>
886bool ConvertGather(const HalOperation& operation, const HalModel& model, ConversionData& data)
887{
888 using HalOperand = typename HalPolicy::Operand;
889 using HalOperandType = typename HalPolicy::OperandType;
890
891 ALOGV("HalPolicy::ConvertGather()");
892
893 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
894 if (!input.IsValid())
895 {
896 return Fail("%s: Operation has invalid input", __func__);
897 }
898 auto inputDimensions = input.GetTensorInfo().GetNumDimensions();
899
900 LayerInputHandle indices = ConvertToLayerInputHandle<HalPolicy>(operation, 2, model, data);
901 if (!indices.IsValid())
902 {
903 return Fail("%s: Operation has invalid indices", __func__);
904 }
905 auto indicesDimensions = indices.GetTensorInfo().GetNumDimensions();
906
907 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
908 if (!output)
909 {
910 return Fail("%s: Operation has invalid output", __func__);
911 }
912 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
913 auto outputDimensions = outputInfo.GetNumDimensions();
Teresa Charlinf931af92020-04-10 16:46:53 +0100914 if (outputDimensions != inputDimensions + indicesDimensions - 1)
915 {
916 return Fail("%s: Operation has invalid output dimensions: %d. Output must be an (%d + %d - 1)-D tensor",
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100917 __func__, outputDimensions, inputDimensions, indicesDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100918 }
919
Teresa Charline0fd6502022-12-05 16:45:50 +0000920 int32_t axis;
Teresa Charlinf931af92020-04-10 16:46:53 +0100921 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
922 {
923 return Fail("%s: Operation has invalid or unsupported axis operand", __func__);
924 }
Teresa Charline0fd6502022-12-05 16:45:50 +0000925 int32_t inputDimensions_int = static_cast<int32_t>(inputDimensions);
926 if ((axis < -inputDimensions_int) || (inputDimensions_int <= axis))
Teresa Charlinf931af92020-04-10 16:46:53 +0100927 {
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100928 return Fail("%s: Operation has invalid axis: %d. It is out of bounds [-%d, %d))", __func__, axis,
929 inputDimensions, inputDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100930 }
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100931
932 GatherDescriptor desc;
933 desc.m_Axis = axis;
Teresa Charlinf931af92020-04-10 16:46:53 +0100934
935 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100936 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100937 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
938 {
939 FORWARD_LAYER_SUPPORT_FUNC(__func__,
940 IsGatherSupported,
941 data.m_Backends,
942 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100943 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100944 input.GetTensorInfo(),
945 indices.GetTensorInfo(),
946 outputInfo,
947 desc);
948 };
949
950 if(!IsDynamicTensor(outputInfo))
951 {
952 validateFunc(outputInfo, isSupported);
953 }
954 else
955 {
956 isSupported = AreDynamicTensorsSupported();
957 }
958
Teresa Charlinf931af92020-04-10 16:46:53 +0100959 if (!isSupported)
960 {
961 return false;
962 }
963
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100964 IConnectableLayer* layer = data.m_Network->AddGatherLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +0100965 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +0100966 if (!layer)
967 {
968 return Fail("%s: Could not add the GatherLayer", __func__);
969 }
Teresa Charlinf931af92020-04-10 16:46:53 +0100970 input.Connect(layer->GetInputSlot(0));
971 indices.Connect(layer->GetInputSlot(1));
972
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100973 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Teresa Charlinf931af92020-04-10 16:46:53 +0100974}
975
976template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +0000977 typename HalOperation = typename HalPolicy::Operation,
978 typename HalModel = typename HalPolicy::Model>
979bool ConvertGroupedConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
980{
981 using HalOperand = typename HalPolicy::Operand;
982 using HalOperandType = typename HalPolicy::OperandType;
983
984 ALOGV("HalPolicy::ConvertGroupedConv2d()");
985
986 //
987 // Parse data
988 //
989 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
990 if (!input.IsValid())
991 {
992 return Fail("%s: Operation has invalid inputs", __func__);
993 }
994 const TensorInfo& inputInfo = input.GetTensorInfo();
995
996 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
997 if (!output)
998 {
999 return Fail("%s: Could not read output 0", __func__);
1000 }
Finn Williamsb0331172020-10-08 14:33:13 +01001001 TensorInfo outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001002
1003 // Look ahead to determine data layout
1004 DataLayout dataLayout = DataLayout::NHWC;
1005 if (operation.inputs.size() == 12)
1006 {
1007 dataLayout = OptionalDataLayout<HalPolicy>(operation, 11, model, data);
1008 }
1009 else
1010 {
1011 dataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
1012 }
1013
1014 // NOTE:
1015 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
1016 // but Arm NN expects the filter's height and width indices to match the input's height and
1017 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
1018 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
1019 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
1020 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
1021 model, data, ohwiToOihw) :
1022 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1023 const ConstTensorPin biasesPin =
1024 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
1025 if (!weightsPin.IsValid() || !biasesPin.IsValid())
1026 {
1027 return Fail("%s: Operation has invalid inputs", __func__);
1028 }
1029
1030 ConstTensor weights = weightsPin.GetConstTensor();
1031 ConstTensor biases = biasesPin.GetConstTensor();
1032 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
1033
1034 const TensorShape& inputShape = inputInfo.GetShape();
1035 const TensorShape& outputShape = outputInfo.GetShape();
1036 const TensorShape& weightsShape = weights.GetShape();
Kevin May42477c12020-03-26 13:34:14 +00001037
1038 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
1039 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
1040 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
1041 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
1042
1043 Convolution2dDescriptor desc;
1044 desc.m_DataLayout = dataLayout;
1045 desc.m_BiasEnabled = true;
1046
Finn Williamsf769f292021-06-25 12:53:09 +01001047 unsigned int numGroups;
Kevin May42477c12020-03-26 13:34:14 +00001048 ActivationFn activation;
1049
1050 if (operation.inputs.size() == 12)
1051 {
1052 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1053 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1054 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1055 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1056 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1057 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1058 !GetInputScalar<HalPolicy>(operation, 9, HalOperandType::INT32, numGroups, model, data) ||
1059 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data))
1060 {
1061 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
1062 }
1063
1064 }
1065 else if (operation.inputs.size() == 9)
1066 {
1067 android::nn::PaddingScheme paddingScheme;
1068 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
1069 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1070 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1071 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, numGroups, model, data) ||
1072 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
1073 {
1074 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
1075 }
1076
1077 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
1078 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
1079
1080 const uint32_t kernelX = weightsShape[widthIndex];
1081 const uint32_t kernelY = weightsShape[heightIndex];
1082
1083 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
1084 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
1085 }
1086 else
1087 {
1088 return Fail("%s: Unsupported number of operation inputs", __func__);
1089 }
1090
Finn Williamsb0331172020-10-08 14:33:13 +01001091 // Equivalent to outputShape[channelsIndex], but we can't know the outputShape in the case of dynamic tensors
1092 const unsigned int outputChannels = weightsShape[0];
Kevin May42477c12020-03-26 13:34:14 +00001093
1094 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
1095 const unsigned int channelMultiplier = outputChannels / numGroups;
1096
1097 //
1098 // Validate all relevant inputs
1099 //
1100 if (numGroups <= 0)
1101 {
1102 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
1103 }
1104
1105 if (outputChannels % numGroups != 0u)
1106 {
1107 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
1108 }
1109
1110 //
1111 // Set up Splitter layer
1112 //
1113 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
1114 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
1115
1116 TensorInfo splitterOutputInfo(4,
1117 splitterDimSizes,
1118 inputInfo.GetDataType(),
1119 inputInfo.GetQuantizationScale(),
1120 inputInfo.GetQuantizationOffset());
1121
1122 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
1123
1124 ViewsDescriptor splitterDesc(numGroups);
1125 for (unsigned int group = 0u; group < numGroups; ++group)
1126 {
1127 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
1128 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
1129 {
1130 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
1131 }
1132 }
1133
1134 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001135 armnn::BackendId setBackendSplit;
Kevin May42477c12020-03-26 13:34:14 +00001136 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1137 IsSplitterSupported,
1138 data.m_Backends,
1139 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001140 setBackendSplit,
Kevin May42477c12020-03-26 13:34:14 +00001141 inputInfo,
1142 splitterOutputInfos,
1143 splitterDesc);
1144 if (!isSupported)
1145 {
1146 return false;
1147 }
1148
1149 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001150 splitterLayer->SetBackendId(setBackendSplit);
Kevin May42477c12020-03-26 13:34:14 +00001151 if (!splitterLayer)
1152 {
1153 return Fail("%s: Failed to add SplitterLayer", __func__);
1154 }
1155
1156 input.Connect(splitterLayer->GetInputSlot(0));
1157 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
1158 {
1159 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
1160 }
1161
1162 //
1163 // Set up Convolution2d layers for each group
1164 //
1165
1166 // Set up group tensor shapes
1167 TensorShape groupInputShape(inputShape);
1168 groupInputShape[channelsIndex] = channelsPerGroup;
1169
Kevin May42477c12020-03-26 13:34:14 +00001170 TensorShape groupWeightsShape(weightsShape);
1171 groupWeightsShape[0] /= channelMultiplier * numGroups;
1172
1173 TensorShape groupBiasesShape({ 1 });
1174
1175 // Set up group tensor infos
1176 TensorInfo groupInputInfo(inputInfo);
1177 groupInputInfo.SetShape(groupInputShape);
1178
1179 const TensorInfo& weightsInfo = weights.GetInfo();
1180 TensorInfo groupWeightsInfo(weightsInfo);
1181 groupWeightsInfo.SetShape(groupWeightsShape);
1182
1183 const TensorInfo& biasesInfo = biases.GetInfo();
1184 TensorInfo groupBiasesInfo(biasesInfo);
1185 groupBiasesInfo.SetShape(groupBiasesShape);
1186
1187 TensorInfo groupOutputInfo(outputInfo);
Finn Williamsb0331172020-10-08 14:33:13 +01001188
1189 TensorShape groupOutputShape(outputShape);
1190 const bool isDynamic = IsDynamicTensor(outputInfo);
1191 if (!isDynamic)
1192 {
1193 groupOutputShape[channelsIndex] = 1;
1194 }
Kevin May42477c12020-03-26 13:34:14 +00001195 groupOutputInfo.SetShape(groupOutputShape);
1196
1197 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
1198 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
1199
1200 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
1201 for (unsigned int group = 0u; group < numGroups; ++group)
1202 {
1203 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1204 {
1205 auto index = group * channelMultiplier + m;
1206
1207 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
1208 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
1209
1210 if (weightsInfo.HasPerAxisQuantization())
1211 {
1212 // Extract per-axis quantization scales for group weights
1213 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
1214 groupWeightsInfo.SetQuantizationScales(
1215 std::vector<float>(weightsQuantScales.begin() + index,
1216 weightsQuantScales.begin() + index + groupWeightsShape[0]));
1217
1218 // Extract per-axis quantization scales for group biases
1219 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
1220 groupBiasesInfo.SetQuantizationScales(
1221 std::vector<float>(biasesQuantScales.begin() + index,
1222 biasesQuantScales.begin() + index + groupWeightsShape[0]));
1223 }
1224
1225 // Extract weights and biases data for current group convolution
1226 ConstTensor groupWeights(groupWeightsInfo,
1227 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
1228 weightsDataOffset));
1229 ConstTensor groupBiases(groupBiasesInfo,
1230 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
1231 biasesDataOffset));
1232
1233 isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001234 armnn::BackendId setBackendConv;
Finn Williamsb0331172020-10-08 14:33:13 +01001235 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1236 {
1237 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1238 IsConvolution2dSupported,
1239 data.m_Backends,
1240 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001241 setBackendConv,
Finn Williamsb0331172020-10-08 14:33:13 +01001242 groupInputInfo,
1243 outputInfo,
1244 desc,
1245 groupWeightsInfo,
1246 Optional<TensorInfo>(groupBiasesInfo));
1247 };
1248
1249 if(!isDynamic)
1250 {
1251 validateFunc(groupOutputInfo, isSupported);
1252 }
1253 else
1254 {
1255 isSupported = AreDynamicTensorsSupported();
1256 }
1257
Kevin May42477c12020-03-26 13:34:14 +00001258 if (!isSupported)
1259 {
1260 return false;
1261 }
1262
Teresa Charlinf8696262022-08-30 15:55:28 +01001263 IConnectableLayer* weightsLayer = data.m_Network->AddConstantLayer(groupWeights);
1264 IConnectableLayer* biasLayer = data.m_Network->AddConstantLayer(groupBiases);
1265 IConnectableLayer* convLayer = data.m_Network->AddConvolution2dLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001266 convLayer->SetBackendId(setBackendConv);
Keith Davis8f22bed2022-04-29 10:57:27 +01001267
Kevin May42477c12020-03-26 13:34:14 +00001268 if (!convLayer)
1269 {
1270 return Fail("%s: AddConvolution2dLayer failed", __func__);
1271 }
1272
1273 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
Teresa Charlinf8696262022-08-30 15:55:28 +01001274 weightsLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(1));
1275 biasLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(2));
1276
1277 weightsLayer->GetOutputSlot(0).SetTensorInfo(groupWeightsInfo);
1278 biasLayer->GetOutputSlot(0).SetTensorInfo(groupBiasesInfo);
Kevin May42477c12020-03-26 13:34:14 +00001279 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1280
Finn Williamsb0331172020-10-08 14:33:13 +01001281 if(isDynamic)
1282 {
1283 convLayer->GetOutputSlot(0).IsTensorInfoSet();
1284
1285 validateFunc(convLayer->GetOutputSlot(0).GetTensorInfo(), isSupported);
1286
1287 outputInfo = convLayer->GetOutputSlot(0).GetTensorInfo();
1288
1289 if (!isSupported)
1290 {
1291 return false;
1292 }
1293 }
1294
Kevin May42477c12020-03-26 13:34:14 +00001295 convLayers[index] = convLayer;
1296 }
1297 }
1298
1299 //
1300 // Set up Concat layer
1301 //
Finn Williamsb0331172020-10-08 14:33:13 +01001302 ConcatDescriptor concatDescriptor;
1303 // Equivalent to outputShape[channelsIndex], but we can't know the outputShape in the case of dynamic tensors
1304 concatDescriptor = ConcatDescriptor(weightsShape[0]);
Kevin May42477c12020-03-26 13:34:14 +00001305 for (unsigned int group = 0u; group < numGroups; ++group)
1306 {
1307 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1308 {
1309 auto index = group * channelMultiplier + m;
1310 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1311 concatDescriptor.SetConcatAxis(channelsIndex);
1312 }
1313 }
1314
1315 isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001316 armnn::BackendId setBackendConcat;
Finn Williamsb0331172020-10-08 14:33:13 +01001317 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1318 IsConcatSupported,
1319 data.m_Backends,
1320 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001321 setBackendConcat,
Finn Williamsb0331172020-10-08 14:33:13 +01001322 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1323 outputInfo,
1324 concatDescriptor);
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001325
Kevin May42477c12020-03-26 13:34:14 +00001326 if (!isSupported)
1327 {
1328 return false;
1329 }
1330
1331 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001332 concatLayer->SetBackendId(setBackendConcat);
Kevin May42477c12020-03-26 13:34:14 +00001333 if (!concatLayer)
1334 {
1335 return Fail("%s: AddConcatLayer failed", __func__);
1336 }
1337
1338 for (unsigned int group = 0u; group < numGroups; ++group)
1339 {
1340 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1341 {
1342 auto index = group * channelMultiplier + m;
1343 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1344 }
1345 }
1346 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1347
Kevin Mayfcf2a152020-09-08 16:06:32 +01001348 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *concatLayer, model,
Finn Williamsb0331172020-10-08 14:33:13 +01001349 data, nullptr, nullptr, activation);
Kevin May42477c12020-03-26 13:34:14 +00001350}
1351
1352template<typename HalPolicy,
1353 typename HalOperation = typename HalPolicy::Operation,
1354 typename HalModel = typename HalPolicy::Model>
1355bool ConvertInstanceNormalization(const HalOperation& operation, const HalModel& model, ConversionData& data)
1356{
1357 using HalOperand = typename HalPolicy::Operand;
1358 using HalOperandType = typename HalPolicy::OperandType;
1359
1360 ALOGV("HalPolicy::ConvertInstanceNormalization()");
1361
1362 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1363 if (!input.IsValid())
1364 {
1365 return Fail("%s: Operation has an invalid input 0", __func__);
1366 }
1367
1368 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1369 if (!output)
1370 {
1371 return Fail("%s: Operation has an invalid output", __func__);
1372 }
1373
1374 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001375
1376 // Determine data type of input tensor
1377 HalOperandType inputType;
1378 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1379 {
1380 return Fail("%s: Operation has invalid inputs", __func__);
1381 }
1382
1383 InstanceNormalizationDescriptor desc;
1384
1385 // Read gamma, beta & epsilon
1386 if (inputType == HalOperandType::TENSOR_FLOAT16)
1387 {
1388 Half fp16Gamma;
1389 Half fp16Beta;
1390 Half fp16Epsilon;
1391
1392 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Gamma, model, data) ||
1393 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, fp16Beta, model, data) ||
1394 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT16, fp16Epsilon, model, data))
1395 {
1396 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1397 }
1398
1399 desc.m_Gamma = static_cast<float>(fp16Gamma);
1400 desc.m_Beta = static_cast<float>(fp16Beta);
1401 desc.m_Eps = static_cast<float>(fp16Epsilon);
1402 }
1403 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1404 {
1405 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, desc.m_Gamma, model, data) ||
1406 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT32, desc.m_Beta, model, data) ||
1407 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT32, desc.m_Eps, model, data))
1408 {
1409 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1410 }
1411 }
1412 else
1413 {
1414 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1415 }
1416
1417 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 4, model, data);
1418
1419 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001420 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001421 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1422 {
1423 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1424 IsInstanceNormalizationSupported,
1425 data.m_Backends,
1426 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001427 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001428 input.GetTensorInfo(),
1429 outputInfo,
1430 desc);
1431 };
1432
1433 if(IsDynamicTensor(outputInfo))
1434 {
1435 isSupported = AreDynamicTensorsSupported();
1436 }
1437 else
1438 {
1439 validateFunc(outputInfo, isSupported);
1440 }
1441
Kevin May42477c12020-03-26 13:34:14 +00001442 if (!isSupported)
1443 {
1444 return false;
1445 }
1446
1447 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001448 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00001449 input.Connect(layer->GetInputSlot(0));
1450
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001451 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001452}
1453
1454template<typename HalPolicy,
1455 typename HalOperation = typename HalPolicy::Operation,
1456 typename HalModel = typename HalPolicy::Model>
1457bool ConvertLogSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
1458{
1459 using HalOperand = typename HalPolicy::Operand;
1460 using HalOperandType = typename HalPolicy::OperandType;
1461
1462 ALOGV("HalPolicy::ConvertLogSoftmax()");
1463
1464 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1465 if (!input.IsValid())
1466 {
1467 return Fail("%s: Failed to read input 0", __func__);
1468 }
1469
1470 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1471 if (!output)
1472 {
1473 return Fail("%s: Failed to read output", __func__);
1474 }
1475
1476 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001477
1478 // Determine data type of input tensor
1479 HalOperandType inputType;
1480 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1481 {
1482 return Fail("%s: Operation has invalid inputs", __func__);
1483 }
1484
1485 LogSoftmaxDescriptor descriptor;
1486
1487 // Read beta
1488 if (inputType == HalOperandType::TENSOR_FLOAT16)
1489 {
1490 Half fp16Beta;
1491 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Beta, model, data))
1492 {
1493 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1494 }
1495
1496 descriptor.m_Beta = static_cast<float>(fp16Beta);
1497 }
1498 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1499 {
1500 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, descriptor.m_Beta, model, data))
1501 {
1502 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1503 }
1504 }
1505 else
1506 {
1507 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1508 }
1509
1510 // Read axis
1511 if (!GetInputInt32<HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1512 {
1513 return Fail("%s: Failed to read input 2", __func__);
1514 }
1515
1516 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001517 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001518 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1519 {
1520 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1521 IsLogSoftmaxSupported,
1522 data.m_Backends,
1523 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001524 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001525 input.GetTensorInfo(),
1526 outputInfo,
1527 descriptor);
1528 };
1529
1530 if(IsDynamicTensor(outputInfo))
1531 {
1532 isSupported = AreDynamicTensorsSupported();
1533 }
1534 else
1535 {
1536 validateFunc(outputInfo, isSupported);
1537 }
1538
Kevin May42477c12020-03-26 13:34:14 +00001539 if (!isSupported)
1540 {
1541 return false;
1542 }
1543
1544 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001545 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00001546 if (!layer)
1547 {
Mike Kellye2d611e2021-10-14 12:35:58 +01001548 return Fail("%s: Could not add the LogSoftmaxLayer", __func__);
Kevin May42477c12020-03-26 13:34:14 +00001549 }
Kevin May42477c12020-03-26 13:34:14 +00001550 input.Connect(layer->GetInputSlot(0));
1551
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001552 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001553}
1554
1555template<typename HalPolicy,
1556 typename HalOperation = typename HalPolicy::Operation,
1557 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +00001558bool ConvertPadV2(const HalOperation& operation, const HalModel& model, ConversionData& data)
1559{
1560 using HalOperand = typename HalPolicy::Operand;
1561 using HalOperandType = typename HalPolicy::OperandType;
1562
1563 ALOGV("HalPolicy::ConvertPadV2()");
1564
1565 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1566 if (!input.IsValid())
1567 {
1568 return Fail("%s: Could not read input 0", __func__);
1569 }
1570
1571 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1572 if (!output)
1573 {
1574 return Fail("%s: Could not read output", __func__);
1575 }
1576
1577 const TensorInfo& inputInfo = input.GetTensorInfo();
1578 unsigned int rank = inputInfo.GetNumDimensions();
1579
1580 PadDescriptor descriptor;
1581 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
1582 {
1583 return Fail("%s: Could not convert paddings", __func__);
1584 }
1585
1586 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001587
1588 // Determine type of padding value
1589 HalOperandType operandType0;
1590 HalOperandType operandType2;
1591
1592 if (!GetOperandType<HalPolicy>(operation, 0, model, operandType0) ||
1593 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1594 {
1595 return Fail("%s: Operation has invalid inputs", __func__);
1596 }
1597
1598 // Read value to use for padding
1599 if (operandType0 == HalOperandType::TENSOR_FLOAT16 && operandType2 == HalOperandType::FLOAT16)
1600 {
1601 Half f16PadValue;
1602 if (!GetInputScalar<HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1603 {
1604 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1605 }
1606
1607 descriptor.m_PadValue = f16PadValue;
1608 }
1609 else if (operandType0 == HalOperandType::TENSOR_FLOAT32 && operandType2 == HalOperandType::FLOAT32)
1610 {
1611 if (!GetInputFloat32<HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1612 {
1613 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1614 }
1615 }
Teresa Charlind3381d52021-06-02 18:35:16 +01001616 else if (isQuantizedOperand(operandType0) && operandType2 == HalOperandType::INT32)
Kevin May42477c12020-03-26 13:34:14 +00001617 {
1618 int32_t intPadValue = 0;
1619 if (!GetInputInt32<HalPolicy>(operation, 2, intPadValue, model, data))
1620 {
1621 return Fail("%s: Could not read input 2 (INT32)", __func__);
1622 }
1623 descriptor.m_PadValue = intPadValue;
1624 }
1625 else
1626 {
1627 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1628 }
1629
1630 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001631 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001632 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1633 {
1634 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1635 IsPadSupported,
1636 data.m_Backends,
1637 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001638 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001639 inputInfo,
1640 outputInfo,
1641 descriptor);
1642 };
1643
1644 if(IsDynamicTensor(outputInfo))
1645 {
1646 isSupported = AreDynamicTensorsSupported();
1647 }
1648 else
1649 {
1650 validateFunc(outputInfo, isSupported);
1651 }
1652
Kevin May42477c12020-03-26 13:34:14 +00001653 if (!isSupported)
1654 {
1655 return false;
1656 }
1657
1658 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001659 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01001660 if (!layer)
1661 {
1662 return Fail("%s: Could not add the PadLayer", __func__);
1663 }
Kevin May42477c12020-03-26 13:34:14 +00001664 input.Connect(layer->GetInputSlot(0));
Kevin May42477c12020-03-26 13:34:14 +00001665
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001666 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001667}
1668
1669template<typename HalPolicy,
1670 typename HalOperation = typename HalPolicy::Operation,
1671 typename HalModel = typename HalPolicy::Model>
1672bool ConvertPrelu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1673{
1674 using HalOperand = typename HalPolicy::Operand;
1675
1676 ALOGV("HalPolicy::ConvertPrelu()");
1677
1678 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1679 LayerInputHandle alpha = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1680
1681 if (!input.IsValid() || !alpha.IsValid())
1682 {
1683 return Fail("%s: Operation has invalid inputs", __func__);
1684 }
1685
1686 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1687
1688 if (!output)
1689 {
1690 return Fail("%s: Could not read output", __func__);
1691 }
1692
1693 const TensorInfo& inputInfo = input.GetTensorInfo();
1694 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1695 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1696
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001697 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001698 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001699 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
Kevin May42477c12020-03-26 13:34:14 +00001700 {
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001701 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1702 IsPreluSupported,
1703 data.m_Backends,
1704 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001705 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001706 inputInfo,
1707 alphaInfo,
1708 outputInfo);
1709 };
1710
1711 if(IsDynamicTensor(outputInfo))
1712 {
1713 isSupported = AreDynamicTensorsSupported();
1714 }
1715 else
1716 {
1717 validateFunc(outputInfo, isSupported);
Kevin May42477c12020-03-26 13:34:14 +00001718 }
1719
Kevin May42477c12020-03-26 13:34:14 +00001720 if (!isSupported)
1721 {
1722 return false;
1723 }
1724
1725 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Cathal Corbett8de96f72022-09-01 13:34:59 +01001726 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00001727 if (!layer)
1728 {
Mike Kellye2d611e2021-10-14 12:35:58 +01001729 return Fail("%s: Could not add the PreluLayer", __func__);
Kevin May42477c12020-03-26 13:34:14 +00001730 }
1731
1732 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1733 if (!isReshapeSupported)
1734 {
1735 return false;
1736 }
1737
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001738 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001739}
1740
1741template<typename HalPolicy,
1742 typename HalOperation = typename HalPolicy::Operation,
1743 typename HalModel = typename HalPolicy::Model>
1744bool ConvertQuantize(const HalOperation& operation, const HalModel& model, ConversionData& data)
1745{
1746 using HalOperand = typename HalPolicy::Operand;
1747
1748 ALOGV("HalPolicy::ConvertQuantize()");
1749
1750 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1751 if (!input.IsValid())
1752 {
1753 return Fail("%s: Operation has invalid input", __func__);
1754 }
1755
1756 const HalOperand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1757 if (!outputOperand)
1758 {
1759 return Fail("%s: Operation has invalid outputs", __func__);
1760 }
1761
1762 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001763
1764 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001765 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001766 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1767 {
1768 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1769 IsQuantizeSupported,
1770 data.m_Backends,
1771 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001772 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001773 input.GetTensorInfo(),
1774 outputInfo);
1775 };
1776
1777 if(IsDynamicTensor(outputInfo))
1778 {
1779 isSupported = AreDynamicTensorsSupported();
1780 }
1781 else
1782 {
1783 validateFunc(outputInfo, isSupported);
1784 }
1785
Kevin May42477c12020-03-26 13:34:14 +00001786 if (!isSupported)
1787 {
1788 return false;
1789 }
1790
1791 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Cathal Corbett8de96f72022-09-01 13:34:59 +01001792 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01001793 if (!layer)
1794 {
1795 return Fail("%s: Could not add the QuantizeLayer", __func__);
1796 }
Kevin May42477c12020-03-26 13:34:14 +00001797 input.Connect(layer->GetInputSlot(0));
1798
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001799 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001800}
1801
1802template<typename HalPolicy,
1803 typename HalOperation = typename HalPolicy::Operation,
1804 typename HalModel = typename HalPolicy::Model>
Sadik Armagan813f2302020-05-19 14:10:30 +01001805bool ConvertQuantized16BitLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
Kevin May42477c12020-03-26 13:34:14 +00001806{
1807 using HalOperand = typename HalPolicy::Operand;
1808
Sadik Armagan813f2302020-05-19 14:10:30 +01001809 ALOGV("HalPolicy::ConvertQuantized16BitLstm()");
Kevin May42477c12020-03-26 13:34:14 +00001810
1811 //Inputs:
1812 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1813 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1814 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1815 if (!input.IsValid())
1816 {
1817 return Fail("%s: Could not read input 0: input", __func__);
1818 }
1819
1820 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1821 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1822 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1823 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 13, model, data);
1824 if (!previousCellStateIn.IsValid())
1825 {
1826 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1827 }
1828
1829 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1830 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1831 // is quantized with a fixed quantization range of -1, 127/128.
1832 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<HalPolicy>(operation, 14, model, data);
1833 if (!previousOutputIn.IsValid())
1834 {
1835 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1836 }
1837
1838 // Get the input tensors:
1839 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1840 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1841 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1842 const ConstTensorPin inputToInputWeightsPin =
1843 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1844
1845 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1846 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1847 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1848 const ConstTensorPin inputToForgetWeightsPin =
1849 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
1850
1851 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1852 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1853 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1854 const ConstTensorPin inputToCellWeightsPin =
1855 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 3, model, data);
1856
1857 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1858 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1859 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1860 const ConstTensorPin inputToOutputWeightsPin =
1861 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 4, model, data);
1862
1863 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1864 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1865 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1866 const ConstTensorPin recurrentToInputWeightsPin =
1867 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 5, model, data);
1868
1869 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1870 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1871 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1872 const ConstTensorPin recurrentToForgetWeightsPin =
1873 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 6, model, data);
1874
1875 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1876 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1877 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1878 const ConstTensorPin recurrentToCellWeightsPin =
1879 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 7, model, data);
1880
1881 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1882 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1883 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1884 const ConstTensorPin recurrentToOutputWeightsPin =
1885 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 8, model, data);
1886
1887 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1888 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1889 // of input and weights scales and zeroPoint equal to 0.
1890 const ConstTensorPin inputGateBiasPin =
1891 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 9, model, data);
1892
1893 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1894 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1895 // of input and weights scales and zeroPoint equal to 0.
1896 const ConstTensorPin forgetGateBiasPin =
1897 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 10, model, data);
1898
1899 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1900 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1901 // and weights scales and zeroPoint equal to 0.
1902 const ConstTensorPin cellBiasPin =
1903 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 11, model, data);
1904
1905 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1906 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1907 // of input and weights scales and zeroPoint equal to 0.
1908 const ConstTensorPin outputGateBiasPin =
1909 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 12, model, data);
1910
1911 if (!inputToInputWeightsPin.IsValid() ||
1912 !inputToForgetWeightsPin.IsValid() ||
1913 !inputToCellWeightsPin.IsValid() ||
1914 !inputToOutputWeightsPin.IsValid() ||
1915 !recurrentToInputWeightsPin.IsValid() ||
1916 !recurrentToForgetWeightsPin.IsValid() ||
1917 !recurrentToCellWeightsPin.IsValid() ||
1918 !recurrentToOutputWeightsPin.IsValid() ||
1919 !inputGateBiasPin.IsValid() ||
1920 !forgetGateBiasPin.IsValid() ||
1921 !cellBiasPin.IsValid() ||
1922 !outputGateBiasPin.IsValid())
1923 {
1924 return Fail("%s: Operation has invalid tensor inputs", __func__);
1925 }
1926
1927 // Outputs:
1928 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1929 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1930 // of -2^4, 2^4 * 32767/32768.
1931 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 0, model);
1932 if (!cellStateOut)
1933 {
1934 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1935 }
1936
1937 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1938 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1939 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 1, model);
1940 if (!output)
1941 {
1942 return Fail("%s: Could not read output 1: output", __func__);
1943 }
1944
1945 // Inputs
1946 const TensorInfo& inputInfo = input.GetTensorInfo();
1947 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1948 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
1949
1950 // Outputs
1951 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1952 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1953
1954 // Dynamic tensors currently not supported
1955 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1956 {
1957 return Fail("%s: Dynamic output tensors are not supported", __func__);
1958 }
1959
1960 QuantizedLstmInputParams params;
1961
1962 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1963 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1964 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1965 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1966 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1967 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1968 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1969 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1970 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1971 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1972 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1973 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1974
1975 QuantizedLstmInputParamsInfo paramsInfo;
1976 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1977 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1978 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1979 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1980 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1981 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1982 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1983 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1984 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1985 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1986 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1987 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1988
1989 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001990 armnn::BackendId setBackend;
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01001991 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1992 {
1993 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1994 IsQuantizedLstmSupported,
1995 data.m_Backends,
1996 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001997 setBackend,
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01001998 inputInfo,
1999 previousCellStateInInfo,
2000 previousOutputInInfo,
2001 cellStateOutInfo,
2002 outputInfo,
2003 paramsInfo);
2004 };
2005
2006 bool isDynamic = false;
2007 if (!IsDynamicTensor(cellStateOutInfo) &&
2008 !IsDynamicTensor(outputInfo))
2009 {
2010 validateFunc(outputInfo, isSupported);
2011 }
2012 else
2013 {
2014 isDynamic = true;
2015 isSupported = AreDynamicTensorsSupported();
2016 }
Kevin May42477c12020-03-26 13:34:14 +00002017
2018 if (!isSupported)
2019 {
2020 return false;
2021 }
2022
2023 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
Cathal Corbett8de96f72022-09-01 13:34:59 +01002024 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00002025 input.Connect(layer->GetInputSlot(0));
2026 previousCellStateIn.Connect(layer->GetInputSlot(1));
2027 previousOutputIn.Connect(layer->GetInputSlot(2));
2028
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002029 if (!isDynamic)
2030 {
2031 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2032 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data));
2033 }
2034 else
2035 {
2036 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2037 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01002038 operation, 1, *layer, 1, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002039 }
2040
Kevin May42477c12020-03-26 13:34:14 +00002041}
2042
2043template<typename HalPolicy,
2044 typename HalOperation = typename HalPolicy::Operation,
2045 typename HalModel = typename HalPolicy::Model>
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002046bool ConvertReduce(const HalOperation& operation,
2047 const HalModel& model,
2048 ConversionData& data,
2049 ReduceOperation reduceOperation)
2050{
2051 using HalOperand = typename HalPolicy::Operand;
2052 using HalOperandType = typename HalPolicy::OperandType;
2053
2054 armnn::ReduceDescriptor descriptor;
2055 descriptor.m_ReduceOperation = reduceOperation;
2056
2057 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2058 if (!input.IsValid())
2059 {
2060 return Fail("%s: Operation has invalid inputs", __func__);
2061 }
2062 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2063
2064 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2065 if (!output)
2066 {
2067 return Fail("%s: Could not read output 0", __func__);
2068 }
2069 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2070
2071 const HalOperand* axisOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2072 if (!axisOperand)
2073 {
2074 return Fail("%s: Could not read input 1", __func__);
2075 }
2076 std::vector<int32_t> axis;
2077 if (!GetTensorInt32Values<HalPolicy>(*axisOperand, axis, model, data))
2078 {
2079 return Fail("%s: Input 1 has invalid values", __func__);
2080 }
2081
2082 // Convert the axis to unsigned int and remove duplicates.
2083 unsigned int rank = inputInfo.GetNumDimensions();
2084 std::set<unsigned int> uniqueAxis;
2085 std::transform(axis.begin(), axis.end(),
2086 std::inserter(uniqueAxis, uniqueAxis.begin()),
2087 [rank](int i) -> unsigned int { return (i + rank) % rank; });
2088 descriptor.m_vAxis.assign(uniqueAxis.begin(), uniqueAxis.end());
2089
2090 // Get the "keep dims" flag.
2091 if (!GetInputScalar<HalPolicy>(operation, 2, HalOperandType::BOOL, descriptor.m_KeepDims, model, data))
2092 {
2093 return Fail("%s: Could not read input 2", __func__);
2094 }
2095
2096 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002097 armnn::BackendId setBackend;
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002098 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2099 {
2100 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2101 IsReduceSupported,
2102 data.m_Backends,
2103 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002104 setBackend,
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002105 inputInfo,
2106 outputInfo,
2107 descriptor);
2108 };
2109
2110 if(!IsDynamicTensor(outputInfo))
2111 {
2112 validateFunc(outputInfo, isSupported);
2113 }
2114 else
2115 {
2116 isSupported = AreDynamicTensorsSupported();
2117 }
2118
2119 if (!isSupported)
2120 {
2121 return false;
2122 }
2123
2124 armnn::IConnectableLayer* const layer = data.m_Network->AddReduceLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +01002125 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01002126 if (!layer)
2127 {
2128 return Fail("%s: Could not add the ReduceLayer", __func__);
2129 }
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002130 input.Connect(layer->GetInputSlot(0));
2131
2132 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
2133}
2134
2135template<typename HalPolicy,
2136 typename HalOperation = typename HalPolicy::Operation,
2137 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +00002138bool ConvertResize(const HalOperation& operation,
2139 const HalModel& model,
2140 ConversionData& data,
2141 ResizeMethod resizeMethod)
2142{
2143 using HalOperand = typename HalPolicy::Operand;
2144 using HalOperandType = typename HalPolicy::OperandType;
2145 ALOGV("HalPolicy::ConvertResize()");
2146 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
2147
2148 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2149 if (!input.IsValid())
2150 {
2151 return Fail("%s: Could not read input 0", __func__);
2152 }
2153
2154 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2155 if (!output)
2156 {
2157 return Fail("%s: Could not read output 0", __func__);
2158 }
2159
2160 const TensorInfo& inputInfo = input.GetTensorInfo();
2161 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2162
Kevin May42477c12020-03-26 13:34:14 +00002163 ResizeDescriptor descriptor;
2164 descriptor.m_Method = resizeMethod;
2165 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
2166
2167 HalOperandType operandType1;
2168 HalOperandType operandType2;
2169
2170 if (!GetOperandType<HalPolicy>(operation, 1, model, operandType1) ||
2171 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
2172 {
2173 return Fail("%s: Operation has invalid inputs", __func__);
2174 }
2175
2176 if (operandType1 != operandType2)
2177 {
2178 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
2179 }
2180
2181 if (operandType1 == HalOperandType::INT32)
2182 {
2183 // Case 1: resizing by shape
2184 int32_t targetWidth = 0;
2185 int32_t targetHeight = 0;
2186
2187 if (!GetInputInt32<HalPolicy>(operation, 1, targetWidth, model, data) ||
2188 !GetInputInt32<HalPolicy>(operation, 2, targetHeight, model, data))
2189 {
2190 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
2191 }
2192
2193 if (targetWidth < 0 || targetHeight < 0)
2194 {
2195 return Fail("%s: Operation has invalid inputs for resizing by shape. "
2196 "Target width/height cannot be < 0", __func__);
2197 }
2198
2199 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
2200 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
2201 }
2202 else if (operandType1 == HalOperandType::FLOAT32)
2203 {
2204 // Case 2: resizing by scale
2205 float widthScale = 1.0f;
2206 float heightScale = 1.0f;
2207
2208 if (!GetInputFloat32<HalPolicy>(operation, 1, widthScale, model, data) ||
2209 !GetInputFloat32<HalPolicy>(operation, 2, heightScale, model, data))
2210 {
2211 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2212 }
2213
2214 const TensorShape& inputShape = inputInfo.GetShape();
2215 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2216
2217 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
2218 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
2219
2220 descriptor.m_TargetWidth = std::floor(width * widthScale);
2221 descriptor.m_TargetHeight = std::floor(height * heightScale);
2222 }
2223 else if (operandType1 == HalOperandType::FLOAT16)
2224 {
2225 Half widthScale;
2226 Half heightScale;
2227
2228 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, widthScale, model, data) ||
2229 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, heightScale, model, data))
2230 {
2231 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2232 }
2233
2234 const TensorShape& inputShape = inputInfo.GetShape();
2235 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2236
2237 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
2238 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
2239
2240 descriptor.m_TargetWidth = std::floor(width * widthScale);
2241 descriptor.m_TargetHeight = std::floor(height * heightScale);
2242 }
2243 else
2244 {
2245 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
2246 }
2247
David Monahanf057e6f2020-06-22 09:55:23 +01002248 descriptor.m_AlignCorners = GetOptionalBool<HalPolicy>(operation, 4, model, data);
2249 descriptor.m_HalfPixelCenters = GetOptionalBool<HalPolicy>(operation, 5, model, data);
David Monahan51e0b132020-04-20 16:12:06 +01002250
Kevin May42477c12020-03-26 13:34:14 +00002251 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002252 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002253 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2254 {
2255 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2256 IsResizeSupported,
2257 data.m_Backends,
2258 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002259 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002260 inputInfo,
2261 outputInfo,
2262 descriptor);
2263 };
2264
2265 if(IsDynamicTensor(outputInfo))
2266 {
2267 isSupported = AreDynamicTensorsSupported();
2268 }
2269 else
2270 {
2271 validateFunc(outputInfo, isSupported);
2272 }
Kevin May42477c12020-03-26 13:34:14 +00002273
2274 if (!isSupported)
2275 {
2276 return false;
2277 }
2278
2279 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +01002280 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01002281 if (!layer)
2282 {
2283 return Fail("%s: Could not add the ResizeLayer", __func__);
2284 }
Kevin May42477c12020-03-26 13:34:14 +00002285 input.Connect(layer->GetInputSlot(0));
2286
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002287 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002288}
2289
2290template<typename HalPolicy,
2291 typename HalOperation = typename HalPolicy::Operation,
2292 typename HalModel = typename HalPolicy::Model>
2293bool ConvertSpaceToDepth(const HalOperation& operation, const HalModel& model, ConversionData& data)
2294{
2295 using HalOperand = typename HalPolicy::Operand;
2296 using HalOperandType = typename HalPolicy::OperandType;
2297
2298 ALOGV("HalPolicy::ConvertSpaceToDepth()");
2299
2300 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2301 if (!input.IsValid() )
2302 {
2303 return Fail("%s: Operation has invalid inputs", __func__);
2304 }
2305
2306 const TensorInfo& inputInfo = input.GetTensorInfo();
2307 unsigned int rank = inputInfo.GetNumDimensions();
2308 if (rank != 4)
2309 {
2310 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2311 }
2312
2313 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2314 if (!output)
2315 {
2316 return Fail("%s: Could not read output 0", __func__);
2317 }
2318
2319 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002320
2321 SpaceToDepthDescriptor desc;
2322
2323 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_BlockSize, model, data);
2324
2325 if (desc.m_BlockSize <= 1)
2326 {
2327 return Fail("%s: Block size must be at least 1 in all dimensions");
2328 }
2329
2330 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
2331
2332 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002333 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002334 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2335 {
2336 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2337 IsSpaceToDepthSupported,
2338 data.m_Backends,
2339 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002340 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002341 inputInfo,
2342 outputInfo,
2343 desc);
2344 };
2345
2346 if(IsDynamicTensor(outputInfo))
2347 {
2348 isSupported = AreDynamicTensorsSupported();
2349 }
2350 else
2351 {
2352 validateFunc(outputInfo, isSupported);
2353 }
2354
Kevin May42477c12020-03-26 13:34:14 +00002355 if (!isSupported)
2356 {
2357 return false;
2358 }
2359
2360 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +01002361 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01002362 if (!layer)
2363 {
Mike Kelly1b46d132021-11-03 11:12:45 +00002364 return Fail("%s: Could not add the SpaceToDepthLayer", __func__);
Mike Kellye2d611e2021-10-14 12:35:58 +01002365 }
Kevin May42477c12020-03-26 13:34:14 +00002366 input.Connect(layer->GetInputSlot(0));
2367
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002368 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002369}
2370
2371template<typename HalPolicy,
2372 typename HalOperation = typename HalPolicy::Operation,
2373 typename HalModel = typename HalPolicy::Model>
2374bool ConvertSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
2375{
2376 using HalOperand = typename HalPolicy::Operand;
2377 using HalOperandType = typename HalPolicy::OperandType;
2378
2379 ALOGV("HalPolicy::ConvertSoftmax()");
2380
2381 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2382 if (!input.IsValid())
2383 {
2384 return Fail("%s: Operation has invalid inputs", __func__);
2385 }
2386
2387 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2388 if (!outputOperand)
2389 {
2390 return Fail("%s: Operation has no outputs", __func__);
2391 }
2392
2393 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00002394
2395 SoftmaxDescriptor desc;
Teresa Charlinbf866e22020-08-09 23:55:01 +01002396 HalOperandType outputType = outputOperand->type;
2397
2398 // Read beta value
2399 if (outputType == HalOperandType::TENSOR_FLOAT16)
Kevin May42477c12020-03-26 13:34:14 +00002400 {
Teresa Charlinbf866e22020-08-09 23:55:01 +01002401 Half value;
2402
2403 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, value, model, data))
2404 {
2405 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2406 }
2407
2408 desc.m_Beta = static_cast<float>(value);
2409 }
2410 else
2411 {
2412 if (!GetInputFloat32<HalPolicy>(operation, 1, desc.m_Beta, model, data))
2413 {
2414 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2415 }
Kevin May42477c12020-03-26 13:34:14 +00002416 }
2417
2418 if (operation.inputs.size() > 2 && !GetInputScalar<HalPolicy>(operation,
Teresa Charlinbf866e22020-08-09 23:55:01 +01002419 2,
2420 HalOperandType::INT32,
2421 desc.m_Axis,
2422 model,
2423 data))
Kevin May42477c12020-03-26 13:34:14 +00002424 {
2425 return Fail("%s: Operation has invalid inputs", __func__);
2426 }
2427
Kevin May42477c12020-03-26 13:34:14 +00002428 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002429 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002430 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2431 {
2432 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2433 IsSoftmaxSupported,
2434 data.m_Backends,
2435 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002436 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002437 input.GetTensorInfo(),
2438 outputInfo,
2439 desc);
2440 };
2441
2442 if(IsDynamicTensor(outputInfo))
2443 {
2444 isSupported = AreDynamicTensorsSupported();
2445 }
2446 else
2447 {
2448 validateFunc(outputInfo, isSupported);
2449 }
2450
Kevin May42477c12020-03-26 13:34:14 +00002451 if (!isSupported)
2452 {
2453 return false;
2454 }
2455
2456 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +01002457 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01002458 if (!layer)
2459 {
2460 return Fail("%s: Could not add the SoftmaxLayer", __func__);
2461 }
Kevin May42477c12020-03-26 13:34:14 +00002462 input.Connect(layer->GetInputSlot(0));
2463
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002464 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002465}
2466
2467template<typename HalPolicy,
2468 typename HalOperation = typename HalPolicy::Operation,
2469 typename HalModel = typename HalPolicy::Model>
2470bool ConvertLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
2471{
2472 using HalOperand = typename HalPolicy::Operand;
2473 using HalOperandType = typename HalPolicy::OperandType;
2474
2475 ALOGV("HalPolicy::ConvertLstm()");
2476
2477 // Inputs:
2478 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2479 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2480 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2481 if (!input.IsValid())
2482 {
2483 return Fail("%s: Could not read input 0: input", __func__);
2484 }
2485 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2486 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
2487 if (!outputStateIn.IsValid())
2488 {
2489 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2490 }
2491 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2492 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
2493 if (!cellStateIn.IsValid())
2494 {
2495 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2496 }
2497
2498 // Get the mandatory input tensors:
2499 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2500 // [num_units, input_size].
2501 const ConstTensorPin inputToForgetWeightsPin =
2502 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
2503 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2504 // [num_units, input_size].
2505 const ConstTensorPin inputToCellWeightsPin =
2506 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
2507 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2508 // [num_units, input_size].
2509 const ConstTensorPin inputToOutputWeightsPin =
2510 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
2511 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2512 // [num_units, output_size].
2513 const ConstTensorPin recurrentToForgetWeightsPin =
2514 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
2515 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2516 // [num_units, output_size].
2517 const ConstTensorPin recurrentToCellWeightsPin =
2518 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
2519 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2520 // [num_units, output_size].
2521 const ConstTensorPin recurrentToOutputWeightsPin =
2522 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
2523 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2524 const ConstTensorPin forgetGateBiasPin =
2525 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
2526 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2527 const ConstTensorPin cellBiasPin =
2528 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
2529 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2530 const ConstTensorPin outputGateBiasPin =
2531 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
2532
2533 if (!inputToForgetWeightsPin.IsValid() ||
2534 !inputToCellWeightsPin.IsValid() ||
2535 !inputToOutputWeightsPin.IsValid() ||
2536 !recurrentToForgetWeightsPin.IsValid() ||
2537 !recurrentToCellWeightsPin.IsValid() ||
2538 !recurrentToOutputWeightsPin.IsValid() ||
2539 !forgetGateBiasPin.IsValid() ||
2540 !cellBiasPin.IsValid() ||
2541 !outputGateBiasPin.IsValid())
2542 {
2543 return Fail("%s: Operation has invalid tensor inputs", __func__);
2544 }
2545
2546 // Get the optional input tensors:
2547 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2548 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2549 const ConstTensorPin inputToInputWeightsPin =
2550 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
2551 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2552 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2553 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2554 const ConstTensorPin recurrentToInputWeightsPin =
2555 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
2556 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2557 const ConstTensorPin cellToInputWeightsPin =
2558 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
2559 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2560 const ConstTensorPin cellToForgetWeightsPin =
2561 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
2562 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2563 const ConstTensorPin cellToOutputWeightsPin =
2564 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
2565 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2566 const ConstTensorPin inputGateBiasPin =
2567 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2568 12,
2569 model,
2570 data,
2571 g_DontPermute,
2572 nullptr,
2573 true);
2574
2575 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2576 // [output_size, num_units].
2577 const ConstTensorPin projectionWeightsPin =
2578 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
2579 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2580 const ConstTensorPin projectionBiasPin =
2581 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2582 17,
2583 model,
2584 data,
2585 g_DontPermute,
2586 nullptr,
2587 true);
2588
2589 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2590 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2591 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2592 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2593 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2594 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2595 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2596 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2597 {
2598 return Fail("%s: Operation has invalid tensor inputs", __func__);
2599 }
2600
2601 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2602 // 20: The activation function: A value indicating the activation function:
2603 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2604 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2605 // If set to 0.0 then clipping is disabled.
2606 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2607 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
Cathal Corbett3fe3a542022-05-25 09:35:37 +01002608 ActivationFn activation = ActivationFn::kActivationNone;
Kevin May42477c12020-03-26 13:34:14 +00002609 float cellClip;
2610 float projClip;
2611 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
2612 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
2613 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
2614 {
2615 return Fail("%s: Operation has invalid scalar inputs", __func__);
2616 }
2617
2618 // Get the normalization tensors
2619 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2620 // Used to rescale normalized inputs to activation at input gate.
2621 const ConstTensorPin inputLayerNormWeightsPin
2622 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 23, true));
2623
2624 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2625 // Used to rescale normalized inputs to activation at forget gate.
2626 const ConstTensorPin forgetLayerNormWeightsPin =
2627 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2628 24,
2629 model,
2630 data,
2631 g_DontPermute,
2632 nullptr,
2633 true);
2634
2635 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2636 // Used to rescale normalized inputs to activation at cell gate.
2637 const ConstTensorPin cellLayerNormWeightsPin =
2638 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2639 25,
2640 model,
2641 data,
2642 g_DontPermute,
2643 nullptr,
2644 true);
2645
2646 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2647 // Used to rescale normalized inputs to activation at output gate.
2648 const ConstTensorPin outputLayerNormWeightsPin =
2649 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2650 26,
2651 model,
2652 data,
2653 g_DontPermute,
2654 nullptr,
2655 true);
2656
2657 // Outputs:
2658 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2659 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2660 const HalOperand* scratchBuffer = GetOutputOperand<HalPolicy>(operation, 0, model);
2661 if (!scratchBuffer)
2662 {
2663 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2664 }
2665 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2666 const HalOperand* outputStateOut = GetOutputOperand<HalPolicy>(operation, 1, model);
2667 if (!outputStateOut)
2668 {
2669 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2670 }
2671 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2672 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 2, model);
2673 if (!cellStateOut)
2674 {
2675 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2676 }
2677 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2678 // effectively the same as the current “output state (out)” value.
2679 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 3, model);
2680 if (!output)
2681 {
2682 return Fail("%s: Could not read output 3: output", __func__);
2683 }
2684
2685 // set the params structure for the AddLstmLayer call
2686 LstmInputParams params;
2687 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2688 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2689 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2690 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2691 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2692 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2693 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2694 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2695 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2696 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2697 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2698 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2699 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2700 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2701 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2702 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2703 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2704 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2705 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2706 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2707 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2708
2709 // set the layer descriptor
2710 LstmDescriptor desc;
2711 desc.m_ActivationFunc = activation;
2712 desc.m_ClippingThresCell = cellClip;
2713 desc.m_ClippingThresProj = projClip;
2714 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2715 params.m_RecurrentToInputWeights == nullptr ||
2716 params.m_InputGateBias == nullptr);
2717 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2718 params.m_CellToOutputWeights != nullptr);
2719 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2720 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2721 params.m_ForgetLayerNormWeights != nullptr ||
2722 params.m_CellLayerNormWeights != nullptr ||
2723 params.m_OutputLayerNormWeights != nullptr);
2724
2725 // validate the optional input groups
2726 if (desc.m_CifgEnabled &&
2727 (params.m_InputToInputWeights != nullptr ||
2728 params.m_RecurrentToInputWeights != nullptr ||
2729 params.m_InputGateBias != nullptr))
2730 {
2731 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2732 " and input gate bias must be provided", __func__);
2733 }
2734
2735 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2736 {
2737 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2738 }
2739
2740 if (desc.m_PeepholeEnabled &&
2741 (params.m_CellToForgetWeights == nullptr ||
2742 params.m_CellToOutputWeights == nullptr ||
2743 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2744 {
2745 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2746 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2747 }
2748
2749 if (desc.m_LayerNormEnabled &&
2750 (params.m_ForgetLayerNormWeights == nullptr ||
2751 params.m_CellLayerNormWeights == nullptr ||
2752 params.m_OutputLayerNormWeights == nullptr ||
2753 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2754 {
2755 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2756 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2757 }
2758
2759 // Check if the layer is supported
2760 // Inputs
2761 const TensorInfo& inputInfo = input.GetTensorInfo();
2762 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2763 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
2764
2765 // Outputs
2766 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2767 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2768 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2769 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2770
Kevin May42477c12020-03-26 13:34:14 +00002771 // Basic parameters
2772 LstmInputParamsInfo paramsInfo;
2773 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2774 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2775 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2776 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2777 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2778 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2779 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2780 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2781 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2782
2783 // Optional parameters
2784 if (!desc.m_CifgEnabled)
2785 {
2786 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2787 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2788 if (params.m_CellToInputWeights != nullptr)
2789 {
2790 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2791 }
2792 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2793 }
2794
2795 if (desc.m_ProjectionEnabled)
2796 {
2797 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2798 if (params.m_ProjectionBias != nullptr)
2799 {
2800 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2801 }
2802 }
2803
2804 if (desc.m_PeepholeEnabled)
2805 {
2806 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2807 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2808 }
2809
2810 if (desc.m_LayerNormEnabled)
2811 {
2812 if(!desc.m_CifgEnabled)
2813 {
2814 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2815 }
2816 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2817 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2818 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2819 }
2820
2821 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002822 armnn::BackendId setBackend;
Sadik Armagandbda4b72020-09-03 11:33:07 +01002823 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2824 {
2825 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2826 IsLstmSupported,
2827 data.m_Backends,
2828 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002829 setBackend,
Sadik Armagandbda4b72020-09-03 11:33:07 +01002830 inputInfo,
2831 outputStateInInfo,
2832 cellStateInInfo,
2833 scratchBufferInfo,
2834 outputStateOutInfo,
2835 cellStateOutInfo,
2836 outputInfo,
2837 desc,
2838 paramsInfo);
2839 };
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002840
Sadik Armagandbda4b72020-09-03 11:33:07 +01002841 bool isDynamic = false;
2842 if (!IsDynamicTensor(outputStateOutInfo) &&
2843 !IsDynamicTensor(scratchBufferInfo) &&
2844 !IsDynamicTensor(cellStateOutInfo) &&
2845 !IsDynamicTensor(outputInfo))
2846 {
2847 validateFunc(outputInfo, isSupported);
2848 }
2849 else
2850 {
2851 isDynamic = true;
2852 isSupported = AreDynamicTensorsSupported();
2853 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002854
Kevin May42477c12020-03-26 13:34:14 +00002855 if (!isSupported)
2856 {
2857 return false;
2858 }
2859
2860 // Add the layer
2861 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Cathal Corbett8de96f72022-09-01 13:34:59 +01002862 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00002863
2864 input.Connect(layer->GetInputSlot(0));
2865 outputStateIn.Connect(layer->GetInputSlot(1));
2866 cellStateIn.Connect(layer->GetInputSlot(2));
2867
Sadik Armagandbda4b72020-09-03 11:33:07 +01002868 if (!isDynamic)
2869 {
2870 return (
2871 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2872 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2873 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2874 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 3, *layer, 3, model, data));
2875 }
2876 else
2877 {
2878 return (
2879 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2880 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2881 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2882 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01002883 operation, 3, *layer, 3, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armagandbda4b72020-09-03 11:33:07 +01002884 }
2885
Kevin May42477c12020-03-26 13:34:14 +00002886}
2887
2888template<typename HalPolicy,
2889 typename HalOperation = typename HalPolicy::Operation,
2890 typename HalModel = typename HalPolicy::Model>
2891bool ConvertTransposeConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2892{
2893 using HalOperand = typename HalPolicy::Operand;
2894 using HalOperandType = typename HalPolicy::OperandType;
2895
2896 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2897
2898 if (!input.IsValid())
2899 {
2900 return Fail("%s: Operation has invalid inputs", __func__);
2901 }
2902
2903 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2904
2905 if (!output)
2906 {
2907 return Fail("%s: Could not read output 0", __func__);
2908 }
2909
2910 const TensorInfo& inputInfo = input.GetTensorInfo();
2911 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002912
2913 // ArmNN does not currently support non-fixed weights or bias
2914 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2915 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2916
2917 if (weightsOperand == nullptr)
2918 {
2919 return Fail("%s: Operand is invalid", __func__);
2920 }
2921 TransposeConvolution2dDescriptor desc;
2922 desc.m_DataLayout = DataLayout::NHWC;
2923
2924 // Determine whether padding is implicit or explicit
2925 bool implicitPadding = operation.inputs.size() == 9;
2926
2927 if (implicitPadding )
2928 {
2929 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
2930 }
2931 else
2932 {
2933 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
2934 }
2935
2936 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2937 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
2938 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
2939
2940 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
2941
2942 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
2943 // We have to permute it to OIHW if the data layout is NCHW.
2944 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
2945 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
2946 model, data, OHWIToOIHW) :
2947 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
2948
2949 // Bias is a 1D tensor
2950 const ConstTensorPin biasPin =
2951 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
2952
2953 if (!weightsPin.IsValid())
2954 {
2955 return Fail("%s: Operation has invalid weights", __func__);
2956 }
2957
2958 if (!biasPin.IsValid())
2959 {
2960 return Fail("%s: Operation has invalid biases", __func__);
2961 }
2962
2963 ConstTensor weights = weightsPin.GetConstTensor();
2964 ConstTensor bias = biasPin.GetConstTensor();
2965 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
2966
2967 ActivationFn activation;
2968
2969 if (implicitPadding)
2970 {
2971 int32_t strideX{0};
2972 int32_t strideY{0};
2973 int32_t padLeft{0};
2974 int32_t padRight{0};
2975 int32_t padTop{0};
2976 int32_t padBottom{0};
2977
2978 android::nn::PaddingScheme paddingScheme;
2979 if (!GetInputPaddingScheme<HalPolicy>(operation, 4, paddingScheme, model, data) ||
2980 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, strideX, model, data) ||
2981 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, strideY, model, data) ||
2982 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
2983 {
2984 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
2985 }
2986
2987 const uint32_t kernelX = weights.GetShape()[widthIndex];
2988 const uint32_t kernelY = weights.GetShape()[heightIndex];
Kevin May42477c12020-03-26 13:34:14 +00002989
Colm Donelan8f3d33e2020-07-06 15:41:43 +01002990 // If output shape has been specified as a parameter then extract it and make it available.
2991 const HalOperand* outputShapeOperand = GetInputOperand<HalPolicy>(operation, 3, model, false);
2992 std::vector<int32_t> outputShape;
2993 if ((outputShapeOperand) && (GetTensorInt32Values<HalPolicy>(*outputShapeOperand, outputShape, model, data)))
2994 {
2995 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
2996 for (int dimension : outputShape)
2997 {
2998 desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
2999 }
3000 desc.m_OutputShapeEnabled = true;
3001 }
3002
Finn Williams8fe50c62020-10-09 15:52:57 +01003003 uint32_t outputX;
3004 uint32_t outputY;
3005
3006 if (IsDynamicTensor(outputInfo))
3007 {
3008 if (outputShape.size() == 0)
3009 {
3010 return Fail("%s: Padding sizes cannot be inferred", __func__);
3011 }
3012
3013 outputX = outputShape[widthIndex];
3014 outputY = outputShape[heightIndex];
3015 }
3016 else
3017 {
3018 outputX = outputInfo.GetShape()[widthIndex];
3019 outputY = outputInfo.GetShape()[heightIndex];
3020 }
3021
3022 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
3023 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
3024
3025 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
3026 // but Arm NN only supports values >= 0
3027 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
3028 {
3029 return Fail("%s: Negative padding values are not supported", __func__);
3030 }
3031
Matthew Sloyan9b088d92020-09-14 15:12:55 +01003032 desc.m_StrideX = armnn::numeric_cast<uint32_t>(strideX);
3033 desc.m_StrideY = armnn::numeric_cast<uint32_t>(strideY);
3034 desc.m_PadLeft = armnn::numeric_cast<uint32_t>(padLeft);
3035 desc.m_PadRight = armnn::numeric_cast<uint32_t>(padRight);
3036 desc.m_PadTop = armnn::numeric_cast<uint32_t>(padTop);
3037 desc.m_PadBottom = armnn::numeric_cast<uint32_t>(padBottom);
Kevin May42477c12020-03-26 13:34:14 +00003038 }
3039 else if (operation.inputs.size() == 11)
3040 {
3041 // explicit padding
3042 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
3043 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
3044 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
3045 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
3046 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
3047 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
3048 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
3049 {
3050 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
3051 }
3052 }
3053 else
3054 {
3055 return Fail("%s: Unsupported number of operation inputs", __func__);
3056 }
3057
3058 desc.m_BiasEnabled = true;
3059 Optional<TensorInfo> biases(bias.GetInfo());
3060
3061 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01003062 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01003063 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
3064 {
3065 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3066 IsTransposeConvolution2dSupported,
3067 data.m_Backends,
3068 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01003069 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01003070 inputInfo,
3071 outputInfo,
3072 desc,
3073 weights.GetInfo(),
3074 biases);
3075 };
3076
3077 if(IsDynamicTensor(outputInfo))
3078 {
3079 isSupported = AreDynamicTensorsSupported();
3080 }
3081 else
3082 {
3083 validateFunc(outputInfo, isSupported);
3084 }
Kevin May42477c12020-03-26 13:34:14 +00003085 if (!isSupported)
3086 {
3087 return false;
3088 }
3089
3090 IConnectableLayer* startLayer =
3091 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Cathal Corbett8de96f72022-09-01 13:34:59 +01003092 startLayer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00003093 if (!startLayer)
3094 {
3095 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
3096 }
3097
Kevin May42477c12020-03-26 13:34:14 +00003098 input.Connect(startLayer->GetInputSlot(0));
3099
Kevin Mayfcf2a152020-09-08 16:06:32 +01003100 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
3101 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +00003102}
3103
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003104template<typename HalPolicy,
3105 typename HalOperation = typename HalPolicy::Operation,
3106 typename HalModel = typename HalPolicy::Model>
3107bool ConvertUnidirectionalSequenceLstm(const HalOperation& operation,
3108 const HalModel& model,
3109 ConversionData& data)
3110{
3111 using HalOperand = typename HalPolicy::Operand;
3112 using HalOperandType = typename HalPolicy::OperandType;
3113
3114 ALOGV("HalPolicy::ConvertUnidirectionalSequenceLstm()");
3115
3116 // Determine if input OperandType is ANEURALNETWORKS_TENSOR_FLOAT 32 or 16
3117 HalOperandType inputType;
3118 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
3119 {
3120 return Fail("%s: Operation has invalid inputs", __func__);
3121 }
3122
3123 // Inputs:
3124 // 0: The input: A 3-D tensor of shape: If time-major: [max_time, batch_size, input_size] If batch-major:
3125 // [batch_size, max_time, input_size] where “max_time” is the number of timesteps (sequence length), “batch_size”
3126 // corresponds to the batching dimension, and “input_size” is the size of the input.
3127 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3128 if (!input.IsValid())
3129 {
3130 return Fail("%s: Could not read input 0: input", __func__);
3131 }
3132 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [batch_size, output_size].
3133 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
3134 if (!outputStateIn.IsValid())
3135 {
3136 return Fail("%s: Could not read input 18: outputStateIn", __func__);
3137 }
3138 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [batch_size, num_units].
3139 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
3140 if (!cellStateIn.IsValid())
3141 {
3142 return Fail("%s: Could not read input 19: cellStateIn", __func__);
3143 }
3144
3145 // Get the mandatory input tensors:
3146 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3147 // [num_units, input_size].
3148 const ConstTensorPin inputToForgetWeightsPin =
3149 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
3150 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3151 // [num_units, input_size].
3152 const ConstTensorPin inputToCellWeightsPin =
3153 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
3154 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3155 // [num_units, input_size].
3156 const ConstTensorPin inputToOutputWeightsPin =
3157 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
3158 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3159 // [num_units, output_size].
3160 const ConstTensorPin recurrentToForgetWeightsPin =
3161 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
3162 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
3163 // [num_units, output_size].
3164 const ConstTensorPin recurrentToCellWeightsPin =
3165 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
3166 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3167 // [num_units, output_size].
3168 const ConstTensorPin recurrentToOutputWeightsPin =
3169 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
3170 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3171 const ConstTensorPin forgetGateBiasPin =
3172 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
3173 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3174 const ConstTensorPin cellBiasPin =
3175 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
3176 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3177 const ConstTensorPin outputGateBiasPin =
3178 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
3179
3180 if (!inputToForgetWeightsPin.IsValid() ||
3181 !inputToCellWeightsPin.IsValid() ||
3182 !inputToOutputWeightsPin.IsValid() ||
3183 !recurrentToForgetWeightsPin.IsValid() ||
3184 !recurrentToCellWeightsPin.IsValid() ||
3185 !recurrentToOutputWeightsPin.IsValid() ||
3186 !forgetGateBiasPin.IsValid() ||
3187 !cellBiasPin.IsValid() ||
3188 !outputGateBiasPin.IsValid())
3189 {
3190 return Fail("%s: Operation has invalid tensor inputs", __func__);
3191 }
3192
3193 // Get the optional input tensors:
3194 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3195 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
3196 const ConstTensorPin inputToInputWeightsPin =
3197 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
3198 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3199 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
3200 // “num_units”), or the second dimension of the “projection_weights”, if defined.
3201 const ConstTensorPin recurrentToInputWeightsPin =
3202 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
3203 // 09: The cell-to-input weights: Optional.
3204 // A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3205 const ConstTensorPin cellToInputWeightsPin =
3206 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
3207 // 10: The cell-to-forget weights: Optional.
3208 // A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3209 const ConstTensorPin cellToForgetWeightsPin =
3210 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
3211 // 11: The cell-to-output weights: Optional.
3212 // A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3213 const ConstTensorPin cellToOutputWeightsPin =
3214 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
3215 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3216 const ConstTensorPin inputGateBiasPin =
3217 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3218 12,
3219 model,
3220 data,
3221 g_DontPermute,
3222 nullptr,
3223 true);
3224
3225 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3226 // [output_size, num_units].
3227 const ConstTensorPin projectionWeightsPin =
3228 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
3229 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [output_size].
3230 const ConstTensorPin projectionBiasPin =
3231 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3232 17,
3233 model,
3234 data,
3235 g_DontPermute,
3236 nullptr,
3237 true);
3238
3239 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
3240 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
3241 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
3242 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
3243 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
3244 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
3245 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
3246 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
3247 {
3248 return Fail("%s: Operation has invalid tensor inputs", __func__);
3249 }
3250
3251 // Get the mandatory input scalars (actually 1-D tensors of size 1):
3252 // 20: The activation function: A value indicating the activation function:
3253 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
3254 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
3255 // If set to 0.0 then clipping is disabled.
3256 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
3257 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
3258 // Determine data type of input tensor
Cathal Corbett3fe3a542022-05-25 09:35:37 +01003259 ActivationFn activation = ActivationFn::kActivationNone;
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003260 LstmDescriptor desc;
3261
3262 if (inputType == HalOperandType::TENSOR_FLOAT32)
3263 {
3264 float cellClip;
3265 float projClip;
3266
3267 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
3268 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
3269 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
3270 {
3271 return Fail("%s: Operation has invalid scalar inputs", __func__);
3272 }
3273
3274 desc.m_ClippingThresCell = cellClip;
3275 desc.m_ClippingThresProj = projClip;
3276 }
3277
3278 if (inputType == HalOperandType::TENSOR_FLOAT16)
3279 {
3280 Half cellClip;
3281 Half projClip;
3282
3283 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
3284 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT16, cellClip, model, data) ||
3285 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT16, projClip, model, data))
3286 {
3287 return Fail("%s: Operation has invalid scalar inputs", __func__);
3288 }
3289
3290 desc.m_ClippingThresCell = cellClip;
3291 desc.m_ClippingThresProj = projClip;
3292 }
3293
3294 // Determine if time-major or batch-major.
3295 // 23: Time-major if true, batch-major if false.
3296 bool isTimeMajor = GetOptionalBool<HalPolicy>(operation, 23, model, data);
3297
3298 // Get the normalization tensors
3299 // 24: The input layer normalization weights. A 1-D tensor of shape [num_units].
3300 // Used to rescale normalized inputs to activation at input gate.
3301 const ConstTensorPin inputLayerNormWeightsPin
3302 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 24, true));
3303
3304 // 25: The forget layer normalization weights. A 1-D tensor of shape [num_units].
3305 // Used to rescale normalized inputs to activation at forget gate.
3306 const ConstTensorPin forgetLayerNormWeightsPin =
3307 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3308 25,
3309 model,
3310 data,
3311 g_DontPermute,
3312 nullptr,
3313 true);
3314
3315 // 26: The cell layer normalization weights. A 1-D tensor of shape [num_units].
3316 // Used to rescale normalized inputs to activation at cell gate.
3317 const ConstTensorPin cellLayerNormWeightsPin =
3318 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3319 26,
3320 model,
3321 data,
3322 g_DontPermute,
3323 nullptr,
3324 true);
3325
3326 // 27: The output layer normalization weights. A 1-D tensor of shape [num_units].
3327 // Used to rescale normalized inputs to activation at output gate.
3328 const ConstTensorPin outputLayerNormWeightsPin =
3329 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3330 27,
3331 model,
3332 data,
3333 g_DontPermute,
3334 nullptr,
3335 true);
3336
3337 // Outputs:
3338 // 00: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16. Shape: if time-major:
3339 // [max_time, batch_size, output_size] If batch-major: [batch_size, max_time, output_size]
3340 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3341 if (!output)
3342 {
3343 return Fail("%s: Could not read output: ", __func__);
3344 }
3345
3346 //
3347 // 01 & 02:
3348 // hiddenStateOut and cellStateOut are not currently supported by our android versioning.
3349 //
3350
3351 // set the params structure for the AddLstmLayer call
3352 LstmInputParams params;
3353 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
3354 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
3355 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
3356 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
3357 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
3358 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
3359 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
3360 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
3361 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
3362 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
3363 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
3364 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
3365 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
3366 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
3367 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
3368 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
3369 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
3370 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
3371 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
3372 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
3373 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
3374
3375 // set the layer descriptor
3376 desc.m_ActivationFunc = activation;
3377 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
3378 params.m_RecurrentToInputWeights == nullptr ||
3379 params.m_InputGateBias == nullptr);
3380 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
3381 params.m_CellToOutputWeights != nullptr);
3382 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
3383 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
3384 params.m_ForgetLayerNormWeights != nullptr ||
3385 params.m_CellLayerNormWeights != nullptr ||
3386 params.m_OutputLayerNormWeights != nullptr);
3387 desc.m_TimeMajor = isTimeMajor;
3388
3389 // validate the optional input groups
3390 if (desc.m_CifgEnabled &&
3391 (params.m_InputToInputWeights != nullptr ||
3392 params.m_RecurrentToInputWeights != nullptr ||
3393 params.m_InputGateBias != nullptr))
3394 {
3395 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
3396 " and input gate bias must be provided", __func__);
3397 }
3398
3399 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
3400 {
3401 return Fail("%s: projection bias should not be provided without projection weights", __func__);
3402 }
3403
3404 if (desc.m_PeepholeEnabled &&
3405 (params.m_CellToForgetWeights == nullptr ||
3406 params.m_CellToOutputWeights == nullptr ||
3407 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
3408 {
3409 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
3410 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
3411 }
3412
3413 if (desc.m_LayerNormEnabled &&
3414 (params.m_ForgetLayerNormWeights == nullptr ||
3415 params.m_CellLayerNormWeights == nullptr ||
3416 params.m_OutputLayerNormWeights == nullptr ||
3417 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
3418 {
3419 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
3420 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
3421 }
3422
3423 // Check if the layer is supported
3424 // Inputs
3425 const TensorInfo& inputInfo = input.GetTensorInfo();
3426 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
3427 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
3428
3429 // Outputs
3430 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3431
Mike Kelly0ae102a2022-04-25 16:18:57 +01003432 unsigned int batchSize = inputInfo.GetShape()[0];
3433 unsigned int outputSize = outputInfo.GetShape()[2];
3434 unsigned int numUnits = cellStateInInfo.GetShape()[1];
3435
3436 armnn::DataType dataType = inputInfo.GetDataType();
3437 float qScale = inputInfo.GetQuantizationScale();
3438 int qOffset = inputInfo.GetQuantizationOffset();
3439
3440 armnn::TensorInfo cellStateOutInfo({batchSize, numUnits}, cellStateInInfo.GetDataType(),
3441 cellStateInInfo.GetQuantizationScale(), cellStateInInfo.GetQuantizationOffset());
3442 armnn::TensorInfo outputStateOutInfo({batchSize, outputSize}, dataType, qScale, qOffset);
3443
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003444 // Basic parameters
3445 LstmInputParamsInfo paramsInfo;
3446 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
3447 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
3448 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
3449 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
3450 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
3451 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
3452 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
3453 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
3454 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
3455
3456 // Optional parameters
3457 if (!desc.m_CifgEnabled)
3458 {
3459 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
3460 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
3461 if (params.m_CellToInputWeights != nullptr)
3462 {
3463 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
3464 }
3465 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
3466 }
3467
3468 if (desc.m_ProjectionEnabled)
3469 {
3470 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
3471 if (params.m_ProjectionBias != nullptr)
3472 {
3473 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
3474 }
3475 }
3476
3477 if (desc.m_PeepholeEnabled)
3478 {
3479 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
3480 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
3481 }
3482
3483 if (desc.m_LayerNormEnabled)
3484 {
3485 if(!desc.m_CifgEnabled)
3486 {
3487 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
3488 }
3489 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
3490 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
3491 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
3492 }
3493
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003494 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01003495 armnn::BackendId setBackend;
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003496 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
3497 {
3498 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3499 IsUnidirectionalSequenceLstmSupported,
3500 data.m_Backends,
3501 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01003502 setBackend,
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003503 inputInfo,
3504 outputStateInInfo,
3505 cellStateInInfo,
Mike Kelly0ae102a2022-04-25 16:18:57 +01003506 outputStateOutInfo,
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003507 cellStateOutInfo,
Mike Kelly0ae102a2022-04-25 16:18:57 +01003508 outputInfo,
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003509 desc,
3510 paramsInfo);
3511 };
3512
3513 bool isDynamic = false;
3514 if (!IsDynamicTensor(outputInfo))
3515 {
3516 validateFunc(outputInfo, isSupported);
3517 }
3518 else
3519 {
3520 isDynamic = true;
3521 isSupported = AreDynamicTensorsSupported();
3522 }
3523
3524 if (!isSupported)
3525 {
3526 return false;
3527 }
3528
3529 // Add the layer
3530 IConnectableLayer* layer = data.m_Network->AddUnidirectionalSequenceLstmLayer(desc,
3531 params,
3532 "UnidirectionalSequenceLstm");
Cathal Corbett8de96f72022-09-01 13:34:59 +01003533 layer->SetBackendId(setBackend);
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003534
3535 input.Connect(layer->GetInputSlot(0));
3536 outputStateIn.Connect(layer->GetInputSlot(1));
3537 cellStateIn.Connect(layer->GetInputSlot(2));
3538
3539 if (!isDynamic)
3540 {
Mike Kelly0ae102a2022-04-25 16:18:57 +01003541 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 2, model, data));
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003542 }
3543 else
3544 {
Mike Kelly0ae102a2022-04-25 16:18:57 +01003545 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 2, model, data, nullptr,
3546 validateFunc, ActivationFn::kActivationNone, true));
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003547 }
3548}
3549
Kevin May42477c12020-03-26 13:34:14 +00003550} // armnn_driver namespace