blob: e5840468c6fa1abba2b7277471606e86f9aa5221 [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,
Kevin Mayf762b832023-08-18 14:01:51 +010030 const HalModel& model,
31 const bool isOptional = true)
Keith Davis8f22bed2022-04-29 10:57:27 +010032{
33 using HalOperand = typename HalPolicy::Operand;
34 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
35 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
36 if (!operand)
37 {
38 Fail("%s: failed to get input operand %i", __func__, inputIndex);
39 return false;
40 }
41
Kevin Mayf762b832023-08-18 14:01:51 +010042 // If the operand is not an optional operand it cannot have a NO_VALUE lifetime
43 if (!isOptional && operand->lifetime == HalOperandLifeTime::NO_VALUE)
44 {
45 return false;
46 }
47
Keith Davis8f22bed2022-04-29 10:57:27 +010048 if (operand->lifetime != HalOperandLifeTime::CONSTANT_COPY
49 && operand->lifetime != HalOperandLifeTime::CONSTANT_REFERENCE
50 && operand->lifetime != HalOperandLifeTime::NO_VALUE)
51 {
52 return false;
53 }
Kevin Mayf762b832023-08-18 14:01:51 +010054
Keith Davis8f22bed2022-04-29 10:57:27 +010055 return true;
56}
57
58template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +000059 typename HalOperation = typename HalPolicy::Operation,
60 typename HalModel = typename HalPolicy::Model>
61bool IsQSymmDequantizeForWeights(const HalOperation& operation, const HalModel& model)
62{
63 using HalOperand = typename HalPolicy::Operand;
64 using HalOperationType = typename HalPolicy::OperationType;
65
66 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, 0, model);
67 if (!operand)
68 {
69 return false;
70 }
71
72 if(!IsQSymm8(*operand))
73 {
74 // Only QSymm8 weights are dequantized on the fly by the driver
75 return false;
76 }
77
78 if (!IsOperandConstant<HalPolicy>(*operand))
79 {
80 // Non-const input is not accepted for weights
81 return false;
82 }
83
84 // Iterate through all the operations and find the operation feeding from the Dequantize output
85 const size_t outputIndex = operation.outputs[0];
86 for (uint32_t operationIdx = 0; operationIdx < getMainModel(model).operations.size(); ++operationIdx)
87 {
88 const auto& operationIt = getMainModel(model).operations[operationIdx];
89 switch (operationIt.type)
90 {
91 case HalOperationType::FULLY_CONNECTED:
92 if (outputIndex == operationIt.inputs[1]) // Weights are bound to slot 1
93 {
94 // If the output is going into the FC weights return true
95 return true;
96 }
97 break;
98 case HalOperationType::LSTM:
99 for (size_t k = 0; k < operationIt.inputs.size(); ++k)
100 {
101 if (outputIndex == operationIt.inputs[k])
102 {
103 // If the output is going into the LSTM weights return true
104 return true;
105 }
106 }
107 break;
108 default:
109 break;
110 }
111 }
112
113 return false;
114}
115
116template<typename HalPolicy,
117 typename HalOperation = typename HalPolicy::Operation,
118 typename HalModel = typename HalPolicy::Model>
119bool SetupAndTrackLayerOutputSlotAndOverrideTensorInfo(const HalOperation& operation,
120 uint32_t operationOutputIndex,
121 armnn::IConnectableLayer& layer,
122 uint32_t layerOutputIndex,
123 const HalModel& model,
124 ConversionData& data,
125 const armnn::TensorInfo tensor_info)
126{
127 using HalOperand = typename HalPolicy::Operand;
128
129 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, operationOutputIndex, model);
130 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
131 {
132 return false;
133 }
134
135 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
136
137 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
138 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
139
140 outputSlot.SetTensorInfo(tensor_info);
141
142 return true;
143}
144
145template<typename HalPolicy,
Sadik Armagan92b5fd12021-04-26 09:52:06 +0100146 typename HalOperation = typename HalPolicy::Operation,
147 typename HalModel = typename HalPolicy::Model>
148bool ConvertCast(const HalOperation& operation,
149 const HalModel& model,
150 ConversionData& data)
151{
152 using HalOperand = typename HalPolicy::Operand;
153
154 ALOGV("HalPolicy::ConvertCast()");
155
156 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
157
158 if (!input.IsValid())
159 {
160 return Fail("%s: Operation has invalid inputs", __func__);
161 }
162
163 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
164 if (!output)
165 {
166 return Fail("%s: Could not read output 0", __func__);
167 }
168
169 const TensorInfo& inputInfo = input.GetTensorInfo();
170 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
171
172 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100173 armnn::BackendId setBackend;
Sadik Armagan92b5fd12021-04-26 09:52:06 +0100174 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
175 {
176 FORWARD_LAYER_SUPPORT_FUNC(__func__,
177 IsCastSupported,
178 data.m_Backends,
179 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100180 setBackend,
Sadik Armagan92b5fd12021-04-26 09:52:06 +0100181 inputInfo,
182 outputInfo);
183 };
184
185 if(!IsDynamicTensor(outputInfo))
186 {
187 validateFunc(outputInfo, isSupported);
188 }
189 else
190 {
191 isSupported = AreDynamicTensorsSupported();
192 }
193
194 if (!isSupported)
195 {
196 return false;
197 }
198
199 IConnectableLayer* layer = data.m_Network->AddCastLayer();
Cathal Corbett8de96f72022-09-01 13:34:59 +0100200 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +0100201 if (!layer)
202 {
203 return Fail("%s: Could not add the CastLayer", __func__);
204 }
Sadik Armagan92b5fd12021-04-26 09:52:06 +0100205 input.Connect(layer->GetInputSlot(0));
206
207 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
208}
209
210template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +0000211 typename HalOperation = typename HalPolicy::Operation,
212 typename HalModel = typename HalPolicy::Model>
Teresa Charlin7f5b51e2021-09-01 14:19:38 +0100213bool ConvertChannelShuffle(const HalOperation& operation,
214 const HalModel& model,
215 ConversionData& data)
216{
217 using HalOperand = typename HalPolicy::Operand;
218 using HalOperandType = typename HalPolicy::OperandType;
219
220 ALOGV("HalPolicy::ConvertChannelShuffle()");
221
222 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
223 if (!input.IsValid())
224 {
225 return Fail("%s: Operation has invalid inputs", __func__);
226 }
227 auto inputDimensions = static_cast<int32_t>(input.GetTensorInfo().GetNumDimensions());
228
229 ChannelShuffleDescriptor descriptor;
230
231 int32_t groups;
232 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, groups, model, data))
233 {
234 return Fail("%s: Operation has invalid or unsupported number of groups operand", __func__);
235 }
236 descriptor.m_NumGroups = static_cast<uint32_t>(groups);
237
238 int32_t axis;
239 if (!GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, axis, model, data))
240 {
241 return Fail("%s: Operation has invalid or unsupported dimension channel shuffle operand", __func__);
242 }
243 if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
244 {
245 return Fail("%s: Operation has invalid dimension: %d. It is out of bounds [-%d, %d))", __func__, axis,
246 inputDimensions, inputDimensions);
247 }
248 int positiveAxis = (axis < 0) ? inputDimensions + axis : axis;
249 descriptor.m_Axis = static_cast<uint32_t>(positiveAxis);
250
251 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
252 if (!output)
253 {
254 return Fail("%s: Could not read output 0", __func__);
255 }
256
257 const TensorInfo& inputInfo = input.GetTensorInfo();
258 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
259
260 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100261 armnn::BackendId setBackend;
Teresa Charlin7f5b51e2021-09-01 14:19:38 +0100262 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
263 {
264 FORWARD_LAYER_SUPPORT_FUNC(__func__,
265 IsChannelShuffleSupported,
266 data.m_Backends,
267 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100268 setBackend,
Teresa Charlin7f5b51e2021-09-01 14:19:38 +0100269 inputInfo,
270 outputInfo,
271 descriptor);
272 };
273
274 if(!IsDynamicTensor(outputInfo))
275 {
276 validateFunc(outputInfo, isSupported);
277 }
278 else
279 {
280 isSupported = AreDynamicTensorsSupported();
281 }
282
283 if (!isSupported)
284 {
285 return false;
286 }
287
288 IConnectableLayer* layer = data.m_Network->AddChannelShuffleLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +0100289 layer->SetBackendId(setBackend);
Teresa Charlin7f5b51e2021-09-01 14:19:38 +0100290 assert(layer != nullptr);
291 input.Connect(layer->GetInputSlot(0));
292
293 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
294}
295
296template<typename HalPolicy,
297 typename HalOperation = typename HalPolicy::Operation,
298 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +0000299bool ConvertComparison_1_2(const HalOperation& operation,
300 const HalModel& model,
301 ConversionData& data,
302 ComparisonOperation comparisonOperation)
303{
304 using HalOperand = typename HalPolicy::Operand;
305
306 ALOGV("HalPolicy::ConvertComparison()");
307 ALOGV("comparisonOperation = %s", GetComparisonOperationAsCString(comparisonOperation));
308
309 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
310 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
311
312 if (!(input0.IsValid() && input1.IsValid()))
313 {
314 return Fail("%s: Operation has invalid inputs", __func__);
315 }
316
317 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
318 if (!output)
319 {
320 return Fail("%s: Could not read output 0", __func__);
321 }
322
323 const TensorInfo& inputInfo0 = input0.GetTensorInfo();
324 const TensorInfo& inputInfo1 = input1.GetTensorInfo();
325 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
326
Kevin May42477c12020-03-26 13:34:14 +0000327 ComparisonDescriptor descriptor(comparisonOperation);
328
329 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100330 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100331 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
332 {
333 FORWARD_LAYER_SUPPORT_FUNC(__func__,
334 IsComparisonSupported,
335 data.m_Backends,
336 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100337 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100338 inputInfo0,
339 inputInfo1,
340 outputInfo,
341 descriptor);
342
343 };
344
345 if(!IsDynamicTensor(outputInfo))
346 {
347 validateFunc(outputInfo, isSupported);
348 }
349 else
350 {
351 isSupported = AreDynamicTensorsSupported();
352 }
Kevin May42477c12020-03-26 13:34:14 +0000353
354 if (!isSupported)
355 {
356 return false;
357 }
358
359 IConnectableLayer* layer = data.m_Network->AddComparisonLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +0100360 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +0100361 if (!layer)
362 {
363 return Fail("%s: Could not add the ComparisonLayer", __func__);
364 }
Kevin May42477c12020-03-26 13:34:14 +0000365
366 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
367 if (!isReshapeSupported)
368 {
369 return false;
370 }
371
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100372 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000373}
374
375template<typename HalPolicy,
376 typename HalOperation = typename HalPolicy::Operation,
377 typename HalModel = typename HalPolicy::Model>
378bool ConvertConv2d_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
379{
380
381 using HalOperand = typename HalPolicy::Operand;
382 using HalOperandType = typename HalPolicy::OperandType;
383
384 ALOGV("HalPolicy::ConvertConv2d_1_2()");
385
386 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
387 if (!input.IsValid())
388 {
389 return Fail("%s: Operation has invalid inputs", __func__);
390 }
391
392 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
393 if (!output)
394 {
395 return Fail("%s: Could not read output 0", __func__);
396 }
397
398 const TensorInfo& inputInfo = input.GetTensorInfo();
399 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
400
Kevin May42477c12020-03-26 13:34:14 +0000401 Convolution2dDescriptor desc;
402 desc.m_DataLayout = DataLayout::NHWC;
403
404 // Determine whether padding is implicit or explicit
405 bool implicitPadding = operation.inputs.size() == 7 ||
406 (operation.inputs.size() >= 8 &&
407 GetInputOperand<HalPolicy>(operation, 7, model)->type == HalOperandType::BOOL);
408
409 if (implicitPadding)
410 {
411 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 7, model, data);
412 }
413 else if (operation.inputs.size() >= 10)
414 {
415 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
416 }
417
418 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
419
420 // ArmNN does not currently support non-fixed weights or bias
421 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
422 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
423 // the DataLayout is NCHW
Kevin May42477c12020-03-26 13:34:14 +0000424
Keith Davis8f22bed2022-04-29 10:57:27 +0100425
Kevin Mayf762b832023-08-18 14:01:51 +0100426 if (!IsWeightsValid<HalPolicy>(operation, 1, model, false) && desc.m_DataLayout == DataLayout::NCHW)
Kevin May42477c12020-03-26 13:34:14 +0000427 {
Keith Davis8f22bed2022-04-29 10:57:27 +0100428 return Fail("%s: Operation has unsupported weights HalOperandLifeTime", __func__);
Kevin May42477c12020-03-26 13:34:14 +0000429 }
Keith Davis8f22bed2022-04-29 10:57:27 +0100430 LayerInputHandle weightsInput = (desc.m_DataLayout == DataLayout::NCHW) ?
431 ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
432 ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
433
434 if (!weightsInput.IsValid())
Kevin May42477c12020-03-26 13:34:14 +0000435 {
Keith Davis8f22bed2022-04-29 10:57:27 +0100436 return Fail("%s: Operation has invalid inputs", __func__);
Kevin May42477c12020-03-26 13:34:14 +0000437 }
438
Keith Davis8f22bed2022-04-29 10:57:27 +0100439 LayerInputHandle biasInput = ConvertToLayerInputHandle<HalPolicy>(operation, 2, model, data); // 1D
440 if (!biasInput.IsValid())
441 {
442 return Fail("%s: Operation has invalid inputs", __func__);
443 }
444
445 biasInput.SanitizeQuantizationScale(weightsInput, input);
446 armnn::TensorInfo weightsInfo = weightsInput.GetTensorInfo();
447 armnn::TensorInfo biasInfo = biasInput.GetTensorInfo();
Kevin May42477c12020-03-26 13:34:14 +0000448
449 ActivationFn activation;
450
451 if (implicitPadding)
452 {
453 android::nn::PaddingScheme paddingScheme;
454 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
455 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
456 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
457 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data) ||
458 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 8, desc, model, data))
459 {
460 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
461 }
462
463 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
464 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
465 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
Keith Davis8f22bed2022-04-29 10:57:27 +0100466 const uint32_t kernelX = weightsInfo.GetShape()[widthIndex];
467 const uint32_t kernelY = weightsInfo.GetShape()[heightIndex];
Kevin May42477c12020-03-26 13:34:14 +0000468 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
469 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
470
471 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
472 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
473
474 }
475 else if (operation.inputs.size() >= 10)
476 {
477 // explicit padding
478 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
479 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
480 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
481 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
482 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
483 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
484 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data) ||
485 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 11, desc, model, data))
486 {
487 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
488 }
489 }
490 else
491 {
492 return Fail("%s: Unsupported number of operation inputs", __func__);
493 }
494
495 desc.m_BiasEnabled = true;
Keith Davis8f22bed2022-04-29 10:57:27 +0100496 Optional<TensorInfo> biases(biasInfo);
Kevin May42477c12020-03-26 13:34:14 +0000497
498 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100499 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100500 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
501 {
502 FORWARD_LAYER_SUPPORT_FUNC(__func__,
503 IsConvolution2dSupported,
504 data.m_Backends,
505 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100506 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100507 inputInfo,
508 outputInfo,
509 desc,
Keith Davis8f22bed2022-04-29 10:57:27 +0100510 weightsInfo,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100511 biases);
512 };
513
514 if(!IsDynamicTensor(outputInfo))
515 {
516 validateFunc(outputInfo, isSupported);
517 }
518 else
519 {
520 isSupported = AreDynamicTensorsSupported();
521 }
Kevin May42477c12020-03-26 13:34:14 +0000522
523 if (!isSupported)
524 {
525 return false;
526 }
527
Keith Davis8f22bed2022-04-29 10:57:27 +0100528 armnn::IConnectableLayer* startLayer = data.m_Network->AddConvolution2dLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +0100529 startLayer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +0000530
531 if (!startLayer)
532 {
533 return Fail("%s: AddConvolution2dLayer failed", __func__);
534 }
535
Kevin May42477c12020-03-26 13:34:14 +0000536 input.Connect(startLayer->GetInputSlot(0));
Keith Davis8f22bed2022-04-29 10:57:27 +0100537 weightsInput.Connect(startLayer->GetInputSlot(1));
538 biasInput.Connect(startLayer->GetInputSlot(2));
Kevin May42477c12020-03-26 13:34:14 +0000539
Kevin Mayfcf2a152020-09-08 16:06:32 +0100540 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
541 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +0000542}
543
544template<typename HalPolicy,
545 typename HalOperation = typename HalPolicy::Operation,
546 typename HalModel = typename HalPolicy::Model>
547bool ConvertDepthwiseConv2d_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
548{
549 using HalOperand = typename HalPolicy::Operand;
550 using HalOperandType = typename HalPolicy::OperandType;
551
552 ALOGV("HalPolicy::ConvertDepthwiseConv2d_1_2()");
553
554 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
555
556 if (!input.IsValid())
557 {
558 return Fail("%s: Operation has invalid inputs", __func__);
559 }
560
561 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
562
563 if (!output)
564 {
565 return Fail("%s: Could not read output 0", __func__);
566 }
567
568 const TensorInfo& inputInfo = input.GetTensorInfo();
569 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
570
Kevin May42477c12020-03-26 13:34:14 +0000571 // ArmNN does not currently support non-fixed weights or bias
Kevin Mayf762b832023-08-18 14:01:51 +0100572 if (!IsWeightsValid<HalPolicy>(operation, 1, model, false))
573 {
574 return Fail("%s: This Operation has unsupported weights HalOperandLifeTime", __func__);
575 }
576
Kevin May42477c12020-03-26 13:34:14 +0000577 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
578 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
Kevin Mayf762b832023-08-18 14:01:51 +0100579
Cathal Corbett915f2a72022-04-15 14:12:08 +0100580 if (weightsOperand->dimensions[0] != 1)
Kevin May42477c12020-03-26 13:34:14 +0000581 {
582 return Fail("%s: Invalid weights; for depthwise convolution, dimension 0 must be 1 but it is %i",
583 __func__, weightsOperand->dimensions[0] );
584 }
585
586 DepthwiseConvolution2dDescriptor desc;
587 desc.m_DataLayout = DataLayout::NHWC;
588
589 // Determine whether padding is implicit or explicit
590 bool implicitPadding = operation.inputs.size() == 8 ||
591 (operation.inputs.size() >= 9 &&
592 GetInputOperand<HalPolicy>(operation, 8, model)->type == HalOperandType::BOOL);
593
594 // Look ahead to find the optional DataLayout, if present
595 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
596 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, dataLayoutFlagIndex, model, data);
597
598 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
Kevin May42477c12020-03-26 13:34:14 +0000599 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
600 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
601
Cathal Corbett915f2a72022-04-15 14:12:08 +0100602 LayerInputHandle weightsInput = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
603 if (!weightsInput.IsValid())
Kevin May42477c12020-03-26 13:34:14 +0000604 {
Cathal Corbett915f2a72022-04-15 14:12:08 +0100605 return Fail("%s: Operation has invalid inputs", __func__);
Kevin May42477c12020-03-26 13:34:14 +0000606 }
607
Cathal Corbett915f2a72022-04-15 14:12:08 +0100608 const HalOperand* biasOperand = GetInputOperand<HalPolicy>(operation, 2, model);
609 if (!biasOperand)
Kevin May42477c12020-03-26 13:34:14 +0000610 {
Cathal Corbett915f2a72022-04-15 14:12:08 +0100611 return Fail("%s: Could not read bias", __func__);
Kevin May42477c12020-03-26 13:34:14 +0000612 }
613
Cathal Corbett915f2a72022-04-15 14:12:08 +0100614 LayerInputHandle biasInput = ConvertToLayerInputHandle<HalPolicy>(operation, 2, model, data); // 1D
615 if (!biasInput.IsValid())
616 {
617 return Fail("%s: Operation has invalid inputs", __func__);
618 }
619
620 biasInput.SanitizeQuantizationScale(weightsInput, input);
621 armnn::TensorInfo weightsInfo = weightsInput.GetTensorInfo();
622 armnn::TensorInfo biasInfo = biasInput.GetTensorInfo();
Kevin May42477c12020-03-26 13:34:14 +0000623
624 ActivationFn activation;
625
626 if (implicitPadding)
627 {
628 android::nn::PaddingScheme paddingScheme;
629 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
630 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
631 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
632 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data) ||
633 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 9, desc, model, data))
634 {
635 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
636 }
637
Cathal Corbett915f2a72022-04-15 14:12:08 +0100638 const uint32_t kernelX = weightsInfo.GetShape()[2];
639 const uint32_t kernelY = weightsInfo.GetShape()[1];
Kevin May42477c12020-03-26 13:34:14 +0000640 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
641 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
642
643 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
644 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
645 }
646 else if (operation.inputs.size() >= 11)
647 {
648 // explicit padding
649 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
650 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
651 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
652 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
653 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
654 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
655 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data) ||
656 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 12, desc, model, data))
657 {
658 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
659 }
660 }
661 else
662 {
663 return Fail("%s: Unsupported number of operation inputs", __func__);
664 }
665
666 desc.m_BiasEnabled = true;
Cathal Corbett915f2a72022-04-15 14:12:08 +0100667 Optional<TensorInfo> biases(biasInfo);
Kevin May42477c12020-03-26 13:34:14 +0000668
669 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100670 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100671 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
672 {
673 FORWARD_LAYER_SUPPORT_FUNC(__func__,
674 IsDepthwiseConvolutionSupported,
675 data.m_Backends,
676 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100677 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100678 inputInfo,
679 outputInfo,
680 desc,
Cathal Corbett915f2a72022-04-15 14:12:08 +0100681 weightsInfo,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100682 biases);
683 };
684
685 if(!IsDynamicTensor(outputInfo))
686 {
687 validateFunc(outputInfo, isSupported);
688 }
689 else
690 {
691 isSupported = AreDynamicTensorsSupported();
692 }
Kevin May42477c12020-03-26 13:34:14 +0000693
694 if (!isSupported)
695 {
696 return false;
697 }
698
Cathal Corbett915f2a72022-04-15 14:12:08 +0100699 armnn::IConnectableLayer* startLayer = data.m_Network->AddDepthwiseConvolution2dLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +0100700 startLayer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +0000701
702 if (!startLayer)
703 {
704 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
705 }
706
Kevin May42477c12020-03-26 13:34:14 +0000707 input.Connect(startLayer->GetInputSlot(0));
708
Cathal Corbett915f2a72022-04-15 14:12:08 +0100709 // Connect weights and bias inputs
710 weightsInput.Connect(startLayer->GetInputSlot(1));
711 biasInput.Connect(startLayer->GetInputSlot(2));
712
Kevin Mayfcf2a152020-09-08 16:06:32 +0100713 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
714 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +0000715}
716
717template<typename HalPolicy,
718 typename HalOperation = typename HalPolicy::Operation,
719 typename HalModel = typename HalPolicy::Model>
720bool ConvertDequantize_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
721{
722 ALOGV("HalPolicy::ConvertDequantize()");
723
724 if (IsQSymmDequantizeForWeights<HalPolicy>(operation, model))
725 {
726 // NOTE: QSymm8 weights are dequantized internally by the driver,
727 // therefore this type of Dequantize is implicitly supported
728 return true;
729 }
730
731 return ::ConvertDequantize<HalPolicy>(operation, model, data);
732}
733
734template<typename HalPolicy,
735 typename HalOperation = typename HalPolicy::Operation,
736 typename HalModel = typename HalPolicy::Model>
737bool ConvertElementwiseUnary(const HalOperation& operation,
738 const HalModel& model,
739 ConversionData& data,
740 UnaryOperation unaryOperation)
741{
742 using HalOperand = typename HalPolicy::Operand;
743
744 ALOGV("HalPolicy::ConvertElementwiseUnary()");
745 ALOGV("unaryOperation = %s", GetUnaryOperationAsCString(unaryOperation));
746
747 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
748
749 if (!input.IsValid())
750 {
751 return Fail("%s: Operation has invalid input", __func__);
752 }
753
754 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
755 if (!output)
756 {
757 return Fail("%s: Could not read output 0", __func__);
758 }
759
760 const TensorInfo& inputInfo = input.GetTensorInfo();
761 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
762
Kevin May42477c12020-03-26 13:34:14 +0000763 ElementwiseUnaryDescriptor descriptor(unaryOperation);
764
765 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100766 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100767 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
768 {
769 FORWARD_LAYER_SUPPORT_FUNC(__func__,
770 IsElementwiseUnarySupported,
771 data.m_Backends,
772 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100773 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100774 inputInfo,
775 outputInfo,
776 descriptor);
777 };
778
779 if(!IsDynamicTensor(outputInfo))
780 {
781 validateFunc(outputInfo, isSupported);
782 }
783 else
784 {
785 isSupported = AreDynamicTensorsSupported();
786 }
Kevin May42477c12020-03-26 13:34:14 +0000787
788 if (!isSupported)
789 {
790 return false;
791 }
792
793 IConnectableLayer* layer = data.m_Network->AddElementwiseUnaryLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +0100794 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +0100795 if (!layer)
796 {
797 return Fail("%s: Could not add the ElementwiseUnaryLayer", __func__);
798 }
Kevin May42477c12020-03-26 13:34:14 +0000799 input.Connect(layer->GetInputSlot(0));
800
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100801 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000802}
803
804template<typename HalPolicy,
805 typename HalOperation = typename HalPolicy::Operation,
806 typename HalModel = typename HalPolicy::Model>
807bool ConvertExpandDims(const HalOperation& operation, const HalModel& model, ConversionData& data)
808{
809 using HalOperand = typename HalPolicy::Operand;
810 using HalOperandType = typename HalPolicy::OperandType;
811
812 ALOGV("HalPolicy::ConvertExpandDims()");
813
814 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
815
816 if (!input.IsValid())
817 {
818 return Fail("%s: Operation has invalid input", __func__);
819 }
820
821 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
822 if (!output)
823 {
824 return Fail("%s: Operation has invalid output", __func__);
825 }
826
827 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +0000828
829 int32_t axis;
830 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
831 {
832 return Fail("%s: failed to get axis input value", __func__);
833 }
834
835 TensorShape targetShape;
836
837 try
838 {
839 targetShape = armnnUtils::ExpandDims(input.GetTensorInfo().GetShape(), axis);
840 }
841 catch (const std::exception& e)
842 {
843 return Fail("%s: %s", __func__, e.what());
844 }
845
Kevin May42477c12020-03-26 13:34:14 +0000846 ReshapeDescriptor reshapeDescriptor;
847 reshapeDescriptor.m_TargetShape = targetShape;
848
849 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100850 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100851 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
852 {
853 FORWARD_LAYER_SUPPORT_FUNC(__func__,
854 IsReshapeSupported,
855 data.m_Backends,
856 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100857 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100858 input.GetTensorInfo(),
859 outputInfo,
860 reshapeDescriptor);
861 };
862
863 if(!IsDynamicTensor(outputInfo))
864 {
Nikhil Rajc5e0bb02021-04-02 10:02:16 +0100865 if (targetShape != outputInfo.GetShape())
866 {
867 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
868 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100869 validateFunc(outputInfo, isSupported);
870 }
871 else
872 {
873 isSupported = AreDynamicTensorsSupported();
874 }
Kevin May42477c12020-03-26 13:34:14 +0000875
876 if (!isSupported)
877 {
878 return false;
879 }
880
881 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +0100882 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +0100883 if (!layer)
884 {
885 return Fail("%s: Could not add the ReshapeLayer", __func__);
886 }
Kevin May42477c12020-03-26 13:34:14 +0000887 input.Connect(layer->GetInputSlot(0));
888
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100889 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000890}
891
892template<typename HalPolicy,
Teresa Charlinf931af92020-04-10 16:46:53 +0100893 typename HalOperation = typename HalPolicy::Operation,
894 typename HalModel = typename HalPolicy::Model>
895bool ConvertGather(const HalOperation& operation, const HalModel& model, ConversionData& data)
896{
897 using HalOperand = typename HalPolicy::Operand;
898 using HalOperandType = typename HalPolicy::OperandType;
899
900 ALOGV("HalPolicy::ConvertGather()");
901
902 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
903 if (!input.IsValid())
904 {
905 return Fail("%s: Operation has invalid input", __func__);
906 }
907 auto inputDimensions = input.GetTensorInfo().GetNumDimensions();
908
909 LayerInputHandle indices = ConvertToLayerInputHandle<HalPolicy>(operation, 2, model, data);
910 if (!indices.IsValid())
911 {
912 return Fail("%s: Operation has invalid indices", __func__);
913 }
914 auto indicesDimensions = indices.GetTensorInfo().GetNumDimensions();
915
916 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
917 if (!output)
918 {
919 return Fail("%s: Operation has invalid output", __func__);
920 }
921 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
922 auto outputDimensions = outputInfo.GetNumDimensions();
Teresa Charlinf931af92020-04-10 16:46:53 +0100923 if (outputDimensions != inputDimensions + indicesDimensions - 1)
924 {
925 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 +0100926 __func__, outputDimensions, inputDimensions, indicesDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100927 }
928
Teresa Charline0fd6502022-12-05 16:45:50 +0000929 int32_t axis;
Teresa Charlinf931af92020-04-10 16:46:53 +0100930 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
931 {
932 return Fail("%s: Operation has invalid or unsupported axis operand", __func__);
933 }
Teresa Charline0fd6502022-12-05 16:45:50 +0000934 int32_t inputDimensions_int = static_cast<int32_t>(inputDimensions);
935 if ((axis < -inputDimensions_int) || (inputDimensions_int <= axis))
Teresa Charlinf931af92020-04-10 16:46:53 +0100936 {
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100937 return Fail("%s: Operation has invalid axis: %d. It is out of bounds [-%d, %d))", __func__, axis,
938 inputDimensions, inputDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100939 }
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100940
941 GatherDescriptor desc;
942 desc.m_Axis = axis;
Teresa Charlinf931af92020-04-10 16:46:53 +0100943
944 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +0100945 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100946 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
947 {
948 FORWARD_LAYER_SUPPORT_FUNC(__func__,
949 IsGatherSupported,
950 data.m_Backends,
951 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +0100952 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100953 input.GetTensorInfo(),
954 indices.GetTensorInfo(),
955 outputInfo,
956 desc);
957 };
958
959 if(!IsDynamicTensor(outputInfo))
960 {
961 validateFunc(outputInfo, isSupported);
962 }
963 else
964 {
965 isSupported = AreDynamicTensorsSupported();
966 }
967
Teresa Charlinf931af92020-04-10 16:46:53 +0100968 if (!isSupported)
969 {
970 return false;
971 }
972
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100973 IConnectableLayer* layer = data.m_Network->AddGatherLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +0100974 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +0100975 if (!layer)
976 {
977 return Fail("%s: Could not add the GatherLayer", __func__);
978 }
Teresa Charlinf931af92020-04-10 16:46:53 +0100979 input.Connect(layer->GetInputSlot(0));
980 indices.Connect(layer->GetInputSlot(1));
981
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100982 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Teresa Charlinf931af92020-04-10 16:46:53 +0100983}
984
985template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +0000986 typename HalOperation = typename HalPolicy::Operation,
987 typename HalModel = typename HalPolicy::Model>
988bool ConvertGroupedConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
989{
990 using HalOperand = typename HalPolicy::Operand;
991 using HalOperandType = typename HalPolicy::OperandType;
992
993 ALOGV("HalPolicy::ConvertGroupedConv2d()");
994
995 //
996 // Parse data
997 //
998 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
999 if (!input.IsValid())
1000 {
1001 return Fail("%s: Operation has invalid inputs", __func__);
1002 }
1003 const TensorInfo& inputInfo = input.GetTensorInfo();
1004
1005 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1006 if (!output)
1007 {
1008 return Fail("%s: Could not read output 0", __func__);
1009 }
Finn Williamsb0331172020-10-08 14:33:13 +01001010 TensorInfo outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001011
1012 // Look ahead to determine data layout
1013 DataLayout dataLayout = DataLayout::NHWC;
1014 if (operation.inputs.size() == 12)
1015 {
1016 dataLayout = OptionalDataLayout<HalPolicy>(operation, 11, model, data);
1017 }
1018 else
1019 {
1020 dataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
1021 }
1022
1023 // NOTE:
1024 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
1025 // but Arm NN expects the filter's height and width indices to match the input's height and
1026 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
1027 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
1028 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
1029 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
1030 model, data, ohwiToOihw) :
1031 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1032 const ConstTensorPin biasesPin =
1033 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
1034 if (!weightsPin.IsValid() || !biasesPin.IsValid())
1035 {
1036 return Fail("%s: Operation has invalid inputs", __func__);
1037 }
1038
1039 ConstTensor weights = weightsPin.GetConstTensor();
1040 ConstTensor biases = biasesPin.GetConstTensor();
1041 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
1042
1043 const TensorShape& inputShape = inputInfo.GetShape();
1044 const TensorShape& outputShape = outputInfo.GetShape();
1045 const TensorShape& weightsShape = weights.GetShape();
Kevin May42477c12020-03-26 13:34:14 +00001046
1047 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
1048 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
1049 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
1050 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
1051
1052 Convolution2dDescriptor desc;
1053 desc.m_DataLayout = dataLayout;
1054 desc.m_BiasEnabled = true;
1055
Finn Williamsf769f292021-06-25 12:53:09 +01001056 unsigned int numGroups;
Kevin May42477c12020-03-26 13:34:14 +00001057 ActivationFn activation;
1058
1059 if (operation.inputs.size() == 12)
1060 {
1061 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1062 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1063 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1064 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1065 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1066 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1067 !GetInputScalar<HalPolicy>(operation, 9, HalOperandType::INT32, numGroups, model, data) ||
1068 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data))
1069 {
1070 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
1071 }
1072
1073 }
1074 else if (operation.inputs.size() == 9)
1075 {
1076 android::nn::PaddingScheme paddingScheme;
1077 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
1078 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1079 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1080 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, numGroups, model, data) ||
1081 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
1082 {
1083 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
1084 }
1085
1086 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
1087 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
1088
1089 const uint32_t kernelX = weightsShape[widthIndex];
1090 const uint32_t kernelY = weightsShape[heightIndex];
1091
1092 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
1093 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
1094 }
1095 else
1096 {
1097 return Fail("%s: Unsupported number of operation inputs", __func__);
1098 }
1099
Finn Williamsb0331172020-10-08 14:33:13 +01001100 // Equivalent to outputShape[channelsIndex], but we can't know the outputShape in the case of dynamic tensors
1101 const unsigned int outputChannels = weightsShape[0];
Kevin May42477c12020-03-26 13:34:14 +00001102
1103 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
1104 const unsigned int channelMultiplier = outputChannels / numGroups;
1105
1106 //
1107 // Validate all relevant inputs
1108 //
1109 if (numGroups <= 0)
1110 {
1111 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
1112 }
1113
1114 if (outputChannels % numGroups != 0u)
1115 {
1116 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
1117 }
1118
1119 //
1120 // Set up Splitter layer
1121 //
1122 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
1123 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
1124
1125 TensorInfo splitterOutputInfo(4,
1126 splitterDimSizes,
1127 inputInfo.GetDataType(),
1128 inputInfo.GetQuantizationScale(),
1129 inputInfo.GetQuantizationOffset());
1130
1131 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
1132
1133 ViewsDescriptor splitterDesc(numGroups);
Mike Kelly23eccd82023-08-08 12:29:14 +01001134 splitterDesc.SetAxis(armnn::numeric_cast<int32_t>(channelsIndex));
Kevin May42477c12020-03-26 13:34:14 +00001135 for (unsigned int group = 0u; group < numGroups; ++group)
1136 {
1137 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
1138 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
1139 {
1140 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
1141 }
1142 }
1143
1144 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001145 armnn::BackendId setBackendSplit;
Kevin May42477c12020-03-26 13:34:14 +00001146 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1147 IsSplitterSupported,
1148 data.m_Backends,
1149 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001150 setBackendSplit,
Kevin May42477c12020-03-26 13:34:14 +00001151 inputInfo,
1152 splitterOutputInfos,
1153 splitterDesc);
1154 if (!isSupported)
1155 {
1156 return false;
1157 }
1158
1159 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001160 splitterLayer->SetBackendId(setBackendSplit);
Kevin May42477c12020-03-26 13:34:14 +00001161 if (!splitterLayer)
1162 {
1163 return Fail("%s: Failed to add SplitterLayer", __func__);
1164 }
1165
1166 input.Connect(splitterLayer->GetInputSlot(0));
1167 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
1168 {
1169 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
1170 }
1171
1172 //
1173 // Set up Convolution2d layers for each group
1174 //
1175
1176 // Set up group tensor shapes
1177 TensorShape groupInputShape(inputShape);
1178 groupInputShape[channelsIndex] = channelsPerGroup;
1179
Kevin May42477c12020-03-26 13:34:14 +00001180 TensorShape groupWeightsShape(weightsShape);
1181 groupWeightsShape[0] /= channelMultiplier * numGroups;
1182
1183 TensorShape groupBiasesShape({ 1 });
1184
1185 // Set up group tensor infos
1186 TensorInfo groupInputInfo(inputInfo);
1187 groupInputInfo.SetShape(groupInputShape);
1188
1189 const TensorInfo& weightsInfo = weights.GetInfo();
1190 TensorInfo groupWeightsInfo(weightsInfo);
1191 groupWeightsInfo.SetShape(groupWeightsShape);
1192
1193 const TensorInfo& biasesInfo = biases.GetInfo();
1194 TensorInfo groupBiasesInfo(biasesInfo);
1195 groupBiasesInfo.SetShape(groupBiasesShape);
1196
1197 TensorInfo groupOutputInfo(outputInfo);
Finn Williamsb0331172020-10-08 14:33:13 +01001198
1199 TensorShape groupOutputShape(outputShape);
1200 const bool isDynamic = IsDynamicTensor(outputInfo);
1201 if (!isDynamic)
1202 {
1203 groupOutputShape[channelsIndex] = 1;
1204 }
Kevin May42477c12020-03-26 13:34:14 +00001205 groupOutputInfo.SetShape(groupOutputShape);
1206
1207 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
1208 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
1209
1210 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
1211 for (unsigned int group = 0u; group < numGroups; ++group)
1212 {
1213 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1214 {
1215 auto index = group * channelMultiplier + m;
1216
1217 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
1218 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
1219
1220 if (weightsInfo.HasPerAxisQuantization())
1221 {
1222 // Extract per-axis quantization scales for group weights
1223 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
1224 groupWeightsInfo.SetQuantizationScales(
1225 std::vector<float>(weightsQuantScales.begin() + index,
1226 weightsQuantScales.begin() + index + groupWeightsShape[0]));
1227
1228 // Extract per-axis quantization scales for group biases
1229 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
1230 groupBiasesInfo.SetQuantizationScales(
1231 std::vector<float>(biasesQuantScales.begin() + index,
1232 biasesQuantScales.begin() + index + groupWeightsShape[0]));
1233 }
1234
1235 // Extract weights and biases data for current group convolution
1236 ConstTensor groupWeights(groupWeightsInfo,
1237 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
1238 weightsDataOffset));
1239 ConstTensor groupBiases(groupBiasesInfo,
1240 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
1241 biasesDataOffset));
1242
1243 isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001244 armnn::BackendId setBackendConv;
Finn Williamsb0331172020-10-08 14:33:13 +01001245 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1246 {
1247 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1248 IsConvolution2dSupported,
1249 data.m_Backends,
1250 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001251 setBackendConv,
Finn Williamsb0331172020-10-08 14:33:13 +01001252 groupInputInfo,
1253 outputInfo,
1254 desc,
1255 groupWeightsInfo,
1256 Optional<TensorInfo>(groupBiasesInfo));
1257 };
1258
1259 if(!isDynamic)
1260 {
1261 validateFunc(groupOutputInfo, isSupported);
1262 }
1263 else
1264 {
1265 isSupported = AreDynamicTensorsSupported();
1266 }
1267
Kevin May42477c12020-03-26 13:34:14 +00001268 if (!isSupported)
1269 {
1270 return false;
1271 }
1272
Teresa Charlinf8696262022-08-30 15:55:28 +01001273 IConnectableLayer* weightsLayer = data.m_Network->AddConstantLayer(groupWeights);
1274 IConnectableLayer* biasLayer = data.m_Network->AddConstantLayer(groupBiases);
1275 IConnectableLayer* convLayer = data.m_Network->AddConvolution2dLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001276 convLayer->SetBackendId(setBackendConv);
Keith Davis8f22bed2022-04-29 10:57:27 +01001277
Kevin May42477c12020-03-26 13:34:14 +00001278 if (!convLayer)
1279 {
1280 return Fail("%s: AddConvolution2dLayer failed", __func__);
1281 }
1282
1283 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
Teresa Charlinf8696262022-08-30 15:55:28 +01001284 weightsLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(1));
1285 biasLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(2));
1286
1287 weightsLayer->GetOutputSlot(0).SetTensorInfo(groupWeightsInfo);
1288 biasLayer->GetOutputSlot(0).SetTensorInfo(groupBiasesInfo);
Kevin May42477c12020-03-26 13:34:14 +00001289 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1290
Finn Williamsb0331172020-10-08 14:33:13 +01001291 if(isDynamic)
1292 {
1293 convLayer->GetOutputSlot(0).IsTensorInfoSet();
1294
1295 validateFunc(convLayer->GetOutputSlot(0).GetTensorInfo(), isSupported);
1296
1297 outputInfo = convLayer->GetOutputSlot(0).GetTensorInfo();
1298
1299 if (!isSupported)
1300 {
1301 return false;
1302 }
1303 }
1304
Kevin May42477c12020-03-26 13:34:14 +00001305 convLayers[index] = convLayer;
1306 }
1307 }
1308
1309 //
1310 // Set up Concat layer
1311 //
Finn Williamsb0331172020-10-08 14:33:13 +01001312 ConcatDescriptor concatDescriptor;
1313 // Equivalent to outputShape[channelsIndex], but we can't know the outputShape in the case of dynamic tensors
1314 concatDescriptor = ConcatDescriptor(weightsShape[0]);
Kevin May42477c12020-03-26 13:34:14 +00001315 for (unsigned int group = 0u; group < numGroups; ++group)
1316 {
1317 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1318 {
1319 auto index = group * channelMultiplier + m;
1320 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1321 concatDescriptor.SetConcatAxis(channelsIndex);
1322 }
1323 }
1324
1325 isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001326 armnn::BackendId setBackendConcat;
Finn Williamsb0331172020-10-08 14:33:13 +01001327 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1328 IsConcatSupported,
1329 data.m_Backends,
1330 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001331 setBackendConcat,
Finn Williamsb0331172020-10-08 14:33:13 +01001332 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1333 outputInfo,
1334 concatDescriptor);
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001335
Kevin May42477c12020-03-26 13:34:14 +00001336 if (!isSupported)
1337 {
1338 return false;
1339 }
1340
1341 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001342 concatLayer->SetBackendId(setBackendConcat);
Kevin May42477c12020-03-26 13:34:14 +00001343 if (!concatLayer)
1344 {
1345 return Fail("%s: AddConcatLayer failed", __func__);
1346 }
1347
1348 for (unsigned int group = 0u; group < numGroups; ++group)
1349 {
1350 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1351 {
1352 auto index = group * channelMultiplier + m;
1353 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1354 }
1355 }
1356 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1357
Kevin Mayfcf2a152020-09-08 16:06:32 +01001358 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *concatLayer, model,
Finn Williamsb0331172020-10-08 14:33:13 +01001359 data, nullptr, nullptr, activation);
Kevin May42477c12020-03-26 13:34:14 +00001360}
1361
1362template<typename HalPolicy,
1363 typename HalOperation = typename HalPolicy::Operation,
1364 typename HalModel = typename HalPolicy::Model>
1365bool ConvertInstanceNormalization(const HalOperation& operation, const HalModel& model, ConversionData& data)
1366{
1367 using HalOperand = typename HalPolicy::Operand;
1368 using HalOperandType = typename HalPolicy::OperandType;
1369
1370 ALOGV("HalPolicy::ConvertInstanceNormalization()");
1371
1372 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1373 if (!input.IsValid())
1374 {
1375 return Fail("%s: Operation has an invalid input 0", __func__);
1376 }
1377
1378 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1379 if (!output)
1380 {
1381 return Fail("%s: Operation has an invalid output", __func__);
1382 }
1383
1384 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001385
1386 // Determine data type of input tensor
1387 HalOperandType inputType;
1388 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1389 {
1390 return Fail("%s: Operation has invalid inputs", __func__);
1391 }
1392
1393 InstanceNormalizationDescriptor desc;
1394
1395 // Read gamma, beta & epsilon
1396 if (inputType == HalOperandType::TENSOR_FLOAT16)
1397 {
1398 Half fp16Gamma;
1399 Half fp16Beta;
1400 Half fp16Epsilon;
1401
1402 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Gamma, model, data) ||
1403 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, fp16Beta, model, data) ||
1404 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT16, fp16Epsilon, model, data))
1405 {
1406 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1407 }
1408
1409 desc.m_Gamma = static_cast<float>(fp16Gamma);
1410 desc.m_Beta = static_cast<float>(fp16Beta);
1411 desc.m_Eps = static_cast<float>(fp16Epsilon);
1412 }
1413 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1414 {
1415 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, desc.m_Gamma, model, data) ||
1416 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT32, desc.m_Beta, model, data) ||
1417 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT32, desc.m_Eps, model, data))
1418 {
1419 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1420 }
1421 }
1422 else
1423 {
1424 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1425 }
1426
1427 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 4, model, data);
1428
1429 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001430 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001431 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1432 {
1433 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1434 IsInstanceNormalizationSupported,
1435 data.m_Backends,
1436 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001437 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001438 input.GetTensorInfo(),
1439 outputInfo,
1440 desc);
1441 };
1442
1443 if(IsDynamicTensor(outputInfo))
1444 {
1445 isSupported = AreDynamicTensorsSupported();
1446 }
1447 else
1448 {
1449 validateFunc(outputInfo, isSupported);
1450 }
1451
Kevin May42477c12020-03-26 13:34:14 +00001452 if (!isSupported)
1453 {
1454 return false;
1455 }
1456
1457 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001458 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00001459 input.Connect(layer->GetInputSlot(0));
1460
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001461 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001462}
1463
1464template<typename HalPolicy,
1465 typename HalOperation = typename HalPolicy::Operation,
1466 typename HalModel = typename HalPolicy::Model>
1467bool ConvertLogSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
1468{
1469 using HalOperand = typename HalPolicy::Operand;
1470 using HalOperandType = typename HalPolicy::OperandType;
1471
1472 ALOGV("HalPolicy::ConvertLogSoftmax()");
1473
1474 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1475 if (!input.IsValid())
1476 {
1477 return Fail("%s: Failed to read input 0", __func__);
1478 }
1479
1480 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1481 if (!output)
1482 {
1483 return Fail("%s: Failed to read output", __func__);
1484 }
1485
1486 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001487
1488 // Determine data type of input tensor
1489 HalOperandType inputType;
1490 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1491 {
1492 return Fail("%s: Operation has invalid inputs", __func__);
1493 }
1494
1495 LogSoftmaxDescriptor descriptor;
1496
1497 // Read beta
1498 if (inputType == HalOperandType::TENSOR_FLOAT16)
1499 {
1500 Half fp16Beta;
1501 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Beta, model, data))
1502 {
1503 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1504 }
1505
1506 descriptor.m_Beta = static_cast<float>(fp16Beta);
1507 }
1508 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1509 {
1510 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, descriptor.m_Beta, model, data))
1511 {
1512 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1513 }
1514 }
1515 else
1516 {
1517 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1518 }
1519
1520 // Read axis
1521 if (!GetInputInt32<HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1522 {
1523 return Fail("%s: Failed to read input 2", __func__);
1524 }
1525
1526 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001527 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001528 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1529 {
1530 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1531 IsLogSoftmaxSupported,
1532 data.m_Backends,
1533 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001534 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001535 input.GetTensorInfo(),
1536 outputInfo,
1537 descriptor);
1538 };
1539
1540 if(IsDynamicTensor(outputInfo))
1541 {
1542 isSupported = AreDynamicTensorsSupported();
1543 }
1544 else
1545 {
1546 validateFunc(outputInfo, isSupported);
1547 }
1548
Kevin May42477c12020-03-26 13:34:14 +00001549 if (!isSupported)
1550 {
1551 return false;
1552 }
1553
1554 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001555 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00001556 if (!layer)
1557 {
Mike Kellye2d611e2021-10-14 12:35:58 +01001558 return Fail("%s: Could not add the LogSoftmaxLayer", __func__);
Kevin May42477c12020-03-26 13:34:14 +00001559 }
Kevin May42477c12020-03-26 13:34:14 +00001560 input.Connect(layer->GetInputSlot(0));
1561
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001562 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001563}
1564
1565template<typename HalPolicy,
1566 typename HalOperation = typename HalPolicy::Operation,
1567 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +00001568bool ConvertPadV2(const HalOperation& operation, const HalModel& model, ConversionData& data)
1569{
1570 using HalOperand = typename HalPolicy::Operand;
1571 using HalOperandType = typename HalPolicy::OperandType;
1572
1573 ALOGV("HalPolicy::ConvertPadV2()");
1574
1575 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1576 if (!input.IsValid())
1577 {
1578 return Fail("%s: Could not read input 0", __func__);
1579 }
1580
1581 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1582 if (!output)
1583 {
1584 return Fail("%s: Could not read output", __func__);
1585 }
1586
1587 const TensorInfo& inputInfo = input.GetTensorInfo();
1588 unsigned int rank = inputInfo.GetNumDimensions();
1589
1590 PadDescriptor descriptor;
1591 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
1592 {
1593 return Fail("%s: Could not convert paddings", __func__);
1594 }
1595
1596 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001597
1598 // Determine type of padding value
1599 HalOperandType operandType0;
1600 HalOperandType operandType2;
1601
1602 if (!GetOperandType<HalPolicy>(operation, 0, model, operandType0) ||
1603 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1604 {
1605 return Fail("%s: Operation has invalid inputs", __func__);
1606 }
1607
1608 // Read value to use for padding
1609 if (operandType0 == HalOperandType::TENSOR_FLOAT16 && operandType2 == HalOperandType::FLOAT16)
1610 {
1611 Half f16PadValue;
1612 if (!GetInputScalar<HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1613 {
1614 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1615 }
1616
1617 descriptor.m_PadValue = f16PadValue;
1618 }
1619 else if (operandType0 == HalOperandType::TENSOR_FLOAT32 && operandType2 == HalOperandType::FLOAT32)
1620 {
1621 if (!GetInputFloat32<HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1622 {
1623 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1624 }
1625 }
Teresa Charlind3381d52021-06-02 18:35:16 +01001626 else if (isQuantizedOperand(operandType0) && operandType2 == HalOperandType::INT32)
Kevin May42477c12020-03-26 13:34:14 +00001627 {
1628 int32_t intPadValue = 0;
1629 if (!GetInputInt32<HalPolicy>(operation, 2, intPadValue, model, data))
1630 {
1631 return Fail("%s: Could not read input 2 (INT32)", __func__);
1632 }
1633 descriptor.m_PadValue = intPadValue;
1634 }
1635 else
1636 {
1637 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1638 }
1639
1640 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001641 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001642 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1643 {
1644 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1645 IsPadSupported,
1646 data.m_Backends,
1647 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001648 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001649 inputInfo,
1650 outputInfo,
1651 descriptor);
1652 };
1653
1654 if(IsDynamicTensor(outputInfo))
1655 {
1656 isSupported = AreDynamicTensorsSupported();
1657 }
1658 else
1659 {
1660 validateFunc(outputInfo, isSupported);
1661 }
1662
Kevin May42477c12020-03-26 13:34:14 +00001663 if (!isSupported)
1664 {
1665 return false;
1666 }
1667
1668 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +01001669 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01001670 if (!layer)
1671 {
1672 return Fail("%s: Could not add the PadLayer", __func__);
1673 }
Kevin May42477c12020-03-26 13:34:14 +00001674 input.Connect(layer->GetInputSlot(0));
Kevin May42477c12020-03-26 13:34:14 +00001675
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001676 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001677}
1678
1679template<typename HalPolicy,
1680 typename HalOperation = typename HalPolicy::Operation,
1681 typename HalModel = typename HalPolicy::Model>
1682bool ConvertPrelu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1683{
1684 using HalOperand = typename HalPolicy::Operand;
1685
1686 ALOGV("HalPolicy::ConvertPrelu()");
1687
1688 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1689 LayerInputHandle alpha = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1690
1691 if (!input.IsValid() || !alpha.IsValid())
1692 {
1693 return Fail("%s: Operation has invalid inputs", __func__);
1694 }
1695
1696 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1697
1698 if (!output)
1699 {
1700 return Fail("%s: Could not read output", __func__);
1701 }
1702
1703 const TensorInfo& inputInfo = input.GetTensorInfo();
1704 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1705 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1706
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001707 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001708 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001709 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
Kevin May42477c12020-03-26 13:34:14 +00001710 {
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001711 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1712 IsPreluSupported,
1713 data.m_Backends,
1714 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001715 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001716 inputInfo,
1717 alphaInfo,
1718 outputInfo);
1719 };
1720
1721 if(IsDynamicTensor(outputInfo))
1722 {
1723 isSupported = AreDynamicTensorsSupported();
1724 }
1725 else
1726 {
1727 validateFunc(outputInfo, isSupported);
Kevin May42477c12020-03-26 13:34:14 +00001728 }
1729
Kevin May42477c12020-03-26 13:34:14 +00001730 if (!isSupported)
1731 {
1732 return false;
1733 }
1734
1735 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Cathal Corbett8de96f72022-09-01 13:34:59 +01001736 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00001737 if (!layer)
1738 {
Mike Kellye2d611e2021-10-14 12:35:58 +01001739 return Fail("%s: Could not add the PreluLayer", __func__);
Kevin May42477c12020-03-26 13:34:14 +00001740 }
1741
1742 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1743 if (!isReshapeSupported)
1744 {
1745 return false;
1746 }
1747
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001748 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001749}
1750
1751template<typename HalPolicy,
1752 typename HalOperation = typename HalPolicy::Operation,
1753 typename HalModel = typename HalPolicy::Model>
1754bool ConvertQuantize(const HalOperation& operation, const HalModel& model, ConversionData& data)
1755{
1756 using HalOperand = typename HalPolicy::Operand;
1757
1758 ALOGV("HalPolicy::ConvertQuantize()");
1759
1760 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1761 if (!input.IsValid())
1762 {
1763 return Fail("%s: Operation has invalid input", __func__);
1764 }
1765
1766 const HalOperand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1767 if (!outputOperand)
1768 {
1769 return Fail("%s: Operation has invalid outputs", __func__);
1770 }
1771
1772 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001773
1774 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01001775 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001776 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1777 {
1778 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1779 IsQuantizeSupported,
1780 data.m_Backends,
1781 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01001782 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001783 input.GetTensorInfo(),
1784 outputInfo);
1785 };
1786
1787 if(IsDynamicTensor(outputInfo))
1788 {
1789 isSupported = AreDynamicTensorsSupported();
1790 }
1791 else
1792 {
1793 validateFunc(outputInfo, isSupported);
1794 }
1795
Kevin May42477c12020-03-26 13:34:14 +00001796 if (!isSupported)
1797 {
1798 return false;
1799 }
1800
1801 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Cathal Corbett8de96f72022-09-01 13:34:59 +01001802 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01001803 if (!layer)
1804 {
1805 return Fail("%s: Could not add the QuantizeLayer", __func__);
1806 }
Kevin May42477c12020-03-26 13:34:14 +00001807 input.Connect(layer->GetInputSlot(0));
1808
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001809 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001810}
1811
1812template<typename HalPolicy,
1813 typename HalOperation = typename HalPolicy::Operation,
1814 typename HalModel = typename HalPolicy::Model>
Sadik Armagan813f2302020-05-19 14:10:30 +01001815bool ConvertQuantized16BitLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
Kevin May42477c12020-03-26 13:34:14 +00001816{
1817 using HalOperand = typename HalPolicy::Operand;
1818
Sadik Armagan813f2302020-05-19 14:10:30 +01001819 ALOGV("HalPolicy::ConvertQuantized16BitLstm()");
Kevin May42477c12020-03-26 13:34:14 +00001820
1821 //Inputs:
1822 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1823 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1824 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1825 if (!input.IsValid())
1826 {
1827 return Fail("%s: Could not read input 0: input", __func__);
1828 }
1829
1830 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1831 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1832 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1833 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 13, model, data);
1834 if (!previousCellStateIn.IsValid())
1835 {
1836 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1837 }
1838
1839 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1840 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1841 // is quantized with a fixed quantization range of -1, 127/128.
1842 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<HalPolicy>(operation, 14, model, data);
1843 if (!previousOutputIn.IsValid())
1844 {
1845 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1846 }
1847
1848 // Get the input tensors:
1849 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1850 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1851 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1852 const ConstTensorPin inputToInputWeightsPin =
1853 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1854
1855 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1856 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1857 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1858 const ConstTensorPin inputToForgetWeightsPin =
1859 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
1860
1861 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1862 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1863 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1864 const ConstTensorPin inputToCellWeightsPin =
1865 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 3, model, data);
1866
1867 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1868 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1869 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1870 const ConstTensorPin inputToOutputWeightsPin =
1871 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 4, model, data);
1872
1873 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1874 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1875 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1876 const ConstTensorPin recurrentToInputWeightsPin =
1877 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 5, model, data);
1878
1879 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1880 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1881 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1882 const ConstTensorPin recurrentToForgetWeightsPin =
1883 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 6, model, data);
1884
1885 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1886 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1887 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1888 const ConstTensorPin recurrentToCellWeightsPin =
1889 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 7, model, data);
1890
1891 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1892 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1893 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1894 const ConstTensorPin recurrentToOutputWeightsPin =
1895 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 8, model, data);
1896
1897 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1898 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1899 // of input and weights scales and zeroPoint equal to 0.
1900 const ConstTensorPin inputGateBiasPin =
1901 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 9, model, data);
1902
1903 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1904 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1905 // of input and weights scales and zeroPoint equal to 0.
1906 const ConstTensorPin forgetGateBiasPin =
1907 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 10, model, data);
1908
1909 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1910 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1911 // and weights scales and zeroPoint equal to 0.
1912 const ConstTensorPin cellBiasPin =
1913 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 11, model, data);
1914
1915 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1916 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1917 // of input and weights scales and zeroPoint equal to 0.
1918 const ConstTensorPin outputGateBiasPin =
1919 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 12, model, data);
1920
1921 if (!inputToInputWeightsPin.IsValid() ||
1922 !inputToForgetWeightsPin.IsValid() ||
1923 !inputToCellWeightsPin.IsValid() ||
1924 !inputToOutputWeightsPin.IsValid() ||
1925 !recurrentToInputWeightsPin.IsValid() ||
1926 !recurrentToForgetWeightsPin.IsValid() ||
1927 !recurrentToCellWeightsPin.IsValid() ||
1928 !recurrentToOutputWeightsPin.IsValid() ||
1929 !inputGateBiasPin.IsValid() ||
1930 !forgetGateBiasPin.IsValid() ||
1931 !cellBiasPin.IsValid() ||
1932 !outputGateBiasPin.IsValid())
1933 {
1934 return Fail("%s: Operation has invalid tensor inputs", __func__);
1935 }
1936
1937 // Outputs:
1938 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
1939 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
1940 // of -2^4, 2^4 * 32767/32768.
1941 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 0, model);
1942 if (!cellStateOut)
1943 {
1944 return Fail("%s: Could not read output 0: cellStateOut", __func__);
1945 }
1946
1947 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
1948 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
1949 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 1, model);
1950 if (!output)
1951 {
1952 return Fail("%s: Could not read output 1: output", __func__);
1953 }
1954
1955 // Inputs
1956 const TensorInfo& inputInfo = input.GetTensorInfo();
1957 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
1958 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
1959
1960 // Outputs
1961 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
1962 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1963
1964 // Dynamic tensors currently not supported
1965 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
1966 {
1967 return Fail("%s: Dynamic output tensors are not supported", __func__);
1968 }
1969
1970 QuantizedLstmInputParams params;
1971
1972 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
1973 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
1974 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
1975 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
1976 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
1977 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
1978 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
1979 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
1980 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
1981 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
1982 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
1983 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
1984
1985 QuantizedLstmInputParamsInfo paramsInfo;
1986 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
1987 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
1988 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
1989 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
1990 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
1991 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
1992 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
1993 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
1994 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
1995 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
1996 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
1997 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
1998
1999 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002000 armnn::BackendId setBackend;
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002001 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2002 {
2003 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2004 IsQuantizedLstmSupported,
2005 data.m_Backends,
2006 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002007 setBackend,
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002008 inputInfo,
2009 previousCellStateInInfo,
2010 previousOutputInInfo,
2011 cellStateOutInfo,
2012 outputInfo,
2013 paramsInfo);
2014 };
2015
2016 bool isDynamic = false;
2017 if (!IsDynamicTensor(cellStateOutInfo) &&
2018 !IsDynamicTensor(outputInfo))
2019 {
2020 validateFunc(outputInfo, isSupported);
2021 }
2022 else
2023 {
2024 isDynamic = true;
2025 isSupported = AreDynamicTensorsSupported();
2026 }
Kevin May42477c12020-03-26 13:34:14 +00002027
2028 if (!isSupported)
2029 {
2030 return false;
2031 }
2032
2033 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
Cathal Corbett8de96f72022-09-01 13:34:59 +01002034 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00002035 input.Connect(layer->GetInputSlot(0));
2036 previousCellStateIn.Connect(layer->GetInputSlot(1));
2037 previousOutputIn.Connect(layer->GetInputSlot(2));
2038
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002039 if (!isDynamic)
2040 {
2041 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2042 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data));
2043 }
2044 else
2045 {
2046 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2047 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01002048 operation, 1, *layer, 1, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002049 }
2050
Kevin May42477c12020-03-26 13:34:14 +00002051}
2052
2053template<typename HalPolicy,
2054 typename HalOperation = typename HalPolicy::Operation,
2055 typename HalModel = typename HalPolicy::Model>
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002056bool ConvertReduce(const HalOperation& operation,
2057 const HalModel& model,
2058 ConversionData& data,
2059 ReduceOperation reduceOperation)
2060{
2061 using HalOperand = typename HalPolicy::Operand;
2062 using HalOperandType = typename HalPolicy::OperandType;
2063
2064 armnn::ReduceDescriptor descriptor;
2065 descriptor.m_ReduceOperation = reduceOperation;
2066
2067 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2068 if (!input.IsValid())
2069 {
2070 return Fail("%s: Operation has invalid inputs", __func__);
2071 }
2072 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2073
2074 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2075 if (!output)
2076 {
2077 return Fail("%s: Could not read output 0", __func__);
2078 }
2079 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2080
2081 const HalOperand* axisOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2082 if (!axisOperand)
2083 {
2084 return Fail("%s: Could not read input 1", __func__);
2085 }
2086 std::vector<int32_t> axis;
2087 if (!GetTensorInt32Values<HalPolicy>(*axisOperand, axis, model, data))
2088 {
2089 return Fail("%s: Input 1 has invalid values", __func__);
2090 }
2091
2092 // Convert the axis to unsigned int and remove duplicates.
2093 unsigned int rank = inputInfo.GetNumDimensions();
2094 std::set<unsigned int> uniqueAxis;
2095 std::transform(axis.begin(), axis.end(),
2096 std::inserter(uniqueAxis, uniqueAxis.begin()),
2097 [rank](int i) -> unsigned int { return (i + rank) % rank; });
2098 descriptor.m_vAxis.assign(uniqueAxis.begin(), uniqueAxis.end());
2099
2100 // Get the "keep dims" flag.
2101 if (!GetInputScalar<HalPolicy>(operation, 2, HalOperandType::BOOL, descriptor.m_KeepDims, model, data))
2102 {
2103 return Fail("%s: Could not read input 2", __func__);
2104 }
2105
2106 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002107 armnn::BackendId setBackend;
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002108 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2109 {
2110 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2111 IsReduceSupported,
2112 data.m_Backends,
2113 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002114 setBackend,
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002115 inputInfo,
2116 outputInfo,
2117 descriptor);
2118 };
2119
2120 if(!IsDynamicTensor(outputInfo))
2121 {
2122 validateFunc(outputInfo, isSupported);
2123 }
2124 else
2125 {
2126 isSupported = AreDynamicTensorsSupported();
2127 }
2128
2129 if (!isSupported)
2130 {
2131 return false;
2132 }
2133
2134 armnn::IConnectableLayer* const layer = data.m_Network->AddReduceLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +01002135 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01002136 if (!layer)
2137 {
2138 return Fail("%s: Could not add the ReduceLayer", __func__);
2139 }
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002140 input.Connect(layer->GetInputSlot(0));
2141
2142 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
2143}
2144
2145template<typename HalPolicy,
2146 typename HalOperation = typename HalPolicy::Operation,
2147 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +00002148bool ConvertResize(const HalOperation& operation,
2149 const HalModel& model,
2150 ConversionData& data,
2151 ResizeMethod resizeMethod)
2152{
2153 using HalOperand = typename HalPolicy::Operand;
2154 using HalOperandType = typename HalPolicy::OperandType;
2155 ALOGV("HalPolicy::ConvertResize()");
2156 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
2157
2158 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2159 if (!input.IsValid())
2160 {
2161 return Fail("%s: Could not read input 0", __func__);
2162 }
2163
2164 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2165 if (!output)
2166 {
2167 return Fail("%s: Could not read output 0", __func__);
2168 }
2169
2170 const TensorInfo& inputInfo = input.GetTensorInfo();
2171 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2172
Kevin May42477c12020-03-26 13:34:14 +00002173 ResizeDescriptor descriptor;
2174 descriptor.m_Method = resizeMethod;
2175 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
2176
2177 HalOperandType operandType1;
2178 HalOperandType operandType2;
2179
2180 if (!GetOperandType<HalPolicy>(operation, 1, model, operandType1) ||
2181 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
2182 {
2183 return Fail("%s: Operation has invalid inputs", __func__);
2184 }
2185
2186 if (operandType1 != operandType2)
2187 {
2188 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
2189 }
2190
2191 if (operandType1 == HalOperandType::INT32)
2192 {
2193 // Case 1: resizing by shape
2194 int32_t targetWidth = 0;
2195 int32_t targetHeight = 0;
2196
2197 if (!GetInputInt32<HalPolicy>(operation, 1, targetWidth, model, data) ||
2198 !GetInputInt32<HalPolicy>(operation, 2, targetHeight, model, data))
2199 {
2200 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
2201 }
2202
2203 if (targetWidth < 0 || targetHeight < 0)
2204 {
2205 return Fail("%s: Operation has invalid inputs for resizing by shape. "
2206 "Target width/height cannot be < 0", __func__);
2207 }
2208
2209 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
2210 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
2211 }
2212 else if (operandType1 == HalOperandType::FLOAT32)
2213 {
2214 // Case 2: resizing by scale
2215 float widthScale = 1.0f;
2216 float heightScale = 1.0f;
2217
2218 if (!GetInputFloat32<HalPolicy>(operation, 1, widthScale, model, data) ||
2219 !GetInputFloat32<HalPolicy>(operation, 2, heightScale, model, data))
2220 {
2221 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2222 }
2223
2224 const TensorShape& inputShape = inputInfo.GetShape();
2225 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2226
2227 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
2228 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
2229
2230 descriptor.m_TargetWidth = std::floor(width * widthScale);
2231 descriptor.m_TargetHeight = std::floor(height * heightScale);
2232 }
2233 else if (operandType1 == HalOperandType::FLOAT16)
2234 {
2235 Half widthScale;
2236 Half heightScale;
2237
2238 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, widthScale, model, data) ||
2239 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, heightScale, model, data))
2240 {
2241 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2242 }
2243
2244 const TensorShape& inputShape = inputInfo.GetShape();
2245 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2246
2247 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
2248 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
2249
2250 descriptor.m_TargetWidth = std::floor(width * widthScale);
2251 descriptor.m_TargetHeight = std::floor(height * heightScale);
2252 }
2253 else
2254 {
2255 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
2256 }
2257
David Monahanf057e6f2020-06-22 09:55:23 +01002258 descriptor.m_AlignCorners = GetOptionalBool<HalPolicy>(operation, 4, model, data);
2259 descriptor.m_HalfPixelCenters = GetOptionalBool<HalPolicy>(operation, 5, model, data);
David Monahan51e0b132020-04-20 16:12:06 +01002260
Kevin May42477c12020-03-26 13:34:14 +00002261 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002262 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002263 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2264 {
2265 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2266 IsResizeSupported,
2267 data.m_Backends,
2268 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002269 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002270 inputInfo,
2271 outputInfo,
2272 descriptor);
2273 };
2274
2275 if(IsDynamicTensor(outputInfo))
2276 {
2277 isSupported = AreDynamicTensorsSupported();
2278 }
2279 else
2280 {
2281 validateFunc(outputInfo, isSupported);
2282 }
Kevin May42477c12020-03-26 13:34:14 +00002283
2284 if (!isSupported)
2285 {
2286 return false;
2287 }
2288
2289 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Cathal Corbett8de96f72022-09-01 13:34:59 +01002290 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01002291 if (!layer)
2292 {
2293 return Fail("%s: Could not add the ResizeLayer", __func__);
2294 }
Kevin May42477c12020-03-26 13:34:14 +00002295 input.Connect(layer->GetInputSlot(0));
2296
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002297 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002298}
2299
2300template<typename HalPolicy,
2301 typename HalOperation = typename HalPolicy::Operation,
2302 typename HalModel = typename HalPolicy::Model>
2303bool ConvertSpaceToDepth(const HalOperation& operation, const HalModel& model, ConversionData& data)
2304{
2305 using HalOperand = typename HalPolicy::Operand;
2306 using HalOperandType = typename HalPolicy::OperandType;
2307
2308 ALOGV("HalPolicy::ConvertSpaceToDepth()");
2309
2310 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2311 if (!input.IsValid() )
2312 {
2313 return Fail("%s: Operation has invalid inputs", __func__);
2314 }
2315
2316 const TensorInfo& inputInfo = input.GetTensorInfo();
2317 unsigned int rank = inputInfo.GetNumDimensions();
2318 if (rank != 4)
2319 {
2320 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2321 }
2322
2323 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2324 if (!output)
2325 {
2326 return Fail("%s: Could not read output 0", __func__);
2327 }
2328
2329 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002330
2331 SpaceToDepthDescriptor desc;
2332
2333 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_BlockSize, model, data);
2334
2335 if (desc.m_BlockSize <= 1)
2336 {
Kevin May17de62e2023-07-31 12:16:04 +01002337 return Fail("%s: Block size must be at least 1 in all dimensions", __func__);
Kevin May42477c12020-03-26 13:34:14 +00002338 }
2339
2340 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
2341
2342 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002343 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002344 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2345 {
2346 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2347 IsSpaceToDepthSupported,
2348 data.m_Backends,
2349 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002350 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002351 inputInfo,
2352 outputInfo,
2353 desc);
2354 };
2355
2356 if(IsDynamicTensor(outputInfo))
2357 {
2358 isSupported = AreDynamicTensorsSupported();
2359 }
2360 else
2361 {
2362 validateFunc(outputInfo, isSupported);
2363 }
2364
Kevin May42477c12020-03-26 13:34:14 +00002365 if (!isSupported)
2366 {
2367 return false;
2368 }
2369
2370 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +01002371 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01002372 if (!layer)
2373 {
Mike Kelly1b46d132021-11-03 11:12:45 +00002374 return Fail("%s: Could not add the SpaceToDepthLayer", __func__);
Mike Kellye2d611e2021-10-14 12:35:58 +01002375 }
Kevin May42477c12020-03-26 13:34:14 +00002376 input.Connect(layer->GetInputSlot(0));
2377
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002378 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002379}
2380
2381template<typename HalPolicy,
2382 typename HalOperation = typename HalPolicy::Operation,
2383 typename HalModel = typename HalPolicy::Model>
2384bool ConvertSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
2385{
2386 using HalOperand = typename HalPolicy::Operand;
2387 using HalOperandType = typename HalPolicy::OperandType;
2388
2389 ALOGV("HalPolicy::ConvertSoftmax()");
2390
2391 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2392 if (!input.IsValid())
2393 {
2394 return Fail("%s: Operation has invalid inputs", __func__);
2395 }
2396
2397 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2398 if (!outputOperand)
2399 {
2400 return Fail("%s: Operation has no outputs", __func__);
2401 }
2402
2403 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00002404
2405 SoftmaxDescriptor desc;
Teresa Charlinbf866e22020-08-09 23:55:01 +01002406 HalOperandType outputType = outputOperand->type;
2407
2408 // Read beta value
2409 if (outputType == HalOperandType::TENSOR_FLOAT16)
Kevin May42477c12020-03-26 13:34:14 +00002410 {
Teresa Charlinbf866e22020-08-09 23:55:01 +01002411 Half value;
2412
2413 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, value, model, data))
2414 {
2415 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2416 }
2417
2418 desc.m_Beta = static_cast<float>(value);
2419 }
2420 else
2421 {
2422 if (!GetInputFloat32<HalPolicy>(operation, 1, desc.m_Beta, model, data))
2423 {
2424 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2425 }
Kevin May42477c12020-03-26 13:34:14 +00002426 }
2427
2428 if (operation.inputs.size() > 2 && !GetInputScalar<HalPolicy>(operation,
Teresa Charlinbf866e22020-08-09 23:55:01 +01002429 2,
2430 HalOperandType::INT32,
2431 desc.m_Axis,
2432 model,
2433 data))
Kevin May42477c12020-03-26 13:34:14 +00002434 {
2435 return Fail("%s: Operation has invalid inputs", __func__);
2436 }
2437
Kevin May42477c12020-03-26 13:34:14 +00002438 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002439 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002440 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2441 {
2442 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2443 IsSoftmaxSupported,
2444 data.m_Backends,
2445 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002446 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002447 input.GetTensorInfo(),
2448 outputInfo,
2449 desc);
2450 };
2451
2452 if(IsDynamicTensor(outputInfo))
2453 {
2454 isSupported = AreDynamicTensorsSupported();
2455 }
2456 else
2457 {
2458 validateFunc(outputInfo, isSupported);
2459 }
2460
Kevin May42477c12020-03-26 13:34:14 +00002461 if (!isSupported)
2462 {
2463 return false;
2464 }
2465
2466 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Cathal Corbett8de96f72022-09-01 13:34:59 +01002467 layer->SetBackendId(setBackend);
Mike Kellye2d611e2021-10-14 12:35:58 +01002468 if (!layer)
2469 {
2470 return Fail("%s: Could not add the SoftmaxLayer", __func__);
2471 }
Kevin May42477c12020-03-26 13:34:14 +00002472 input.Connect(layer->GetInputSlot(0));
2473
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002474 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002475}
2476
2477template<typename HalPolicy,
2478 typename HalOperation = typename HalPolicy::Operation,
2479 typename HalModel = typename HalPolicy::Model>
2480bool ConvertLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
2481{
2482 using HalOperand = typename HalPolicy::Operand;
2483 using HalOperandType = typename HalPolicy::OperandType;
2484
2485 ALOGV("HalPolicy::ConvertLstm()");
2486
2487 // Inputs:
2488 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2489 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2490 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2491 if (!input.IsValid())
2492 {
2493 return Fail("%s: Could not read input 0: input", __func__);
2494 }
2495 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2496 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
2497 if (!outputStateIn.IsValid())
2498 {
2499 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2500 }
2501 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2502 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
2503 if (!cellStateIn.IsValid())
2504 {
2505 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2506 }
2507
2508 // Get the mandatory input tensors:
2509 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2510 // [num_units, input_size].
2511 const ConstTensorPin inputToForgetWeightsPin =
2512 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
2513 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2514 // [num_units, input_size].
2515 const ConstTensorPin inputToCellWeightsPin =
2516 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
2517 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2518 // [num_units, input_size].
2519 const ConstTensorPin inputToOutputWeightsPin =
2520 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
2521 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2522 // [num_units, output_size].
2523 const ConstTensorPin recurrentToForgetWeightsPin =
2524 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
2525 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2526 // [num_units, output_size].
2527 const ConstTensorPin recurrentToCellWeightsPin =
2528 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
2529 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2530 // [num_units, output_size].
2531 const ConstTensorPin recurrentToOutputWeightsPin =
2532 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
2533 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2534 const ConstTensorPin forgetGateBiasPin =
2535 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
2536 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2537 const ConstTensorPin cellBiasPin =
2538 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
2539 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2540 const ConstTensorPin outputGateBiasPin =
2541 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
2542
2543 if (!inputToForgetWeightsPin.IsValid() ||
2544 !inputToCellWeightsPin.IsValid() ||
2545 !inputToOutputWeightsPin.IsValid() ||
2546 !recurrentToForgetWeightsPin.IsValid() ||
2547 !recurrentToCellWeightsPin.IsValid() ||
2548 !recurrentToOutputWeightsPin.IsValid() ||
2549 !forgetGateBiasPin.IsValid() ||
2550 !cellBiasPin.IsValid() ||
2551 !outputGateBiasPin.IsValid())
2552 {
2553 return Fail("%s: Operation has invalid tensor inputs", __func__);
2554 }
2555
2556 // Get the optional input tensors:
2557 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2558 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2559 const ConstTensorPin inputToInputWeightsPin =
2560 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
2561 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2562 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2563 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2564 const ConstTensorPin recurrentToInputWeightsPin =
2565 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
2566 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2567 const ConstTensorPin cellToInputWeightsPin =
2568 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
2569 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2570 const ConstTensorPin cellToForgetWeightsPin =
2571 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
2572 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2573 const ConstTensorPin cellToOutputWeightsPin =
2574 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
2575 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2576 const ConstTensorPin inputGateBiasPin =
2577 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2578 12,
2579 model,
2580 data,
2581 g_DontPermute,
2582 nullptr,
2583 true);
2584
2585 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2586 // [output_size, num_units].
2587 const ConstTensorPin projectionWeightsPin =
2588 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
2589 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2590 const ConstTensorPin projectionBiasPin =
2591 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2592 17,
2593 model,
2594 data,
2595 g_DontPermute,
2596 nullptr,
2597 true);
2598
2599 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2600 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2601 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2602 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2603 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2604 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2605 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2606 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2607 {
2608 return Fail("%s: Operation has invalid tensor inputs", __func__);
2609 }
2610
2611 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2612 // 20: The activation function: A value indicating the activation function:
2613 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2614 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2615 // If set to 0.0 then clipping is disabled.
2616 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2617 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
Cathal Corbett3fe3a542022-05-25 09:35:37 +01002618 ActivationFn activation = ActivationFn::kActivationNone;
Kevin May42477c12020-03-26 13:34:14 +00002619 float cellClip;
2620 float projClip;
2621 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
2622 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
2623 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
2624 {
2625 return Fail("%s: Operation has invalid scalar inputs", __func__);
2626 }
2627
2628 // Get the normalization tensors
2629 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2630 // Used to rescale normalized inputs to activation at input gate.
2631 const ConstTensorPin inputLayerNormWeightsPin
2632 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 23, true));
2633
2634 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2635 // Used to rescale normalized inputs to activation at forget gate.
2636 const ConstTensorPin forgetLayerNormWeightsPin =
2637 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2638 24,
2639 model,
2640 data,
2641 g_DontPermute,
2642 nullptr,
2643 true);
2644
2645 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2646 // Used to rescale normalized inputs to activation at cell gate.
2647 const ConstTensorPin cellLayerNormWeightsPin =
2648 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2649 25,
2650 model,
2651 data,
2652 g_DontPermute,
2653 nullptr,
2654 true);
2655
2656 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2657 // Used to rescale normalized inputs to activation at output gate.
2658 const ConstTensorPin outputLayerNormWeightsPin =
2659 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2660 26,
2661 model,
2662 data,
2663 g_DontPermute,
2664 nullptr,
2665 true);
2666
2667 // Outputs:
2668 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2669 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2670 const HalOperand* scratchBuffer = GetOutputOperand<HalPolicy>(operation, 0, model);
2671 if (!scratchBuffer)
2672 {
2673 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2674 }
2675 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2676 const HalOperand* outputStateOut = GetOutputOperand<HalPolicy>(operation, 1, model);
2677 if (!outputStateOut)
2678 {
2679 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2680 }
2681 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2682 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 2, model);
2683 if (!cellStateOut)
2684 {
2685 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2686 }
2687 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2688 // effectively the same as the current “output state (out)” value.
2689 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 3, model);
2690 if (!output)
2691 {
2692 return Fail("%s: Could not read output 3: output", __func__);
2693 }
2694
2695 // set the params structure for the AddLstmLayer call
2696 LstmInputParams params;
2697 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2698 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2699 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2700 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2701 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2702 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2703 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2704 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2705 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2706 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2707 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2708 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2709 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2710 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2711 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2712 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2713 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2714 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2715 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2716 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2717 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2718
2719 // set the layer descriptor
2720 LstmDescriptor desc;
2721 desc.m_ActivationFunc = activation;
2722 desc.m_ClippingThresCell = cellClip;
2723 desc.m_ClippingThresProj = projClip;
2724 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2725 params.m_RecurrentToInputWeights == nullptr ||
2726 params.m_InputGateBias == nullptr);
2727 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2728 params.m_CellToOutputWeights != nullptr);
2729 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2730 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2731 params.m_ForgetLayerNormWeights != nullptr ||
2732 params.m_CellLayerNormWeights != nullptr ||
2733 params.m_OutputLayerNormWeights != nullptr);
2734
2735 // validate the optional input groups
2736 if (desc.m_CifgEnabled &&
2737 (params.m_InputToInputWeights != nullptr ||
2738 params.m_RecurrentToInputWeights != nullptr ||
2739 params.m_InputGateBias != nullptr))
2740 {
2741 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2742 " and input gate bias must be provided", __func__);
2743 }
2744
2745 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2746 {
2747 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2748 }
2749
2750 if (desc.m_PeepholeEnabled &&
2751 (params.m_CellToForgetWeights == nullptr ||
2752 params.m_CellToOutputWeights == nullptr ||
2753 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2754 {
2755 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2756 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2757 }
2758
2759 if (desc.m_LayerNormEnabled &&
2760 (params.m_ForgetLayerNormWeights == nullptr ||
2761 params.m_CellLayerNormWeights == nullptr ||
2762 params.m_OutputLayerNormWeights == nullptr ||
2763 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2764 {
2765 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2766 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2767 }
2768
2769 // Check if the layer is supported
2770 // Inputs
2771 const TensorInfo& inputInfo = input.GetTensorInfo();
2772 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2773 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
2774
2775 // Outputs
2776 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2777 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2778 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2779 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2780
Kevin May42477c12020-03-26 13:34:14 +00002781 // Basic parameters
2782 LstmInputParamsInfo paramsInfo;
2783 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2784 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2785 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2786 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2787 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2788 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2789 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2790 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2791 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2792
2793 // Optional parameters
2794 if (!desc.m_CifgEnabled)
2795 {
2796 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2797 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2798 if (params.m_CellToInputWeights != nullptr)
2799 {
2800 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2801 }
2802 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2803 }
2804
2805 if (desc.m_ProjectionEnabled)
2806 {
2807 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2808 if (params.m_ProjectionBias != nullptr)
2809 {
2810 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2811 }
2812 }
2813
2814 if (desc.m_PeepholeEnabled)
2815 {
2816 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2817 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2818 }
2819
2820 if (desc.m_LayerNormEnabled)
2821 {
2822 if(!desc.m_CifgEnabled)
2823 {
2824 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2825 }
2826 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2827 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2828 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2829 }
2830
2831 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002832 armnn::BackendId setBackend;
Sadik Armagandbda4b72020-09-03 11:33:07 +01002833 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2834 {
2835 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2836 IsLstmSupported,
2837 data.m_Backends,
2838 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002839 setBackend,
Sadik Armagandbda4b72020-09-03 11:33:07 +01002840 inputInfo,
2841 outputStateInInfo,
2842 cellStateInInfo,
2843 scratchBufferInfo,
2844 outputStateOutInfo,
2845 cellStateOutInfo,
2846 outputInfo,
2847 desc,
2848 paramsInfo);
2849 };
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002850
Sadik Armagandbda4b72020-09-03 11:33:07 +01002851 bool isDynamic = false;
2852 if (!IsDynamicTensor(outputStateOutInfo) &&
2853 !IsDynamicTensor(scratchBufferInfo) &&
2854 !IsDynamicTensor(cellStateOutInfo) &&
2855 !IsDynamicTensor(outputInfo))
2856 {
2857 validateFunc(outputInfo, isSupported);
2858 }
2859 else
2860 {
2861 isDynamic = true;
2862 isSupported = AreDynamicTensorsSupported();
2863 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002864
Kevin May42477c12020-03-26 13:34:14 +00002865 if (!isSupported)
2866 {
2867 return false;
2868 }
2869
2870 // Add the layer
2871 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Cathal Corbett8de96f72022-09-01 13:34:59 +01002872 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00002873
2874 input.Connect(layer->GetInputSlot(0));
2875 outputStateIn.Connect(layer->GetInputSlot(1));
2876 cellStateIn.Connect(layer->GetInputSlot(2));
2877
Sadik Armagandbda4b72020-09-03 11:33:07 +01002878 if (!isDynamic)
2879 {
2880 return (
2881 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2882 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2883 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2884 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 3, *layer, 3, model, data));
2885 }
2886 else
2887 {
2888 return (
2889 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2890 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2891 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2892 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01002893 operation, 3, *layer, 3, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armagandbda4b72020-09-03 11:33:07 +01002894 }
2895
Kevin May42477c12020-03-26 13:34:14 +00002896}
2897
2898template<typename HalPolicy,
2899 typename HalOperation = typename HalPolicy::Operation,
2900 typename HalModel = typename HalPolicy::Model>
Teresa Charlin7f0ff162023-07-24 23:42:10 +01002901bool ConvertTile(const HalOperation& operation, const HalModel& model, ConversionData& data)
2902{
2903 using HalOperand = typename HalPolicy::Operand;
2904
2905 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2906
2907 if (!input.IsValid())
2908 {
2909 return Fail("%s: Operation has invalid inputs", __func__);
2910 }
2911
2912 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2913
2914 if (!output)
2915 {
2916 return Fail("%s: Could not read output", __func__);
2917 }
2918
2919 const HalOperand* multiplesOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2920 if (!multiplesOperand)
2921 {
2922 return Fail("%s: Could not read input 1", __func__);
2923 }
2924 std::vector<int32_t> multiples;
2925 if (!GetTensorInt32Values<HalPolicy>(*multiplesOperand, multiples, model, data))
2926 {
2927 return Fail("%s: Input 1 has invalid values", __func__);
2928 }
2929 // Convert the multiples from int to unsigned int,
2930 // as values are always going to be positive despite the data type being integer.
2931 TileDescriptor descriptor;
2932 descriptor.m_Multiples.assign(multiples.begin(), multiples.end());
2933
2934 const TensorInfo& inputInfo = input.GetTensorInfo();
2935 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2936
2937 bool isSupported = false;
2938 armnn::BackendId setBackend;
2939 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2940 {
2941 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2942 IsTileSupported,
2943 data.m_Backends,
2944 isSupported,
2945 setBackend,
2946 inputInfo,
2947 outputInfo,
2948 descriptor);
2949 };
2950
2951 if(IsDynamicTensor(outputInfo))
2952 {
2953 isSupported = AreDynamicTensorsSupported();
2954 }
2955 else
2956 {
2957 validateFunc(outputInfo, isSupported);
2958 }
2959 if (!isSupported)
2960 {
2961 return false;
2962 }
2963
2964 IConnectableLayer* tileLayer = data.m_Network->AddTileLayer(descriptor);
2965 if (!tileLayer)
2966 {
2967 return Fail("%s: AddTileLayer failed", __func__);
2968 }
2969 tileLayer->SetBackendId(setBackend);
2970
2971 input.Connect(tileLayer->GetInputSlot(0));
2972
2973 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *tileLayer, model, data, nullptr, validateFunc);
2974}
2975
2976template<typename HalPolicy,
2977 typename HalOperation = typename HalPolicy::Operation,
2978 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +00002979bool ConvertTransposeConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2980{
2981 using HalOperand = typename HalPolicy::Operand;
2982 using HalOperandType = typename HalPolicy::OperandType;
2983
2984 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2985
2986 if (!input.IsValid())
2987 {
2988 return Fail("%s: Operation has invalid inputs", __func__);
2989 }
2990
2991 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2992
2993 if (!output)
2994 {
2995 return Fail("%s: Could not read output 0", __func__);
2996 }
2997
2998 const TensorInfo& inputInfo = input.GetTensorInfo();
2999 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00003000
3001 // ArmNN does not currently support non-fixed weights or bias
3002 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
3003 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3004
3005 if (weightsOperand == nullptr)
3006 {
3007 return Fail("%s: Operand is invalid", __func__);
3008 }
3009 TransposeConvolution2dDescriptor desc;
3010 desc.m_DataLayout = DataLayout::NHWC;
3011
3012 // Determine whether padding is implicit or explicit
3013 bool implicitPadding = operation.inputs.size() == 9;
3014
3015 if (implicitPadding )
3016 {
3017 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
3018 }
3019 else
3020 {
3021 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
3022 }
3023
3024 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
3025 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
3026 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
3027
3028 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
3029
3030 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
3031 // We have to permute it to OIHW if the data layout is NCHW.
3032 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
3033 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
3034 model, data, OHWIToOIHW) :
3035 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
3036
3037 // Bias is a 1D tensor
3038 const ConstTensorPin biasPin =
3039 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
3040
3041 if (!weightsPin.IsValid())
3042 {
3043 return Fail("%s: Operation has invalid weights", __func__);
3044 }
3045
3046 if (!biasPin.IsValid())
3047 {
3048 return Fail("%s: Operation has invalid biases", __func__);
3049 }
3050
3051 ConstTensor weights = weightsPin.GetConstTensor();
3052 ConstTensor bias = biasPin.GetConstTensor();
3053 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
3054
3055 ActivationFn activation;
3056
3057 if (implicitPadding)
3058 {
3059 int32_t strideX{0};
3060 int32_t strideY{0};
3061 int32_t padLeft{0};
3062 int32_t padRight{0};
3063 int32_t padTop{0};
3064 int32_t padBottom{0};
3065
3066 android::nn::PaddingScheme paddingScheme;
3067 if (!GetInputPaddingScheme<HalPolicy>(operation, 4, paddingScheme, model, data) ||
3068 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, strideX, model, data) ||
3069 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, strideY, model, data) ||
3070 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
3071 {
3072 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
3073 }
3074
3075 const uint32_t kernelX = weights.GetShape()[widthIndex];
3076 const uint32_t kernelY = weights.GetShape()[heightIndex];
Kevin May42477c12020-03-26 13:34:14 +00003077
Colm Donelan8f3d33e2020-07-06 15:41:43 +01003078 // If output shape has been specified as a parameter then extract it and make it available.
3079 const HalOperand* outputShapeOperand = GetInputOperand<HalPolicy>(operation, 3, model, false);
3080 std::vector<int32_t> outputShape;
3081 if ((outputShapeOperand) && (GetTensorInt32Values<HalPolicy>(*outputShapeOperand, outputShape, model, data)))
3082 {
3083 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
3084 for (int dimension : outputShape)
3085 {
3086 desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
3087 }
3088 desc.m_OutputShapeEnabled = true;
3089 }
3090
Finn Williams8fe50c62020-10-09 15:52:57 +01003091 uint32_t outputX;
3092 uint32_t outputY;
3093
3094 if (IsDynamicTensor(outputInfo))
3095 {
3096 if (outputShape.size() == 0)
3097 {
3098 return Fail("%s: Padding sizes cannot be inferred", __func__);
3099 }
3100
3101 outputX = outputShape[widthIndex];
3102 outputY = outputShape[heightIndex];
3103 }
3104 else
3105 {
3106 outputX = outputInfo.GetShape()[widthIndex];
3107 outputY = outputInfo.GetShape()[heightIndex];
3108 }
3109
3110 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
3111 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
3112
3113 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
3114 // but Arm NN only supports values >= 0
3115 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
3116 {
3117 return Fail("%s: Negative padding values are not supported", __func__);
3118 }
3119
Matthew Sloyan9b088d92020-09-14 15:12:55 +01003120 desc.m_StrideX = armnn::numeric_cast<uint32_t>(strideX);
3121 desc.m_StrideY = armnn::numeric_cast<uint32_t>(strideY);
3122 desc.m_PadLeft = armnn::numeric_cast<uint32_t>(padLeft);
3123 desc.m_PadRight = armnn::numeric_cast<uint32_t>(padRight);
3124 desc.m_PadTop = armnn::numeric_cast<uint32_t>(padTop);
3125 desc.m_PadBottom = armnn::numeric_cast<uint32_t>(padBottom);
Kevin May42477c12020-03-26 13:34:14 +00003126 }
3127 else if (operation.inputs.size() == 11)
3128 {
3129 // explicit padding
3130 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
3131 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
3132 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
3133 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
3134 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
3135 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
3136 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
3137 {
3138 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
3139 }
3140 }
3141 else
3142 {
3143 return Fail("%s: Unsupported number of operation inputs", __func__);
3144 }
3145
3146 desc.m_BiasEnabled = true;
3147 Optional<TensorInfo> biases(bias.GetInfo());
3148
3149 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01003150 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01003151 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
3152 {
3153 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3154 IsTransposeConvolution2dSupported,
3155 data.m_Backends,
3156 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01003157 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01003158 inputInfo,
3159 outputInfo,
3160 desc,
3161 weights.GetInfo(),
3162 biases);
3163 };
3164
3165 if(IsDynamicTensor(outputInfo))
3166 {
3167 isSupported = AreDynamicTensorsSupported();
3168 }
3169 else
3170 {
3171 validateFunc(outputInfo, isSupported);
3172 }
Kevin May42477c12020-03-26 13:34:14 +00003173 if (!isSupported)
3174 {
3175 return false;
3176 }
3177
3178 IConnectableLayer* startLayer =
3179 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Cathal Corbett8de96f72022-09-01 13:34:59 +01003180 startLayer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00003181 if (!startLayer)
3182 {
3183 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
3184 }
3185
Kevin May42477c12020-03-26 13:34:14 +00003186 input.Connect(startLayer->GetInputSlot(0));
3187
Kevin Mayfcf2a152020-09-08 16:06:32 +01003188 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
3189 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +00003190}
3191
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003192template<typename HalPolicy,
3193 typename HalOperation = typename HalPolicy::Operation,
3194 typename HalModel = typename HalPolicy::Model>
3195bool ConvertUnidirectionalSequenceLstm(const HalOperation& operation,
3196 const HalModel& model,
3197 ConversionData& data)
3198{
3199 using HalOperand = typename HalPolicy::Operand;
3200 using HalOperandType = typename HalPolicy::OperandType;
3201
3202 ALOGV("HalPolicy::ConvertUnidirectionalSequenceLstm()");
3203
3204 // Determine if input OperandType is ANEURALNETWORKS_TENSOR_FLOAT 32 or 16
3205 HalOperandType inputType;
3206 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
3207 {
3208 return Fail("%s: Operation has invalid inputs", __func__);
3209 }
3210
3211 // Inputs:
3212 // 0: The input: A 3-D tensor of shape: If time-major: [max_time, batch_size, input_size] If batch-major:
3213 // [batch_size, max_time, input_size] where “max_time” is the number of timesteps (sequence length), “batch_size”
3214 // corresponds to the batching dimension, and “input_size” is the size of the input.
3215 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3216 if (!input.IsValid())
3217 {
3218 return Fail("%s: Could not read input 0: input", __func__);
3219 }
3220 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [batch_size, output_size].
3221 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
3222 if (!outputStateIn.IsValid())
3223 {
3224 return Fail("%s: Could not read input 18: outputStateIn", __func__);
3225 }
3226 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [batch_size, num_units].
3227 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
3228 if (!cellStateIn.IsValid())
3229 {
3230 return Fail("%s: Could not read input 19: cellStateIn", __func__);
3231 }
3232
3233 // Get the mandatory input tensors:
3234 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3235 // [num_units, input_size].
3236 const ConstTensorPin inputToForgetWeightsPin =
3237 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
3238 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3239 // [num_units, input_size].
3240 const ConstTensorPin inputToCellWeightsPin =
3241 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
3242 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3243 // [num_units, input_size].
3244 const ConstTensorPin inputToOutputWeightsPin =
3245 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
3246 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3247 // [num_units, output_size].
3248 const ConstTensorPin recurrentToForgetWeightsPin =
3249 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
3250 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
3251 // [num_units, output_size].
3252 const ConstTensorPin recurrentToCellWeightsPin =
3253 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
3254 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3255 // [num_units, output_size].
3256 const ConstTensorPin recurrentToOutputWeightsPin =
3257 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
3258 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3259 const ConstTensorPin forgetGateBiasPin =
3260 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
3261 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3262 const ConstTensorPin cellBiasPin =
3263 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
3264 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3265 const ConstTensorPin outputGateBiasPin =
3266 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
3267
3268 if (!inputToForgetWeightsPin.IsValid() ||
3269 !inputToCellWeightsPin.IsValid() ||
3270 !inputToOutputWeightsPin.IsValid() ||
3271 !recurrentToForgetWeightsPin.IsValid() ||
3272 !recurrentToCellWeightsPin.IsValid() ||
3273 !recurrentToOutputWeightsPin.IsValid() ||
3274 !forgetGateBiasPin.IsValid() ||
3275 !cellBiasPin.IsValid() ||
3276 !outputGateBiasPin.IsValid())
3277 {
3278 return Fail("%s: Operation has invalid tensor inputs", __func__);
3279 }
3280
3281 // Get the optional input tensors:
3282 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3283 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
3284 const ConstTensorPin inputToInputWeightsPin =
3285 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
3286 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3287 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
3288 // “num_units”), or the second dimension of the “projection_weights”, if defined.
3289 const ConstTensorPin recurrentToInputWeightsPin =
3290 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
3291 // 09: The cell-to-input weights: Optional.
3292 // A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3293 const ConstTensorPin cellToInputWeightsPin =
3294 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
3295 // 10: The cell-to-forget weights: Optional.
3296 // A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3297 const ConstTensorPin cellToForgetWeightsPin =
3298 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
3299 // 11: The cell-to-output weights: Optional.
3300 // A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3301 const ConstTensorPin cellToOutputWeightsPin =
3302 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
3303 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3304 const ConstTensorPin inputGateBiasPin =
3305 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3306 12,
3307 model,
3308 data,
3309 g_DontPermute,
3310 nullptr,
3311 true);
3312
3313 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3314 // [output_size, num_units].
3315 const ConstTensorPin projectionWeightsPin =
3316 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
3317 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [output_size].
3318 const ConstTensorPin projectionBiasPin =
3319 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3320 17,
3321 model,
3322 data,
3323 g_DontPermute,
3324 nullptr,
3325 true);
3326
3327 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
3328 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
3329 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
3330 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
3331 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
3332 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
3333 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
3334 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
3335 {
3336 return Fail("%s: Operation has invalid tensor inputs", __func__);
3337 }
3338
3339 // Get the mandatory input scalars (actually 1-D tensors of size 1):
3340 // 20: The activation function: A value indicating the activation function:
3341 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
3342 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
3343 // If set to 0.0 then clipping is disabled.
3344 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
3345 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
3346 // Determine data type of input tensor
Cathal Corbett3fe3a542022-05-25 09:35:37 +01003347 ActivationFn activation = ActivationFn::kActivationNone;
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003348 LstmDescriptor desc;
3349
3350 if (inputType == HalOperandType::TENSOR_FLOAT32)
3351 {
3352 float cellClip;
3353 float projClip;
3354
3355 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
3356 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
3357 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
3358 {
3359 return Fail("%s: Operation has invalid scalar inputs", __func__);
3360 }
3361
3362 desc.m_ClippingThresCell = cellClip;
3363 desc.m_ClippingThresProj = projClip;
3364 }
3365
3366 if (inputType == HalOperandType::TENSOR_FLOAT16)
3367 {
3368 Half cellClip;
3369 Half projClip;
3370
3371 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
3372 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT16, cellClip, model, data) ||
3373 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT16, projClip, model, data))
3374 {
3375 return Fail("%s: Operation has invalid scalar inputs", __func__);
3376 }
3377
3378 desc.m_ClippingThresCell = cellClip;
3379 desc.m_ClippingThresProj = projClip;
3380 }
3381
3382 // Determine if time-major or batch-major.
3383 // 23: Time-major if true, batch-major if false.
3384 bool isTimeMajor = GetOptionalBool<HalPolicy>(operation, 23, model, data);
3385
3386 // Get the normalization tensors
3387 // 24: The input layer normalization weights. A 1-D tensor of shape [num_units].
3388 // Used to rescale normalized inputs to activation at input gate.
3389 const ConstTensorPin inputLayerNormWeightsPin
3390 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 24, true));
3391
3392 // 25: The forget layer normalization weights. A 1-D tensor of shape [num_units].
3393 // Used to rescale normalized inputs to activation at forget gate.
3394 const ConstTensorPin forgetLayerNormWeightsPin =
3395 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3396 25,
3397 model,
3398 data,
3399 g_DontPermute,
3400 nullptr,
3401 true);
3402
3403 // 26: The cell layer normalization weights. A 1-D tensor of shape [num_units].
3404 // Used to rescale normalized inputs to activation at cell gate.
3405 const ConstTensorPin cellLayerNormWeightsPin =
3406 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3407 26,
3408 model,
3409 data,
3410 g_DontPermute,
3411 nullptr,
3412 true);
3413
3414 // 27: The output layer normalization weights. A 1-D tensor of shape [num_units].
3415 // Used to rescale normalized inputs to activation at output gate.
3416 const ConstTensorPin outputLayerNormWeightsPin =
3417 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3418 27,
3419 model,
3420 data,
3421 g_DontPermute,
3422 nullptr,
3423 true);
3424
3425 // Outputs:
3426 // 00: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16. Shape: if time-major:
3427 // [max_time, batch_size, output_size] If batch-major: [batch_size, max_time, output_size]
3428 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3429 if (!output)
3430 {
3431 return Fail("%s: Could not read output: ", __func__);
3432 }
3433
3434 //
3435 // 01 & 02:
3436 // hiddenStateOut and cellStateOut are not currently supported by our android versioning.
3437 //
3438
3439 // set the params structure for the AddLstmLayer call
3440 LstmInputParams params;
3441 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
3442 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
3443 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
3444 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
3445 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
3446 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
3447 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
3448 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
3449 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
3450 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
3451 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
3452 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
3453 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
3454 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
3455 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
3456 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
3457 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
3458 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
3459 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
3460 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
3461 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
3462
3463 // set the layer descriptor
3464 desc.m_ActivationFunc = activation;
3465 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
3466 params.m_RecurrentToInputWeights == nullptr ||
3467 params.m_InputGateBias == nullptr);
3468 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
3469 params.m_CellToOutputWeights != nullptr);
3470 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
3471 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
3472 params.m_ForgetLayerNormWeights != nullptr ||
3473 params.m_CellLayerNormWeights != nullptr ||
3474 params.m_OutputLayerNormWeights != nullptr);
3475 desc.m_TimeMajor = isTimeMajor;
3476
3477 // validate the optional input groups
3478 if (desc.m_CifgEnabled &&
3479 (params.m_InputToInputWeights != nullptr ||
3480 params.m_RecurrentToInputWeights != nullptr ||
3481 params.m_InputGateBias != nullptr))
3482 {
3483 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
3484 " and input gate bias must be provided", __func__);
3485 }
3486
3487 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
3488 {
3489 return Fail("%s: projection bias should not be provided without projection weights", __func__);
3490 }
3491
3492 if (desc.m_PeepholeEnabled &&
3493 (params.m_CellToForgetWeights == nullptr ||
3494 params.m_CellToOutputWeights == nullptr ||
3495 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
3496 {
3497 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
3498 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
3499 }
3500
3501 if (desc.m_LayerNormEnabled &&
3502 (params.m_ForgetLayerNormWeights == nullptr ||
3503 params.m_CellLayerNormWeights == nullptr ||
3504 params.m_OutputLayerNormWeights == nullptr ||
3505 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
3506 {
3507 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
3508 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
3509 }
3510
3511 // Check if the layer is supported
3512 // Inputs
3513 const TensorInfo& inputInfo = input.GetTensorInfo();
3514 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
3515 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
3516
3517 // Outputs
3518 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3519
Mike Kelly0ae102a2022-04-25 16:18:57 +01003520 unsigned int batchSize = inputInfo.GetShape()[0];
3521 unsigned int outputSize = outputInfo.GetShape()[2];
3522 unsigned int numUnits = cellStateInInfo.GetShape()[1];
3523
3524 armnn::DataType dataType = inputInfo.GetDataType();
3525 float qScale = inputInfo.GetQuantizationScale();
3526 int qOffset = inputInfo.GetQuantizationOffset();
3527
3528 armnn::TensorInfo cellStateOutInfo({batchSize, numUnits}, cellStateInInfo.GetDataType(),
3529 cellStateInInfo.GetQuantizationScale(), cellStateInInfo.GetQuantizationOffset());
3530 armnn::TensorInfo outputStateOutInfo({batchSize, outputSize}, dataType, qScale, qOffset);
3531
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003532 // Basic parameters
3533 LstmInputParamsInfo paramsInfo;
3534 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
3535 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
3536 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
3537 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
3538 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
3539 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
3540 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
3541 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
3542 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
3543
3544 // Optional parameters
3545 if (!desc.m_CifgEnabled)
3546 {
3547 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
3548 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
3549 if (params.m_CellToInputWeights != nullptr)
3550 {
3551 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
3552 }
3553 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
3554 }
3555
3556 if (desc.m_ProjectionEnabled)
3557 {
3558 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
3559 if (params.m_ProjectionBias != nullptr)
3560 {
3561 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
3562 }
3563 }
3564
3565 if (desc.m_PeepholeEnabled)
3566 {
3567 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
3568 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
3569 }
3570
3571 if (desc.m_LayerNormEnabled)
3572 {
3573 if(!desc.m_CifgEnabled)
3574 {
3575 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
3576 }
3577 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
3578 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
3579 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
3580 }
3581
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003582 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01003583 armnn::BackendId setBackend;
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003584 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
3585 {
3586 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3587 IsUnidirectionalSequenceLstmSupported,
3588 data.m_Backends,
3589 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01003590 setBackend,
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003591 inputInfo,
3592 outputStateInInfo,
3593 cellStateInInfo,
Mike Kelly0ae102a2022-04-25 16:18:57 +01003594 outputStateOutInfo,
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003595 cellStateOutInfo,
Mike Kelly0ae102a2022-04-25 16:18:57 +01003596 outputInfo,
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003597 desc,
3598 paramsInfo);
3599 };
3600
3601 bool isDynamic = false;
3602 if (!IsDynamicTensor(outputInfo))
3603 {
3604 validateFunc(outputInfo, isSupported);
3605 }
3606 else
3607 {
3608 isDynamic = true;
3609 isSupported = AreDynamicTensorsSupported();
3610 }
3611
3612 if (!isSupported)
3613 {
3614 return false;
3615 }
3616
3617 // Add the layer
3618 IConnectableLayer* layer = data.m_Network->AddUnidirectionalSequenceLstmLayer(desc,
3619 params,
3620 "UnidirectionalSequenceLstm");
Cathal Corbett8de96f72022-09-01 13:34:59 +01003621 layer->SetBackendId(setBackend);
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003622
3623 input.Connect(layer->GetInputSlot(0));
3624 outputStateIn.Connect(layer->GetInputSlot(1));
3625 cellStateIn.Connect(layer->GetInputSlot(2));
3626
3627 if (!isDynamic)
3628 {
Mike Kelly0ae102a2022-04-25 16:18:57 +01003629 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 2, model, data));
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003630 }
3631 else
3632 {
Mike Kelly0ae102a2022-04-25 16:18:57 +01003633 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 2, model, data, nullptr,
3634 validateFunc, ActivationFn::kActivationNone, true));
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003635 }
3636}
3637
Kevin May42477c12020-03-26 13:34:14 +00003638} // armnn_driver namespace