blob: 7a629e5a0551fc6368812ed6d9870ae90c81ce9a [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);
Mike Kelly23eccd82023-08-08 12:29:14 +01001125 splitterDesc.SetAxis(armnn::numeric_cast<int32_t>(channelsIndex));
Kevin May42477c12020-03-26 13:34:14 +00001126 for (unsigned int group = 0u; group < numGroups; ++group)
1127 {
1128 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
1129 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
1130 {
1131 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
1132 }
1133 }
1134
1135 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001136 armnn::BackendId setBackendSplit;
Kevin May42477c12020-03-26 13:34:14 +00001137 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1138 IsSplitterSupported,
1139 data.m_Backends,
1140 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001141 setBackendSplit,
Kevin May42477c12020-03-26 13:34:14 +00001142 inputInfo,
1143 splitterOutputInfos,
1144 splitterDesc);
1145 if (!isSupported)
1146 {
1147 return false;
1148 }
1149
1150 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001151 splitterLayer->SetBackendId(setBackendSplit);
Kevin May42477c12020-03-26 13:34:14 +00001152 if (!splitterLayer)
1153 {
1154 return Fail("%s: Failed to add SplitterLayer", __func__);
1155 }
1156
1157 input.Connect(splitterLayer->GetInputSlot(0));
1158 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
1159 {
1160 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
1161 }
1162
1163 //
1164 // Set up Convolution2d layers for each group
1165 //
1166
1167 // Set up group tensor shapes
1168 TensorShape groupInputShape(inputShape);
1169 groupInputShape[channelsIndex] = channelsPerGroup;
1170
Kevin May42477c12020-03-26 13:34:14 +00001171 TensorShape groupWeightsShape(weightsShape);
1172 groupWeightsShape[0] /= channelMultiplier * numGroups;
1173
1174 TensorShape groupBiasesShape({ 1 });
1175
1176 // Set up group tensor infos
1177 TensorInfo groupInputInfo(inputInfo);
1178 groupInputInfo.SetShape(groupInputShape);
1179
1180 const TensorInfo& weightsInfo = weights.GetInfo();
1181 TensorInfo groupWeightsInfo(weightsInfo);
1182 groupWeightsInfo.SetShape(groupWeightsShape);
1183
1184 const TensorInfo& biasesInfo = biases.GetInfo();
1185 TensorInfo groupBiasesInfo(biasesInfo);
1186 groupBiasesInfo.SetShape(groupBiasesShape);
1187
1188 TensorInfo groupOutputInfo(outputInfo);
Finn Williamsb0331172020-10-08 14:33:13 +01001189
1190 TensorShape groupOutputShape(outputShape);
1191 const bool isDynamic = IsDynamicTensor(outputInfo);
1192 if (!isDynamic)
1193 {
1194 groupOutputShape[channelsIndex] = 1;
1195 }
Kevin May42477c12020-03-26 13:34:14 +00001196 groupOutputInfo.SetShape(groupOutputShape);
1197
1198 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
1199 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
1200
1201 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
1202 for (unsigned int group = 0u; group < numGroups; ++group)
1203 {
1204 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1205 {
1206 auto index = group * channelMultiplier + m;
1207
1208 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
1209 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
1210
1211 if (weightsInfo.HasPerAxisQuantization())
1212 {
1213 // Extract per-axis quantization scales for group weights
1214 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
1215 groupWeightsInfo.SetQuantizationScales(
1216 std::vector<float>(weightsQuantScales.begin() + index,
1217 weightsQuantScales.begin() + index + groupWeightsShape[0]));
1218
1219 // Extract per-axis quantization scales for group biases
1220 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
1221 groupBiasesInfo.SetQuantizationScales(
1222 std::vector<float>(biasesQuantScales.begin() + index,
1223 biasesQuantScales.begin() + index + groupWeightsShape[0]));
1224 }
1225
1226 // Extract weights and biases data for current group convolution
1227 ConstTensor groupWeights(groupWeightsInfo,
1228 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
1229 weightsDataOffset));
1230 ConstTensor groupBiases(groupBiasesInfo,
1231 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
1232 biasesDataOffset));
1233
1234 isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001235 armnn::BackendId setBackendConv;
Finn Williamsb0331172020-10-08 14:33:13 +01001236 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1237 {
1238 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1239 IsConvolution2dSupported,
1240 data.m_Backends,
1241 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001242 setBackendConv,
Finn Williamsb0331172020-10-08 14:33:13 +01001243 groupInputInfo,
1244 outputInfo,
1245 desc,
1246 groupWeightsInfo,
1247 Optional<TensorInfo>(groupBiasesInfo));
1248 };
1249
1250 if(!isDynamic)
1251 {
1252 validateFunc(groupOutputInfo, isSupported);
1253 }
1254 else
1255 {
1256 isSupported = AreDynamicTensorsSupported();
1257 }
1258
Kevin May42477c12020-03-26 13:34:14 +00001259 if (!isSupported)
1260 {
1261 return false;
1262 }
1263
Teresa Charlinf8696262022-08-30 15:55:28 +01001264 IConnectableLayer* weightsLayer = data.m_Network->AddConstantLayer(groupWeights);
1265 IConnectableLayer* biasLayer = data.m_Network->AddConstantLayer(groupBiases);
1266 IConnectableLayer* convLayer = data.m_Network->AddConvolution2dLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001267 convLayer->SetBackendId(setBackendConv);
Keith Davis8f22bed2022-04-29 10:57:27 +01001268
Kevin May42477c12020-03-26 13:34:14 +00001269 if (!convLayer)
1270 {
1271 return Fail("%s: AddConvolution2dLayer failed", __func__);
1272 }
1273
1274 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
Teresa Charlinf8696262022-08-30 15:55:28 +01001275 weightsLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(1));
1276 biasLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(2));
1277
1278 weightsLayer->GetOutputSlot(0).SetTensorInfo(groupWeightsInfo);
1279 biasLayer->GetOutputSlot(0).SetTensorInfo(groupBiasesInfo);
Kevin May42477c12020-03-26 13:34:14 +00001280 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1281
Finn Williamsb0331172020-10-08 14:33:13 +01001282 if(isDynamic)
1283 {
1284 convLayer->GetOutputSlot(0).IsTensorInfoSet();
1285
1286 validateFunc(convLayer->GetOutputSlot(0).GetTensorInfo(), isSupported);
1287
1288 outputInfo = convLayer->GetOutputSlot(0).GetTensorInfo();
1289
1290 if (!isSupported)
1291 {
1292 return false;
1293 }
1294 }
1295
Kevin May42477c12020-03-26 13:34:14 +00001296 convLayers[index] = convLayer;
1297 }
1298 }
1299
1300 //
1301 // Set up Concat layer
1302 //
Finn Williamsb0331172020-10-08 14:33:13 +01001303 ConcatDescriptor concatDescriptor;
1304 // Equivalent to outputShape[channelsIndex], but we can't know the outputShape in the case of dynamic tensors
1305 concatDescriptor = ConcatDescriptor(weightsShape[0]);
Kevin May42477c12020-03-26 13:34:14 +00001306 for (unsigned int group = 0u; group < numGroups; ++group)
1307 {
1308 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1309 {
1310 auto index = group * channelMultiplier + m;
1311 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1312 concatDescriptor.SetConcatAxis(channelsIndex);
1313 }
1314 }
1315
1316 isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001317 armnn::BackendId setBackendConcat;
Finn Williamsb0331172020-10-08 14:33:13 +01001318 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1319 IsConcatSupported,
1320 data.m_Backends,
1321 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001322 setBackendConcat,
Finn Williamsb0331172020-10-08 14:33:13 +01001323 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1324 outputInfo,
1325 concatDescriptor);
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001326
Kevin May42477c12020-03-26 13:34:14 +00001327 if (!isSupported)
1328 {
1329 return false;
1330 }
1331
1332 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001333 concatLayer->SetBackendId(setBackendConcat);
Kevin May42477c12020-03-26 13:34:14 +00001334 if (!concatLayer)
1335 {
1336 return Fail("%s: AddConcatLayer failed", __func__);
1337 }
1338
1339 for (unsigned int group = 0u; group < numGroups; ++group)
1340 {
1341 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1342 {
1343 auto index = group * channelMultiplier + m;
1344 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1345 }
1346 }
1347 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1348
Kevin Mayfcf2a152020-09-08 16:06:32 +01001349 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *concatLayer, model,
Finn Williamsb0331172020-10-08 14:33:13 +01001350 data, nullptr, nullptr, activation);
Kevin May42477c12020-03-26 13:34:14 +00001351}
1352
1353template<typename HalPolicy,
1354 typename HalOperation = typename HalPolicy::Operation,
1355 typename HalModel = typename HalPolicy::Model>
1356bool ConvertInstanceNormalization(const HalOperation& operation, const HalModel& model, ConversionData& data)
1357{
1358 using HalOperand = typename HalPolicy::Operand;
1359 using HalOperandType = typename HalPolicy::OperandType;
1360
1361 ALOGV("HalPolicy::ConvertInstanceNormalization()");
1362
1363 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1364 if (!input.IsValid())
1365 {
1366 return Fail("%s: Operation has an invalid input 0", __func__);
1367 }
1368
1369 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1370 if (!output)
1371 {
1372 return Fail("%s: Operation has an invalid output", __func__);
1373 }
1374
1375 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001376
1377 // Determine data type of input tensor
1378 HalOperandType inputType;
1379 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1380 {
1381 return Fail("%s: Operation has invalid inputs", __func__);
1382 }
1383
1384 InstanceNormalizationDescriptor desc;
1385
1386 // Read gamma, beta & epsilon
1387 if (inputType == HalOperandType::TENSOR_FLOAT16)
1388 {
1389 Half fp16Gamma;
1390 Half fp16Beta;
1391 Half fp16Epsilon;
1392
1393 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Gamma, model, data) ||
1394 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, fp16Beta, model, data) ||
1395 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT16, fp16Epsilon, model, data))
1396 {
1397 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1398 }
1399
1400 desc.m_Gamma = static_cast<float>(fp16Gamma);
1401 desc.m_Beta = static_cast<float>(fp16Beta);
1402 desc.m_Eps = static_cast<float>(fp16Epsilon);
1403 }
1404 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1405 {
1406 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, desc.m_Gamma, model, data) ||
1407 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT32, desc.m_Beta, model, data) ||
1408 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT32, desc.m_Eps, model, data))
1409 {
1410 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1411 }
1412 }
1413 else
1414 {
1415 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1416 }
1417
1418 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 4, model, data);
1419
1420 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001421 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001422 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1423 {
1424 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1425 IsInstanceNormalizationSupported,
1426 data.m_Backends,
1427 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001428 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001429 input.GetTensorInfo(),
1430 outputInfo,
1431 desc);
1432 };
1433
1434 if(IsDynamicTensor(outputInfo))
1435 {
1436 isSupported = AreDynamicTensorsSupported();
1437 }
1438 else
1439 {
1440 validateFunc(outputInfo, isSupported);
1441 }
1442
Kevin May42477c12020-03-26 13:34:14 +00001443 if (!isSupported)
1444 {
1445 return false;
1446 }
1447
1448 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001449 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00001450 input.Connect(layer->GetInputSlot(0));
1451
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001452 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001453}
1454
1455template<typename HalPolicy,
1456 typename HalOperation = typename HalPolicy::Operation,
1457 typename HalModel = typename HalPolicy::Model>
1458bool ConvertLogSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
1459{
1460 using HalOperand = typename HalPolicy::Operand;
1461 using HalOperandType = typename HalPolicy::OperandType;
1462
1463 ALOGV("HalPolicy::ConvertLogSoftmax()");
1464
1465 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1466 if (!input.IsValid())
1467 {
1468 return Fail("%s: Failed to read input 0", __func__);
1469 }
1470
1471 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1472 if (!output)
1473 {
1474 return Fail("%s: Failed to read output", __func__);
1475 }
1476
1477 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001478
1479 // Determine data type of input tensor
1480 HalOperandType inputType;
1481 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1482 {
1483 return Fail("%s: Operation has invalid inputs", __func__);
1484 }
1485
1486 LogSoftmaxDescriptor descriptor;
1487
1488 // Read beta
1489 if (inputType == HalOperandType::TENSOR_FLOAT16)
1490 {
1491 Half fp16Beta;
1492 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Beta, model, data))
1493 {
1494 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1495 }
1496
1497 descriptor.m_Beta = static_cast<float>(fp16Beta);
1498 }
1499 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1500 {
1501 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, descriptor.m_Beta, model, data))
1502 {
1503 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1504 }
1505 }
1506 else
1507 {
1508 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1509 }
1510
1511 // Read axis
1512 if (!GetInputInt32<HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1513 {
1514 return Fail("%s: Failed to read input 2", __func__);
1515 }
1516
1517 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001518 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001519 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1520 {
1521 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1522 IsLogSoftmaxSupported,
1523 data.m_Backends,
1524 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001525 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001526 input.GetTensorInfo(),
1527 outputInfo,
1528 descriptor);
1529 };
1530
1531 if(IsDynamicTensor(outputInfo))
1532 {
1533 isSupported = AreDynamicTensorsSupported();
1534 }
1535 else
1536 {
1537 validateFunc(outputInfo, isSupported);
1538 }
1539
Kevin May42477c12020-03-26 13:34:14 +00001540 if (!isSupported)
1541 {
1542 return false;
1543 }
1544
1545 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001546 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00001547 if (!layer)
1548 {
Mike Kellye2d611e2021-10-14 12:35:58 +01001549 return Fail("%s: Could not add the LogSoftmaxLayer", __func__);
Kevin May42477c12020-03-26 13:34:14 +00001550 }
Kevin May42477c12020-03-26 13:34:14 +00001551 input.Connect(layer->GetInputSlot(0));
1552
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001553 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001554}
1555
1556template<typename HalPolicy,
1557 typename HalOperation = typename HalPolicy::Operation,
1558 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +00001559bool ConvertPadV2(const HalOperation& operation, const HalModel& model, ConversionData& data)
1560{
1561 using HalOperand = typename HalPolicy::Operand;
1562 using HalOperandType = typename HalPolicy::OperandType;
1563
1564 ALOGV("HalPolicy::ConvertPadV2()");
1565
1566 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1567 if (!input.IsValid())
1568 {
1569 return Fail("%s: Could not read input 0", __func__);
1570 }
1571
1572 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1573 if (!output)
1574 {
1575 return Fail("%s: Could not read output", __func__);
1576 }
1577
1578 const TensorInfo& inputInfo = input.GetTensorInfo();
1579 unsigned int rank = inputInfo.GetNumDimensions();
1580
1581 PadDescriptor descriptor;
1582 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
1583 {
1584 return Fail("%s: Could not convert paddings", __func__);
1585 }
1586
1587 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001588
1589 // Determine type of padding value
1590 HalOperandType operandType0;
1591 HalOperandType operandType2;
1592
1593 if (!GetOperandType<HalPolicy>(operation, 0, model, operandType0) ||
1594 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1595 {
1596 return Fail("%s: Operation has invalid inputs", __func__);
1597 }
1598
1599 // Read value to use for padding
1600 if (operandType0 == HalOperandType::TENSOR_FLOAT16 && operandType2 == HalOperandType::FLOAT16)
1601 {
1602 Half f16PadValue;
1603 if (!GetInputScalar<HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1604 {
1605 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1606 }
1607
1608 descriptor.m_PadValue = f16PadValue;
1609 }
1610 else if (operandType0 == HalOperandType::TENSOR_FLOAT32 && operandType2 == HalOperandType::FLOAT32)
1611 {
1612 if (!GetInputFloat32<HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1613 {
1614 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1615 }
1616 }
Teresa Charlind3381d52021-06-02 18:35:16 +01001617 else if (isQuantizedOperand(operandType0) && operandType2 == HalOperandType::INT32)
Kevin May42477c12020-03-26 13:34:14 +00001618 {
1619 int32_t intPadValue = 0;
1620 if (!GetInputInt32<HalPolicy>(operation, 2, intPadValue, model, data))
1621 {
1622 return Fail("%s: Could not read input 2 (INT32)", __func__);
1623 }
1624 descriptor.m_PadValue = intPadValue;
1625 }
1626 else
1627 {
1628 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1629 }
1630
1631 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001632 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001633 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1634 {
1635 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1636 IsPadSupported,
1637 data.m_Backends,
1638 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001639 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001640 inputInfo,
1641 outputInfo,
1642 descriptor);
1643 };
1644
1645 if(IsDynamicTensor(outputInfo))
1646 {
1647 isSupported = AreDynamicTensorsSupported();
1648 }
1649 else
1650 {
1651 validateFunc(outputInfo, isSupported);
1652 }
1653
Kevin May42477c12020-03-26 13:34:14 +00001654 if (!isSupported)
1655 {
1656 return false;
1657 }
1658
1659 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001660 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01001661 if (!layer)
1662 {
1663 return Fail("%s: Could not add the PadLayer", __func__);
1664 }
Kevin May42477c12020-03-26 13:34:14 +00001665 input.Connect(layer->GetInputSlot(0));
Kevin May42477c12020-03-26 13:34:14 +00001666
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001667 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001668}
1669
1670template<typename HalPolicy,
1671 typename HalOperation = typename HalPolicy::Operation,
1672 typename HalModel = typename HalPolicy::Model>
1673bool ConvertPrelu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1674{
1675 using HalOperand = typename HalPolicy::Operand;
1676
1677 ALOGV("HalPolicy::ConvertPrelu()");
1678
1679 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1680 LayerInputHandle alpha = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1681
1682 if (!input.IsValid() || !alpha.IsValid())
1683 {
1684 return Fail("%s: Operation has invalid inputs", __func__);
1685 }
1686
1687 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1688
1689 if (!output)
1690 {
1691 return Fail("%s: Could not read output", __func__);
1692 }
1693
1694 const TensorInfo& inputInfo = input.GetTensorInfo();
1695 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1696 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1697
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001698 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001699 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001700 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
Kevin May42477c12020-03-26 13:34:14 +00001701 {
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001702 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1703 IsPreluSupported,
1704 data.m_Backends,
1705 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001706 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001707 inputInfo,
1708 alphaInfo,
1709 outputInfo);
1710 };
1711
1712 if(IsDynamicTensor(outputInfo))
1713 {
1714 isSupported = AreDynamicTensorsSupported();
1715 }
1716 else
1717 {
1718 validateFunc(outputInfo, isSupported);
Kevin May42477c12020-03-26 13:34:14 +00001719 }
1720
Kevin May42477c12020-03-26 13:34:14 +00001721 if (!isSupported)
1722 {
1723 return false;
1724 }
1725
1726 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Cathal Corbett8de96f72022-09-01 13:34:59 +01001727 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00001728 if (!layer)
1729 {
Mike Kellye2d611e2021-10-14 12:35:58 +01001730 return Fail("%s: Could not add the PreluLayer", __func__);
Kevin May42477c12020-03-26 13:34:14 +00001731 }
1732
1733 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1734 if (!isReshapeSupported)
1735 {
1736 return false;
1737 }
1738
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001739 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001740}
1741
1742template<typename HalPolicy,
1743 typename HalOperation = typename HalPolicy::Operation,
1744 typename HalModel = typename HalPolicy::Model>
1745bool ConvertQuantize(const HalOperation& operation, const HalModel& model, ConversionData& data)
1746{
1747 using HalOperand = typename HalPolicy::Operand;
1748
1749 ALOGV("HalPolicy::ConvertQuantize()");
1750
1751 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1752 if (!input.IsValid())
1753 {
1754 return Fail("%s: Operation has invalid input", __func__);
1755 }
1756
1757 const HalOperand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1758 if (!outputOperand)
1759 {
1760 return Fail("%s: Operation has invalid outputs", __func__);
1761 }
1762
1763 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001764
1765 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001766 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001767 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1768 {
1769 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1770 IsQuantizeSupported,
1771 data.m_Backends,
1772 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001773 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001774 input.GetTensorInfo(),
1775 outputInfo);
1776 };
1777
1778 if(IsDynamicTensor(outputInfo))
1779 {
1780 isSupported = AreDynamicTensorsSupported();
1781 }
1782 else
1783 {
1784 validateFunc(outputInfo, isSupported);
1785 }
1786
Kevin May42477c12020-03-26 13:34:14 +00001787 if (!isSupported)
1788 {
1789 return false;
1790 }
1791
1792 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Cathal Corbett8de96f72022-09-01 13:34:59 +01001793 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01001794 if (!layer)
1795 {
1796 return Fail("%s: Could not add the QuantizeLayer", __func__);
1797 }
Kevin May42477c12020-03-26 13:34:14 +00001798 input.Connect(layer->GetInputSlot(0));
1799
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001800 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001801}
1802
1803template<typename HalPolicy,
1804 typename HalOperation = typename HalPolicy::Operation,
1805 typename HalModel = typename HalPolicy::Model>
Sadik Armagan813f2302020-05-19 14:10:30 +01001806bool ConvertQuantized16BitLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
Kevin May42477c12020-03-26 13:34:14 +00001807{
1808 using HalOperand = typename HalPolicy::Operand;
1809
Sadik Armagan813f2302020-05-19 14:10:30 +01001810 ALOGV("HalPolicy::ConvertQuantized16BitLstm()");
Kevin May42477c12020-03-26 13:34:14 +00001811
1812 //Inputs:
1813 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1814 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1815 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1816 if (!input.IsValid())
1817 {
1818 return Fail("%s: Could not read input 0: input", __func__);
1819 }
1820
1821 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1822 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1823 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1824 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 13, model, data);
1825 if (!previousCellStateIn.IsValid())
1826 {
1827 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1828 }
1829
1830 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1831 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1832 // is quantized with a fixed quantization range of -1, 127/128.
1833 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<HalPolicy>(operation, 14, model, data);
1834 if (!previousOutputIn.IsValid())
1835 {
1836 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1837 }
1838
1839 // Get the input tensors:
1840 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1841 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1842 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1843 const ConstTensorPin inputToInputWeightsPin =
1844 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1845
1846 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1847 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1848 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1849 const ConstTensorPin inputToForgetWeightsPin =
1850 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
1851
1852 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1853 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1854 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1855 const ConstTensorPin inputToCellWeightsPin =
1856 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 3, model, data);
1857
1858 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1859 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1860 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1861 const ConstTensorPin inputToOutputWeightsPin =
1862 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 4, model, data);
1863
1864 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1865 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1866 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1867 const ConstTensorPin recurrentToInputWeightsPin =
1868 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 5, model, data);
1869
1870 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1871 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1872 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1873 const ConstTensorPin recurrentToForgetWeightsPin =
1874 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 6, model, data);
1875
1876 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1877 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1878 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1879 const ConstTensorPin recurrentToCellWeightsPin =
1880 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 7, model, data);
1881
1882 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1883 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1884 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1885 const ConstTensorPin recurrentToOutputWeightsPin =
1886 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 8, model, data);
1887
1888 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1889 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1890 // of input and weights scales and zeroPoint equal to 0.
1891 const ConstTensorPin inputGateBiasPin =
1892 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 9, model, data);
1893
1894 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1895 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1896 // of input and weights scales and zeroPoint equal to 0.
1897 const ConstTensorPin forgetGateBiasPin =
1898 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 10, model, data);
1899
1900 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1901 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1902 // and weights scales and zeroPoint equal to 0.
1903 const ConstTensorPin cellBiasPin =
1904 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 11, model, data);
1905
1906 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1907 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1908 // of input and weights scales and zeroPoint equal to 0.
1909 const ConstTensorPin outputGateBiasPin =
1910 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 12, model, data);
1911
1912 if (!inputToInputWeightsPin.IsValid() ||
1913 !inputToForgetWeightsPin.IsValid() ||
1914 !inputToCellWeightsPin.IsValid() ||
1915 !inputToOutputWeightsPin.IsValid() ||
1916 !recurrentToInputWeightsPin.IsValid() ||
1917 !recurrentToForgetWeightsPin.IsValid() ||
1918 !recurrentToCellWeightsPin.IsValid() ||
1919 !recurrentToOutputWeightsPin.IsValid() ||
1920 !inputGateBiasPin.IsValid() ||
1921 !forgetGateBiasPin.IsValid() ||
1922 !cellBiasPin.IsValid() ||
1923 !outputGateBiasPin.IsValid())
1924 {
1925 return Fail("%s: Operation has invalid tensor inputs", __func__);
1926 }
1927
1928 // Outputs:
1929 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1930 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1931 // of -2^4, 2^4 * 32767/32768.
1932 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 0, model);
1933 if (!cellStateOut)
1934 {
1935 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1936 }
1937
1938 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1939 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1940 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 1, model);
1941 if (!output)
1942 {
1943 return Fail("%s: Could not read output 1: output", __func__);
1944 }
1945
1946 // Inputs
1947 const TensorInfo& inputInfo = input.GetTensorInfo();
1948 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1949 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
1950
1951 // Outputs
1952 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1953 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1954
1955 // Dynamic tensors currently not supported
1956 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1957 {
1958 return Fail("%s: Dynamic output tensors are not supported", __func__);
1959 }
1960
1961 QuantizedLstmInputParams params;
1962
1963 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1964 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1965 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1966 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1967 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1968 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1969 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1970 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1971 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1972 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1973 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1974 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1975
1976 QuantizedLstmInputParamsInfo paramsInfo;
1977 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1978 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1979 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1980 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1981 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1982 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1983 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1984 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1985 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1986 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1987 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1988 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1989
1990 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001991 armnn::BackendId setBackend;
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01001992 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1993 {
1994 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1995 IsQuantizedLstmSupported,
1996 data.m_Backends,
1997 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001998 setBackend,
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01001999 inputInfo,
2000 previousCellStateInInfo,
2001 previousOutputInInfo,
2002 cellStateOutInfo,
2003 outputInfo,
2004 paramsInfo);
2005 };
2006
2007 bool isDynamic = false;
2008 if (!IsDynamicTensor(cellStateOutInfo) &&
2009 !IsDynamicTensor(outputInfo))
2010 {
2011 validateFunc(outputInfo, isSupported);
2012 }
2013 else
2014 {
2015 isDynamic = true;
2016 isSupported = AreDynamicTensorsSupported();
2017 }
Kevin May42477c12020-03-26 13:34:14 +00002018
2019 if (!isSupported)
2020 {
2021 return false;
2022 }
2023
2024 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
Cathal Corbett8de96f72022-09-01 13:34:59 +01002025 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00002026 input.Connect(layer->GetInputSlot(0));
2027 previousCellStateIn.Connect(layer->GetInputSlot(1));
2028 previousOutputIn.Connect(layer->GetInputSlot(2));
2029
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002030 if (!isDynamic)
2031 {
2032 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2033 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data));
2034 }
2035 else
2036 {
2037 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2038 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01002039 operation, 1, *layer, 1, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002040 }
2041
Kevin May42477c12020-03-26 13:34:14 +00002042}
2043
2044template<typename HalPolicy,
2045 typename HalOperation = typename HalPolicy::Operation,
2046 typename HalModel = typename HalPolicy::Model>
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002047bool ConvertReduce(const HalOperation& operation,
2048 const HalModel& model,
2049 ConversionData& data,
2050 ReduceOperation reduceOperation)
2051{
2052 using HalOperand = typename HalPolicy::Operand;
2053 using HalOperandType = typename HalPolicy::OperandType;
2054
2055 armnn::ReduceDescriptor descriptor;
2056 descriptor.m_ReduceOperation = reduceOperation;
2057
2058 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2059 if (!input.IsValid())
2060 {
2061 return Fail("%s: Operation has invalid inputs", __func__);
2062 }
2063 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2064
2065 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2066 if (!output)
2067 {
2068 return Fail("%s: Could not read output 0", __func__);
2069 }
2070 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2071
2072 const HalOperand* axisOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2073 if (!axisOperand)
2074 {
2075 return Fail("%s: Could not read input 1", __func__);
2076 }
2077 std::vector<int32_t> axis;
2078 if (!GetTensorInt32Values<HalPolicy>(*axisOperand, axis, model, data))
2079 {
2080 return Fail("%s: Input 1 has invalid values", __func__);
2081 }
2082
2083 // Convert the axis to unsigned int and remove duplicates.
2084 unsigned int rank = inputInfo.GetNumDimensions();
2085 std::set<unsigned int> uniqueAxis;
2086 std::transform(axis.begin(), axis.end(),
2087 std::inserter(uniqueAxis, uniqueAxis.begin()),
2088 [rank](int i) -> unsigned int { return (i + rank) % rank; });
2089 descriptor.m_vAxis.assign(uniqueAxis.begin(), uniqueAxis.end());
2090
2091 // Get the "keep dims" flag.
2092 if (!GetInputScalar<HalPolicy>(operation, 2, HalOperandType::BOOL, descriptor.m_KeepDims, model, data))
2093 {
2094 return Fail("%s: Could not read input 2", __func__);
2095 }
2096
2097 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002098 armnn::BackendId setBackend;
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002099 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2100 {
2101 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2102 IsReduceSupported,
2103 data.m_Backends,
2104 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002105 setBackend,
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002106 inputInfo,
2107 outputInfo,
2108 descriptor);
2109 };
2110
2111 if(!IsDynamicTensor(outputInfo))
2112 {
2113 validateFunc(outputInfo, isSupported);
2114 }
2115 else
2116 {
2117 isSupported = AreDynamicTensorsSupported();
2118 }
2119
2120 if (!isSupported)
2121 {
2122 return false;
2123 }
2124
2125 armnn::IConnectableLayer* const layer = data.m_Network->AddReduceLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +01002126 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01002127 if (!layer)
2128 {
2129 return Fail("%s: Could not add the ReduceLayer", __func__);
2130 }
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002131 input.Connect(layer->GetInputSlot(0));
2132
2133 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
2134}
2135
2136template<typename HalPolicy,
2137 typename HalOperation = typename HalPolicy::Operation,
2138 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +00002139bool ConvertResize(const HalOperation& operation,
2140 const HalModel& model,
2141 ConversionData& data,
2142 ResizeMethod resizeMethod)
2143{
2144 using HalOperand = typename HalPolicy::Operand;
2145 using HalOperandType = typename HalPolicy::OperandType;
2146 ALOGV("HalPolicy::ConvertResize()");
2147 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
2148
2149 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2150 if (!input.IsValid())
2151 {
2152 return Fail("%s: Could not read input 0", __func__);
2153 }
2154
2155 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2156 if (!output)
2157 {
2158 return Fail("%s: Could not read output 0", __func__);
2159 }
2160
2161 const TensorInfo& inputInfo = input.GetTensorInfo();
2162 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2163
Kevin May42477c12020-03-26 13:34:14 +00002164 ResizeDescriptor descriptor;
2165 descriptor.m_Method = resizeMethod;
2166 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
2167
2168 HalOperandType operandType1;
2169 HalOperandType operandType2;
2170
2171 if (!GetOperandType<HalPolicy>(operation, 1, model, operandType1) ||
2172 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
2173 {
2174 return Fail("%s: Operation has invalid inputs", __func__);
2175 }
2176
2177 if (operandType1 != operandType2)
2178 {
2179 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
2180 }
2181
2182 if (operandType1 == HalOperandType::INT32)
2183 {
2184 // Case 1: resizing by shape
2185 int32_t targetWidth = 0;
2186 int32_t targetHeight = 0;
2187
2188 if (!GetInputInt32<HalPolicy>(operation, 1, targetWidth, model, data) ||
2189 !GetInputInt32<HalPolicy>(operation, 2, targetHeight, model, data))
2190 {
2191 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
2192 }
2193
2194 if (targetWidth < 0 || targetHeight < 0)
2195 {
2196 return Fail("%s: Operation has invalid inputs for resizing by shape. "
2197 "Target width/height cannot be < 0", __func__);
2198 }
2199
2200 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
2201 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
2202 }
2203 else if (operandType1 == HalOperandType::FLOAT32)
2204 {
2205 // Case 2: resizing by scale
2206 float widthScale = 1.0f;
2207 float heightScale = 1.0f;
2208
2209 if (!GetInputFloat32<HalPolicy>(operation, 1, widthScale, model, data) ||
2210 !GetInputFloat32<HalPolicy>(operation, 2, heightScale, model, data))
2211 {
2212 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2213 }
2214
2215 const TensorShape& inputShape = inputInfo.GetShape();
2216 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2217
2218 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
2219 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
2220
2221 descriptor.m_TargetWidth = std::floor(width * widthScale);
2222 descriptor.m_TargetHeight = std::floor(height * heightScale);
2223 }
2224 else if (operandType1 == HalOperandType::FLOAT16)
2225 {
2226 Half widthScale;
2227 Half heightScale;
2228
2229 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, widthScale, model, data) ||
2230 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, heightScale, model, data))
2231 {
2232 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2233 }
2234
2235 const TensorShape& inputShape = inputInfo.GetShape();
2236 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2237
2238 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
2239 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
2240
2241 descriptor.m_TargetWidth = std::floor(width * widthScale);
2242 descriptor.m_TargetHeight = std::floor(height * heightScale);
2243 }
2244 else
2245 {
2246 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
2247 }
2248
David Monahanf057e6f2020-06-22 09:55:23 +01002249 descriptor.m_AlignCorners = GetOptionalBool<HalPolicy>(operation, 4, model, data);
2250 descriptor.m_HalfPixelCenters = GetOptionalBool<HalPolicy>(operation, 5, model, data);
David Monahan51e0b132020-04-20 16:12:06 +01002251
Kevin May42477c12020-03-26 13:34:14 +00002252 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002253 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002254 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2255 {
2256 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2257 IsResizeSupported,
2258 data.m_Backends,
2259 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002260 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002261 inputInfo,
2262 outputInfo,
2263 descriptor);
2264 };
2265
2266 if(IsDynamicTensor(outputInfo))
2267 {
2268 isSupported = AreDynamicTensorsSupported();
2269 }
2270 else
2271 {
2272 validateFunc(outputInfo, isSupported);
2273 }
Kevin May42477c12020-03-26 13:34:14 +00002274
2275 if (!isSupported)
2276 {
2277 return false;
2278 }
2279
2280 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +01002281 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01002282 if (!layer)
2283 {
2284 return Fail("%s: Could not add the ResizeLayer", __func__);
2285 }
Kevin May42477c12020-03-26 13:34:14 +00002286 input.Connect(layer->GetInputSlot(0));
2287
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002288 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002289}
2290
2291template<typename HalPolicy,
2292 typename HalOperation = typename HalPolicy::Operation,
2293 typename HalModel = typename HalPolicy::Model>
2294bool ConvertSpaceToDepth(const HalOperation& operation, const HalModel& model, ConversionData& data)
2295{
2296 using HalOperand = typename HalPolicy::Operand;
2297 using HalOperandType = typename HalPolicy::OperandType;
2298
2299 ALOGV("HalPolicy::ConvertSpaceToDepth()");
2300
2301 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2302 if (!input.IsValid() )
2303 {
2304 return Fail("%s: Operation has invalid inputs", __func__);
2305 }
2306
2307 const TensorInfo& inputInfo = input.GetTensorInfo();
2308 unsigned int rank = inputInfo.GetNumDimensions();
2309 if (rank != 4)
2310 {
2311 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2312 }
2313
2314 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2315 if (!output)
2316 {
2317 return Fail("%s: Could not read output 0", __func__);
2318 }
2319
2320 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002321
2322 SpaceToDepthDescriptor desc;
2323
2324 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_BlockSize, model, data);
2325
2326 if (desc.m_BlockSize <= 1)
2327 {
Kevin May17de62e2023-07-31 12:16:04 +01002328 return Fail("%s: Block size must be at least 1 in all dimensions", __func__);
Kevin May42477c12020-03-26 13:34:14 +00002329 }
2330
2331 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
2332
2333 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002334 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002335 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2336 {
2337 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2338 IsSpaceToDepthSupported,
2339 data.m_Backends,
2340 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002341 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002342 inputInfo,
2343 outputInfo,
2344 desc);
2345 };
2346
2347 if(IsDynamicTensor(outputInfo))
2348 {
2349 isSupported = AreDynamicTensorsSupported();
2350 }
2351 else
2352 {
2353 validateFunc(outputInfo, isSupported);
2354 }
2355
Kevin May42477c12020-03-26 13:34:14 +00002356 if (!isSupported)
2357 {
2358 return false;
2359 }
2360
2361 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +01002362 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01002363 if (!layer)
2364 {
Mike Kelly1b46d132021-11-03 11:12:45 +00002365 return Fail("%s: Could not add the SpaceToDepthLayer", __func__);
Mike Kellye2d611e2021-10-14 12:35:58 +01002366 }
Kevin May42477c12020-03-26 13:34:14 +00002367 input.Connect(layer->GetInputSlot(0));
2368
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002369 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002370}
2371
2372template<typename HalPolicy,
2373 typename HalOperation = typename HalPolicy::Operation,
2374 typename HalModel = typename HalPolicy::Model>
2375bool ConvertSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
2376{
2377 using HalOperand = typename HalPolicy::Operand;
2378 using HalOperandType = typename HalPolicy::OperandType;
2379
2380 ALOGV("HalPolicy::ConvertSoftmax()");
2381
2382 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2383 if (!input.IsValid())
2384 {
2385 return Fail("%s: Operation has invalid inputs", __func__);
2386 }
2387
2388 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2389 if (!outputOperand)
2390 {
2391 return Fail("%s: Operation has no outputs", __func__);
2392 }
2393
2394 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00002395
2396 SoftmaxDescriptor desc;
Teresa Charlinbf866e22020-08-09 23:55:01 +01002397 HalOperandType outputType = outputOperand->type;
2398
2399 // Read beta value
2400 if (outputType == HalOperandType::TENSOR_FLOAT16)
Kevin May42477c12020-03-26 13:34:14 +00002401 {
Teresa Charlinbf866e22020-08-09 23:55:01 +01002402 Half value;
2403
2404 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, value, model, data))
2405 {
2406 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2407 }
2408
2409 desc.m_Beta = static_cast<float>(value);
2410 }
2411 else
2412 {
2413 if (!GetInputFloat32<HalPolicy>(operation, 1, desc.m_Beta, model, data))
2414 {
2415 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2416 }
Kevin May42477c12020-03-26 13:34:14 +00002417 }
2418
2419 if (operation.inputs.size() > 2 && !GetInputScalar<HalPolicy>(operation,
Teresa Charlinbf866e22020-08-09 23:55:01 +01002420 2,
2421 HalOperandType::INT32,
2422 desc.m_Axis,
2423 model,
2424 data))
Kevin May42477c12020-03-26 13:34:14 +00002425 {
2426 return Fail("%s: Operation has invalid inputs", __func__);
2427 }
2428
Kevin May42477c12020-03-26 13:34:14 +00002429 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002430 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002431 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2432 {
2433 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2434 IsSoftmaxSupported,
2435 data.m_Backends,
2436 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002437 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002438 input.GetTensorInfo(),
2439 outputInfo,
2440 desc);
2441 };
2442
2443 if(IsDynamicTensor(outputInfo))
2444 {
2445 isSupported = AreDynamicTensorsSupported();
2446 }
2447 else
2448 {
2449 validateFunc(outputInfo, isSupported);
2450 }
2451
Kevin May42477c12020-03-26 13:34:14 +00002452 if (!isSupported)
2453 {
2454 return false;
2455 }
2456
2457 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +01002458 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01002459 if (!layer)
2460 {
2461 return Fail("%s: Could not add the SoftmaxLayer", __func__);
2462 }
Kevin May42477c12020-03-26 13:34:14 +00002463 input.Connect(layer->GetInputSlot(0));
2464
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002465 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002466}
2467
2468template<typename HalPolicy,
2469 typename HalOperation = typename HalPolicy::Operation,
2470 typename HalModel = typename HalPolicy::Model>
2471bool ConvertLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
2472{
2473 using HalOperand = typename HalPolicy::Operand;
2474 using HalOperandType = typename HalPolicy::OperandType;
2475
2476 ALOGV("HalPolicy::ConvertLstm()");
2477
2478 // Inputs:
2479 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2480 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2481 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2482 if (!input.IsValid())
2483 {
2484 return Fail("%s: Could not read input 0: input", __func__);
2485 }
2486 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2487 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
2488 if (!outputStateIn.IsValid())
2489 {
2490 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2491 }
2492 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2493 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
2494 if (!cellStateIn.IsValid())
2495 {
2496 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2497 }
2498
2499 // Get the mandatory input tensors:
2500 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2501 // [num_units, input_size].
2502 const ConstTensorPin inputToForgetWeightsPin =
2503 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
2504 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2505 // [num_units, input_size].
2506 const ConstTensorPin inputToCellWeightsPin =
2507 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
2508 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2509 // [num_units, input_size].
2510 const ConstTensorPin inputToOutputWeightsPin =
2511 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
2512 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2513 // [num_units, output_size].
2514 const ConstTensorPin recurrentToForgetWeightsPin =
2515 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
2516 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2517 // [num_units, output_size].
2518 const ConstTensorPin recurrentToCellWeightsPin =
2519 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
2520 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2521 // [num_units, output_size].
2522 const ConstTensorPin recurrentToOutputWeightsPin =
2523 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
2524 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2525 const ConstTensorPin forgetGateBiasPin =
2526 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
2527 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2528 const ConstTensorPin cellBiasPin =
2529 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
2530 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2531 const ConstTensorPin outputGateBiasPin =
2532 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
2533
2534 if (!inputToForgetWeightsPin.IsValid() ||
2535 !inputToCellWeightsPin.IsValid() ||
2536 !inputToOutputWeightsPin.IsValid() ||
2537 !recurrentToForgetWeightsPin.IsValid() ||
2538 !recurrentToCellWeightsPin.IsValid() ||
2539 !recurrentToOutputWeightsPin.IsValid() ||
2540 !forgetGateBiasPin.IsValid() ||
2541 !cellBiasPin.IsValid() ||
2542 !outputGateBiasPin.IsValid())
2543 {
2544 return Fail("%s: Operation has invalid tensor inputs", __func__);
2545 }
2546
2547 // Get the optional input tensors:
2548 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2549 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2550 const ConstTensorPin inputToInputWeightsPin =
2551 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
2552 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2553 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2554 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2555 const ConstTensorPin recurrentToInputWeightsPin =
2556 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
2557 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2558 const ConstTensorPin cellToInputWeightsPin =
2559 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
2560 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2561 const ConstTensorPin cellToForgetWeightsPin =
2562 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
2563 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2564 const ConstTensorPin cellToOutputWeightsPin =
2565 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
2566 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2567 const ConstTensorPin inputGateBiasPin =
2568 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2569 12,
2570 model,
2571 data,
2572 g_DontPermute,
2573 nullptr,
2574 true);
2575
2576 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2577 // [output_size, num_units].
2578 const ConstTensorPin projectionWeightsPin =
2579 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
2580 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2581 const ConstTensorPin projectionBiasPin =
2582 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2583 17,
2584 model,
2585 data,
2586 g_DontPermute,
2587 nullptr,
2588 true);
2589
2590 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2591 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2592 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2593 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2594 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2595 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2596 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2597 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2598 {
2599 return Fail("%s: Operation has invalid tensor inputs", __func__);
2600 }
2601
2602 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2603 // 20: The activation function: A value indicating the activation function:
2604 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2605 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2606 // If set to 0.0 then clipping is disabled.
2607 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2608 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
Cathal Corbett3fe3a542022-05-25 09:35:37 +01002609 ActivationFn activation = ActivationFn::kActivationNone;
Kevin May42477c12020-03-26 13:34:14 +00002610 float cellClip;
2611 float projClip;
2612 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
2613 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
2614 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
2615 {
2616 return Fail("%s: Operation has invalid scalar inputs", __func__);
2617 }
2618
2619 // Get the normalization tensors
2620 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2621 // Used to rescale normalized inputs to activation at input gate.
2622 const ConstTensorPin inputLayerNormWeightsPin
2623 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 23, true));
2624
2625 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2626 // Used to rescale normalized inputs to activation at forget gate.
2627 const ConstTensorPin forgetLayerNormWeightsPin =
2628 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2629 24,
2630 model,
2631 data,
2632 g_DontPermute,
2633 nullptr,
2634 true);
2635
2636 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2637 // Used to rescale normalized inputs to activation at cell gate.
2638 const ConstTensorPin cellLayerNormWeightsPin =
2639 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2640 25,
2641 model,
2642 data,
2643 g_DontPermute,
2644 nullptr,
2645 true);
2646
2647 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2648 // Used to rescale normalized inputs to activation at output gate.
2649 const ConstTensorPin outputLayerNormWeightsPin =
2650 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2651 26,
2652 model,
2653 data,
2654 g_DontPermute,
2655 nullptr,
2656 true);
2657
2658 // Outputs:
2659 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2660 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2661 const HalOperand* scratchBuffer = GetOutputOperand<HalPolicy>(operation, 0, model);
2662 if (!scratchBuffer)
2663 {
2664 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2665 }
2666 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2667 const HalOperand* outputStateOut = GetOutputOperand<HalPolicy>(operation, 1, model);
2668 if (!outputStateOut)
2669 {
2670 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2671 }
2672 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2673 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 2, model);
2674 if (!cellStateOut)
2675 {
2676 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2677 }
2678 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2679 // effectively the same as the current “output state (out)” value.
2680 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 3, model);
2681 if (!output)
2682 {
2683 return Fail("%s: Could not read output 3: output", __func__);
2684 }
2685
2686 // set the params structure for the AddLstmLayer call
2687 LstmInputParams params;
2688 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2689 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2690 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2691 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2692 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2693 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2694 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2695 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2696 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2697 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2698 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2699 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2700 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2701 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2702 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2703 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2704 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2705 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2706 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2707 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2708 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2709
2710 // set the layer descriptor
2711 LstmDescriptor desc;
2712 desc.m_ActivationFunc = activation;
2713 desc.m_ClippingThresCell = cellClip;
2714 desc.m_ClippingThresProj = projClip;
2715 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2716 params.m_RecurrentToInputWeights == nullptr ||
2717 params.m_InputGateBias == nullptr);
2718 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2719 params.m_CellToOutputWeights != nullptr);
2720 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2721 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2722 params.m_ForgetLayerNormWeights != nullptr ||
2723 params.m_CellLayerNormWeights != nullptr ||
2724 params.m_OutputLayerNormWeights != nullptr);
2725
2726 // validate the optional input groups
2727 if (desc.m_CifgEnabled &&
2728 (params.m_InputToInputWeights != nullptr ||
2729 params.m_RecurrentToInputWeights != nullptr ||
2730 params.m_InputGateBias != nullptr))
2731 {
2732 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2733 " and input gate bias must be provided", __func__);
2734 }
2735
2736 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2737 {
2738 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2739 }
2740
2741 if (desc.m_PeepholeEnabled &&
2742 (params.m_CellToForgetWeights == nullptr ||
2743 params.m_CellToOutputWeights == nullptr ||
2744 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2745 {
2746 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2747 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2748 }
2749
2750 if (desc.m_LayerNormEnabled &&
2751 (params.m_ForgetLayerNormWeights == nullptr ||
2752 params.m_CellLayerNormWeights == nullptr ||
2753 params.m_OutputLayerNormWeights == nullptr ||
2754 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2755 {
2756 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2757 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2758 }
2759
2760 // Check if the layer is supported
2761 // Inputs
2762 const TensorInfo& inputInfo = input.GetTensorInfo();
2763 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2764 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
2765
2766 // Outputs
2767 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2768 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2769 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2770 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2771
Kevin May42477c12020-03-26 13:34:14 +00002772 // Basic parameters
2773 LstmInputParamsInfo paramsInfo;
2774 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2775 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2776 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2777 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2778 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2779 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2780 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2781 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2782 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2783
2784 // Optional parameters
2785 if (!desc.m_CifgEnabled)
2786 {
2787 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2788 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2789 if (params.m_CellToInputWeights != nullptr)
2790 {
2791 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2792 }
2793 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2794 }
2795
2796 if (desc.m_ProjectionEnabled)
2797 {
2798 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2799 if (params.m_ProjectionBias != nullptr)
2800 {
2801 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2802 }
2803 }
2804
2805 if (desc.m_PeepholeEnabled)
2806 {
2807 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2808 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2809 }
2810
2811 if (desc.m_LayerNormEnabled)
2812 {
2813 if(!desc.m_CifgEnabled)
2814 {
2815 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2816 }
2817 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2818 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2819 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2820 }
2821
2822 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002823 armnn::BackendId setBackend;
Sadik Armagandbda4b72020-09-03 11:33:07 +01002824 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2825 {
2826 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2827 IsLstmSupported,
2828 data.m_Backends,
2829 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002830 setBackend,
Sadik Armagandbda4b72020-09-03 11:33:07 +01002831 inputInfo,
2832 outputStateInInfo,
2833 cellStateInInfo,
2834 scratchBufferInfo,
2835 outputStateOutInfo,
2836 cellStateOutInfo,
2837 outputInfo,
2838 desc,
2839 paramsInfo);
2840 };
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002841
Sadik Armagandbda4b72020-09-03 11:33:07 +01002842 bool isDynamic = false;
2843 if (!IsDynamicTensor(outputStateOutInfo) &&
2844 !IsDynamicTensor(scratchBufferInfo) &&
2845 !IsDynamicTensor(cellStateOutInfo) &&
2846 !IsDynamicTensor(outputInfo))
2847 {
2848 validateFunc(outputInfo, isSupported);
2849 }
2850 else
2851 {
2852 isDynamic = true;
2853 isSupported = AreDynamicTensorsSupported();
2854 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002855
Kevin May42477c12020-03-26 13:34:14 +00002856 if (!isSupported)
2857 {
2858 return false;
2859 }
2860
2861 // Add the layer
2862 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Cathal Corbett8de96f72022-09-01 13:34:59 +01002863 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00002864
2865 input.Connect(layer->GetInputSlot(0));
2866 outputStateIn.Connect(layer->GetInputSlot(1));
2867 cellStateIn.Connect(layer->GetInputSlot(2));
2868
Sadik Armagandbda4b72020-09-03 11:33:07 +01002869 if (!isDynamic)
2870 {
2871 return (
2872 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2873 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2874 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2875 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 3, *layer, 3, model, data));
2876 }
2877 else
2878 {
2879 return (
2880 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2881 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2882 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2883 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01002884 operation, 3, *layer, 3, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armagandbda4b72020-09-03 11:33:07 +01002885 }
2886
Kevin May42477c12020-03-26 13:34:14 +00002887}
2888
2889template<typename HalPolicy,
2890 typename HalOperation = typename HalPolicy::Operation,
2891 typename HalModel = typename HalPolicy::Model>
Teresa Charlin7f0ff162023-07-24 23:42:10 +01002892bool ConvertTile(const HalOperation& operation, const HalModel& model, ConversionData& data)
2893{
2894 using HalOperand = typename HalPolicy::Operand;
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", __func__);
2908 }
2909
2910 const HalOperand* multiplesOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2911 if (!multiplesOperand)
2912 {
2913 return Fail("%s: Could not read input 1", __func__);
2914 }
2915 std::vector<int32_t> multiples;
2916 if (!GetTensorInt32Values<HalPolicy>(*multiplesOperand, multiples, model, data))
2917 {
2918 return Fail("%s: Input 1 has invalid values", __func__);
2919 }
2920 // Convert the multiples from int to unsigned int,
2921 // as values are always going to be positive despite the data type being integer.
2922 TileDescriptor descriptor;
2923 descriptor.m_Multiples.assign(multiples.begin(), multiples.end());
2924
2925 const TensorInfo& inputInfo = input.GetTensorInfo();
2926 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2927
2928 bool isSupported = false;
2929 armnn::BackendId setBackend;
2930 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2931 {
2932 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2933 IsTileSupported,
2934 data.m_Backends,
2935 isSupported,
2936 setBackend,
2937 inputInfo,
2938 outputInfo,
2939 descriptor);
2940 };
2941
2942 if(IsDynamicTensor(outputInfo))
2943 {
2944 isSupported = AreDynamicTensorsSupported();
2945 }
2946 else
2947 {
2948 validateFunc(outputInfo, isSupported);
2949 }
2950 if (!isSupported)
2951 {
2952 return false;
2953 }
2954
2955 IConnectableLayer* tileLayer = data.m_Network->AddTileLayer(descriptor);
2956 if (!tileLayer)
2957 {
2958 return Fail("%s: AddTileLayer failed", __func__);
2959 }
2960 tileLayer->SetBackendId(setBackend);
2961
2962 input.Connect(tileLayer->GetInputSlot(0));
2963
2964 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *tileLayer, model, data, nullptr, validateFunc);
2965}
2966
2967template<typename HalPolicy,
2968 typename HalOperation = typename HalPolicy::Operation,
2969 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +00002970bool ConvertTransposeConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2971{
2972 using HalOperand = typename HalPolicy::Operand;
2973 using HalOperandType = typename HalPolicy::OperandType;
2974
2975 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2976
2977 if (!input.IsValid())
2978 {
2979 return Fail("%s: Operation has invalid inputs", __func__);
2980 }
2981
2982 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2983
2984 if (!output)
2985 {
2986 return Fail("%s: Could not read output 0", __func__);
2987 }
2988
2989 const TensorInfo& inputInfo = input.GetTensorInfo();
2990 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002991
2992 // ArmNN does not currently support non-fixed weights or bias
2993 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2994 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2995
2996 if (weightsOperand == nullptr)
2997 {
2998 return Fail("%s: Operand is invalid", __func__);
2999 }
3000 TransposeConvolution2dDescriptor desc;
3001 desc.m_DataLayout = DataLayout::NHWC;
3002
3003 // Determine whether padding is implicit or explicit
3004 bool implicitPadding = operation.inputs.size() == 9;
3005
3006 if (implicitPadding )
3007 {
3008 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
3009 }
3010 else
3011 {
3012 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
3013 }
3014
3015 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
3016 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
3017 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
3018
3019 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
3020
3021 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
3022 // We have to permute it to OIHW if the data layout is NCHW.
3023 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
3024 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
3025 model, data, OHWIToOIHW) :
3026 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
3027
3028 // Bias is a 1D tensor
3029 const ConstTensorPin biasPin =
3030 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
3031
3032 if (!weightsPin.IsValid())
3033 {
3034 return Fail("%s: Operation has invalid weights", __func__);
3035 }
3036
3037 if (!biasPin.IsValid())
3038 {
3039 return Fail("%s: Operation has invalid biases", __func__);
3040 }
3041
3042 ConstTensor weights = weightsPin.GetConstTensor();
3043 ConstTensor bias = biasPin.GetConstTensor();
3044 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
3045
3046 ActivationFn activation;
3047
3048 if (implicitPadding)
3049 {
3050 int32_t strideX{0};
3051 int32_t strideY{0};
3052 int32_t padLeft{0};
3053 int32_t padRight{0};
3054 int32_t padTop{0};
3055 int32_t padBottom{0};
3056
3057 android::nn::PaddingScheme paddingScheme;
3058 if (!GetInputPaddingScheme<HalPolicy>(operation, 4, paddingScheme, model, data) ||
3059 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, strideX, model, data) ||
3060 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, strideY, model, data) ||
3061 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
3062 {
3063 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
3064 }
3065
3066 const uint32_t kernelX = weights.GetShape()[widthIndex];
3067 const uint32_t kernelY = weights.GetShape()[heightIndex];
Kevin May42477c12020-03-26 13:34:14 +00003068
Colm Donelan8f3d33e2020-07-06 15:41:43 +01003069 // If output shape has been specified as a parameter then extract it and make it available.
3070 const HalOperand* outputShapeOperand = GetInputOperand<HalPolicy>(operation, 3, model, false);
3071 std::vector<int32_t> outputShape;
3072 if ((outputShapeOperand) && (GetTensorInt32Values<HalPolicy>(*outputShapeOperand, outputShape, model, data)))
3073 {
3074 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
3075 for (int dimension : outputShape)
3076 {
3077 desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
3078 }
3079 desc.m_OutputShapeEnabled = true;
3080 }
3081
Finn Williams8fe50c62020-10-09 15:52:57 +01003082 uint32_t outputX;
3083 uint32_t outputY;
3084
3085 if (IsDynamicTensor(outputInfo))
3086 {
3087 if (outputShape.size() == 0)
3088 {
3089 return Fail("%s: Padding sizes cannot be inferred", __func__);
3090 }
3091
3092 outputX = outputShape[widthIndex];
3093 outputY = outputShape[heightIndex];
3094 }
3095 else
3096 {
3097 outputX = outputInfo.GetShape()[widthIndex];
3098 outputY = outputInfo.GetShape()[heightIndex];
3099 }
3100
3101 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
3102 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
3103
3104 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
3105 // but Arm NN only supports values >= 0
3106 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
3107 {
3108 return Fail("%s: Negative padding values are not supported", __func__);
3109 }
3110
Matthew Sloyan9b088d92020-09-14 15:12:55 +01003111 desc.m_StrideX = armnn::numeric_cast<uint32_t>(strideX);
3112 desc.m_StrideY = armnn::numeric_cast<uint32_t>(strideY);
3113 desc.m_PadLeft = armnn::numeric_cast<uint32_t>(padLeft);
3114 desc.m_PadRight = armnn::numeric_cast<uint32_t>(padRight);
3115 desc.m_PadTop = armnn::numeric_cast<uint32_t>(padTop);
3116 desc.m_PadBottom = armnn::numeric_cast<uint32_t>(padBottom);
Kevin May42477c12020-03-26 13:34:14 +00003117 }
3118 else if (operation.inputs.size() == 11)
3119 {
3120 // explicit padding
3121 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
3122 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
3123 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
3124 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
3125 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
3126 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
3127 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
3128 {
3129 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
3130 }
3131 }
3132 else
3133 {
3134 return Fail("%s: Unsupported number of operation inputs", __func__);
3135 }
3136
3137 desc.m_BiasEnabled = true;
3138 Optional<TensorInfo> biases(bias.GetInfo());
3139
3140 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01003141 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01003142 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
3143 {
3144 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3145 IsTransposeConvolution2dSupported,
3146 data.m_Backends,
3147 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01003148 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01003149 inputInfo,
3150 outputInfo,
3151 desc,
3152 weights.GetInfo(),
3153 biases);
3154 };
3155
3156 if(IsDynamicTensor(outputInfo))
3157 {
3158 isSupported = AreDynamicTensorsSupported();
3159 }
3160 else
3161 {
3162 validateFunc(outputInfo, isSupported);
3163 }
Kevin May42477c12020-03-26 13:34:14 +00003164 if (!isSupported)
3165 {
3166 return false;
3167 }
3168
3169 IConnectableLayer* startLayer =
3170 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Cathal Corbett8de96f72022-09-01 13:34:59 +01003171 startLayer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00003172 if (!startLayer)
3173 {
3174 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
3175 }
3176
Kevin May42477c12020-03-26 13:34:14 +00003177 input.Connect(startLayer->GetInputSlot(0));
3178
Kevin Mayfcf2a152020-09-08 16:06:32 +01003179 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
3180 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +00003181}
3182
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003183template<typename HalPolicy,
3184 typename HalOperation = typename HalPolicy::Operation,
3185 typename HalModel = typename HalPolicy::Model>
3186bool ConvertUnidirectionalSequenceLstm(const HalOperation& operation,
3187 const HalModel& model,
3188 ConversionData& data)
3189{
3190 using HalOperand = typename HalPolicy::Operand;
3191 using HalOperandType = typename HalPolicy::OperandType;
3192
3193 ALOGV("HalPolicy::ConvertUnidirectionalSequenceLstm()");
3194
3195 // Determine if input OperandType is ANEURALNETWORKS_TENSOR_FLOAT 32 or 16
3196 HalOperandType inputType;
3197 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
3198 {
3199 return Fail("%s: Operation has invalid inputs", __func__);
3200 }
3201
3202 // Inputs:
3203 // 0: The input: A 3-D tensor of shape: If time-major: [max_time, batch_size, input_size] If batch-major:
3204 // [batch_size, max_time, input_size] where “max_time” is the number of timesteps (sequence length), “batch_size”
3205 // corresponds to the batching dimension, and “input_size” is the size of the input.
3206 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3207 if (!input.IsValid())
3208 {
3209 return Fail("%s: Could not read input 0: input", __func__);
3210 }
3211 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [batch_size, output_size].
3212 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
3213 if (!outputStateIn.IsValid())
3214 {
3215 return Fail("%s: Could not read input 18: outputStateIn", __func__);
3216 }
3217 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [batch_size, num_units].
3218 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
3219 if (!cellStateIn.IsValid())
3220 {
3221 return Fail("%s: Could not read input 19: cellStateIn", __func__);
3222 }
3223
3224 // Get the mandatory input tensors:
3225 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3226 // [num_units, input_size].
3227 const ConstTensorPin inputToForgetWeightsPin =
3228 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
3229 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3230 // [num_units, input_size].
3231 const ConstTensorPin inputToCellWeightsPin =
3232 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
3233 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3234 // [num_units, input_size].
3235 const ConstTensorPin inputToOutputWeightsPin =
3236 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
3237 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3238 // [num_units, output_size].
3239 const ConstTensorPin recurrentToForgetWeightsPin =
3240 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
3241 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
3242 // [num_units, output_size].
3243 const ConstTensorPin recurrentToCellWeightsPin =
3244 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
3245 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3246 // [num_units, output_size].
3247 const ConstTensorPin recurrentToOutputWeightsPin =
3248 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
3249 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3250 const ConstTensorPin forgetGateBiasPin =
3251 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
3252 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3253 const ConstTensorPin cellBiasPin =
3254 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
3255 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3256 const ConstTensorPin outputGateBiasPin =
3257 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
3258
3259 if (!inputToForgetWeightsPin.IsValid() ||
3260 !inputToCellWeightsPin.IsValid() ||
3261 !inputToOutputWeightsPin.IsValid() ||
3262 !recurrentToForgetWeightsPin.IsValid() ||
3263 !recurrentToCellWeightsPin.IsValid() ||
3264 !recurrentToOutputWeightsPin.IsValid() ||
3265 !forgetGateBiasPin.IsValid() ||
3266 !cellBiasPin.IsValid() ||
3267 !outputGateBiasPin.IsValid())
3268 {
3269 return Fail("%s: Operation has invalid tensor inputs", __func__);
3270 }
3271
3272 // Get the optional input tensors:
3273 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3274 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
3275 const ConstTensorPin inputToInputWeightsPin =
3276 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
3277 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3278 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
3279 // “num_units”), or the second dimension of the “projection_weights”, if defined.
3280 const ConstTensorPin recurrentToInputWeightsPin =
3281 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
3282 // 09: The cell-to-input weights: Optional.
3283 // A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3284 const ConstTensorPin cellToInputWeightsPin =
3285 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
3286 // 10: The cell-to-forget weights: Optional.
3287 // A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3288 const ConstTensorPin cellToForgetWeightsPin =
3289 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
3290 // 11: The cell-to-output weights: Optional.
3291 // A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3292 const ConstTensorPin cellToOutputWeightsPin =
3293 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
3294 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3295 const ConstTensorPin inputGateBiasPin =
3296 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3297 12,
3298 model,
3299 data,
3300 g_DontPermute,
3301 nullptr,
3302 true);
3303
3304 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3305 // [output_size, num_units].
3306 const ConstTensorPin projectionWeightsPin =
3307 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
3308 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [output_size].
3309 const ConstTensorPin projectionBiasPin =
3310 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3311 17,
3312 model,
3313 data,
3314 g_DontPermute,
3315 nullptr,
3316 true);
3317
3318 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
3319 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
3320 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
3321 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
3322 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
3323 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
3324 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
3325 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
3326 {
3327 return Fail("%s: Operation has invalid tensor inputs", __func__);
3328 }
3329
3330 // Get the mandatory input scalars (actually 1-D tensors of size 1):
3331 // 20: The activation function: A value indicating the activation function:
3332 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
3333 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
3334 // If set to 0.0 then clipping is disabled.
3335 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
3336 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
3337 // Determine data type of input tensor
Cathal Corbett3fe3a542022-05-25 09:35:37 +01003338 ActivationFn activation = ActivationFn::kActivationNone;
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003339 LstmDescriptor desc;
3340
3341 if (inputType == HalOperandType::TENSOR_FLOAT32)
3342 {
3343 float cellClip;
3344 float projClip;
3345
3346 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
3347 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
3348 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
3349 {
3350 return Fail("%s: Operation has invalid scalar inputs", __func__);
3351 }
3352
3353 desc.m_ClippingThresCell = cellClip;
3354 desc.m_ClippingThresProj = projClip;
3355 }
3356
3357 if (inputType == HalOperandType::TENSOR_FLOAT16)
3358 {
3359 Half cellClip;
3360 Half projClip;
3361
3362 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
3363 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT16, cellClip, model, data) ||
3364 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT16, projClip, model, data))
3365 {
3366 return Fail("%s: Operation has invalid scalar inputs", __func__);
3367 }
3368
3369 desc.m_ClippingThresCell = cellClip;
3370 desc.m_ClippingThresProj = projClip;
3371 }
3372
3373 // Determine if time-major or batch-major.
3374 // 23: Time-major if true, batch-major if false.
3375 bool isTimeMajor = GetOptionalBool<HalPolicy>(operation, 23, model, data);
3376
3377 // Get the normalization tensors
3378 // 24: The input layer normalization weights. A 1-D tensor of shape [num_units].
3379 // Used to rescale normalized inputs to activation at input gate.
3380 const ConstTensorPin inputLayerNormWeightsPin
3381 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 24, true));
3382
3383 // 25: The forget layer normalization weights. A 1-D tensor of shape [num_units].
3384 // Used to rescale normalized inputs to activation at forget gate.
3385 const ConstTensorPin forgetLayerNormWeightsPin =
3386 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3387 25,
3388 model,
3389 data,
3390 g_DontPermute,
3391 nullptr,
3392 true);
3393
3394 // 26: The cell layer normalization weights. A 1-D tensor of shape [num_units].
3395 // Used to rescale normalized inputs to activation at cell gate.
3396 const ConstTensorPin cellLayerNormWeightsPin =
3397 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3398 26,
3399 model,
3400 data,
3401 g_DontPermute,
3402 nullptr,
3403 true);
3404
3405 // 27: The output layer normalization weights. A 1-D tensor of shape [num_units].
3406 // Used to rescale normalized inputs to activation at output gate.
3407 const ConstTensorPin outputLayerNormWeightsPin =
3408 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3409 27,
3410 model,
3411 data,
3412 g_DontPermute,
3413 nullptr,
3414 true);
3415
3416 // Outputs:
3417 // 00: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16. Shape: if time-major:
3418 // [max_time, batch_size, output_size] If batch-major: [batch_size, max_time, output_size]
3419 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3420 if (!output)
3421 {
3422 return Fail("%s: Could not read output: ", __func__);
3423 }
3424
3425 //
3426 // 01 & 02:
3427 // hiddenStateOut and cellStateOut are not currently supported by our android versioning.
3428 //
3429
3430 // set the params structure for the AddLstmLayer call
3431 LstmInputParams params;
3432 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
3433 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
3434 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
3435 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
3436 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
3437 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
3438 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
3439 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
3440 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
3441 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
3442 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
3443 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
3444 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
3445 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
3446 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
3447 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
3448 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
3449 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
3450 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
3451 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
3452 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
3453
3454 // set the layer descriptor
3455 desc.m_ActivationFunc = activation;
3456 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
3457 params.m_RecurrentToInputWeights == nullptr ||
3458 params.m_InputGateBias == nullptr);
3459 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
3460 params.m_CellToOutputWeights != nullptr);
3461 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
3462 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
3463 params.m_ForgetLayerNormWeights != nullptr ||
3464 params.m_CellLayerNormWeights != nullptr ||
3465 params.m_OutputLayerNormWeights != nullptr);
3466 desc.m_TimeMajor = isTimeMajor;
3467
3468 // validate the optional input groups
3469 if (desc.m_CifgEnabled &&
3470 (params.m_InputToInputWeights != nullptr ||
3471 params.m_RecurrentToInputWeights != nullptr ||
3472 params.m_InputGateBias != nullptr))
3473 {
3474 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
3475 " and input gate bias must be provided", __func__);
3476 }
3477
3478 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
3479 {
3480 return Fail("%s: projection bias should not be provided without projection weights", __func__);
3481 }
3482
3483 if (desc.m_PeepholeEnabled &&
3484 (params.m_CellToForgetWeights == nullptr ||
3485 params.m_CellToOutputWeights == nullptr ||
3486 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
3487 {
3488 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
3489 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
3490 }
3491
3492 if (desc.m_LayerNormEnabled &&
3493 (params.m_ForgetLayerNormWeights == nullptr ||
3494 params.m_CellLayerNormWeights == nullptr ||
3495 params.m_OutputLayerNormWeights == nullptr ||
3496 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
3497 {
3498 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
3499 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
3500 }
3501
3502 // Check if the layer is supported
3503 // Inputs
3504 const TensorInfo& inputInfo = input.GetTensorInfo();
3505 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
3506 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
3507
3508 // Outputs
3509 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3510
Mike Kelly0ae102a2022-04-25 16:18:57 +01003511 unsigned int batchSize = inputInfo.GetShape()[0];
3512 unsigned int outputSize = outputInfo.GetShape()[2];
3513 unsigned int numUnits = cellStateInInfo.GetShape()[1];
3514
3515 armnn::DataType dataType = inputInfo.GetDataType();
3516 float qScale = inputInfo.GetQuantizationScale();
3517 int qOffset = inputInfo.GetQuantizationOffset();
3518
3519 armnn::TensorInfo cellStateOutInfo({batchSize, numUnits}, cellStateInInfo.GetDataType(),
3520 cellStateInInfo.GetQuantizationScale(), cellStateInInfo.GetQuantizationOffset());
3521 armnn::TensorInfo outputStateOutInfo({batchSize, outputSize}, dataType, qScale, qOffset);
3522
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003523 // Basic parameters
3524 LstmInputParamsInfo paramsInfo;
3525 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
3526 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
3527 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
3528 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
3529 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
3530 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
3531 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
3532 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
3533 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
3534
3535 // Optional parameters
3536 if (!desc.m_CifgEnabled)
3537 {
3538 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
3539 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
3540 if (params.m_CellToInputWeights != nullptr)
3541 {
3542 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
3543 }
3544 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
3545 }
3546
3547 if (desc.m_ProjectionEnabled)
3548 {
3549 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
3550 if (params.m_ProjectionBias != nullptr)
3551 {
3552 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
3553 }
3554 }
3555
3556 if (desc.m_PeepholeEnabled)
3557 {
3558 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
3559 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
3560 }
3561
3562 if (desc.m_LayerNormEnabled)
3563 {
3564 if(!desc.m_CifgEnabled)
3565 {
3566 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
3567 }
3568 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
3569 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
3570 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
3571 }
3572
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003573 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01003574 armnn::BackendId setBackend;
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003575 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
3576 {
3577 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3578 IsUnidirectionalSequenceLstmSupported,
3579 data.m_Backends,
3580 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01003581 setBackend,
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003582 inputInfo,
3583 outputStateInInfo,
3584 cellStateInInfo,
Mike Kelly0ae102a2022-04-25 16:18:57 +01003585 outputStateOutInfo,
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003586 cellStateOutInfo,
Mike Kelly0ae102a2022-04-25 16:18:57 +01003587 outputInfo,
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003588 desc,
3589 paramsInfo);
3590 };
3591
3592 bool isDynamic = false;
3593 if (!IsDynamicTensor(outputInfo))
3594 {
3595 validateFunc(outputInfo, isSupported);
3596 }
3597 else
3598 {
3599 isDynamic = true;
3600 isSupported = AreDynamicTensorsSupported();
3601 }
3602
3603 if (!isSupported)
3604 {
3605 return false;
3606 }
3607
3608 // Add the layer
3609 IConnectableLayer* layer = data.m_Network->AddUnidirectionalSequenceLstmLayer(desc,
3610 params,
3611 "UnidirectionalSequenceLstm");
Cathal Corbett8de96f72022-09-01 13:34:59 +01003612 layer->SetBackendId(setBackend);
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003613
3614 input.Connect(layer->GetInputSlot(0));
3615 outputStateIn.Connect(layer->GetInputSlot(1));
3616 cellStateIn.Connect(layer->GetInputSlot(2));
3617
3618 if (!isDynamic)
3619 {
Mike Kelly0ae102a2022-04-25 16:18:57 +01003620 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 2, model, data));
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003621 }
3622 else
3623 {
Mike Kelly0ae102a2022-04-25 16:18:57 +01003624 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 2, model, data, nullptr,
3625 validateFunc, ActivationFn::kActivationNone, true));
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003626 }
3627}
3628
Kevin May42477c12020-03-26 13:34:14 +00003629} // armnn_driver namespace