blob: 00c891ae821a26b2a33434186820e2f98826e0dd [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,
Mike Kellyd261ffe2023-08-04 13:17:23 +01002478 typename Operation = typename HalPolicy::Operation,
2479 typename Model = typename HalPolicy::Model>
2480bool ConvertSplit(const Operation& operation, const Model& model, ConversionData& data)
2481{
2482 using HalOperand = typename HalPolicy::Operand;
2483 using HalOperandType = typename HalPolicy::OperandType;
2484
2485 if (operation.inputs.size() != 3)
2486 {
2487 return Fail("%s: Optional inputs are not supported expected 3 was %i", __func__, operation.inputs.size());
2488 }
2489
2490 // 0: An n-D tensor to split.
2491 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2492 // 1: An ANEURALNETWORKS_INT32 scalar specifying the axis along which to split.
2493 int32_t axis = 0;
2494 // 2: An ANEURALNETWORKS_INT32 scalar indicating the number of splits along given axis.
2495 // Must evenly divide axis size.
2496 int32_t numSplits = 0;
2497
2498 if (!input.IsValid() ||
2499 !GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data) ||
2500 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, numSplits, model, data))
2501 {
2502 return Fail("%s: Operation has invalid inputs", __func__);
2503 }
2504
2505 // If number of splits is <= zero, return false.
2506 if (numSplits <= 0)
2507 {
2508 return Fail("%s: Number of splits must be greater than zero", __func__);
2509 }
2510 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2511 unsigned int inputDimSize = inputInfo.GetNumDimensions();
2512 int32_t inputDimensions = static_cast<int32_t>(inputDimSize);
2513
2514 if (axis < -inputDimensions || axis >= inputDimensions)
2515 {
2516 // The axis for a tensor with n dimensions must be between -n and n-1
2517 // E.g. Rank 4 tensor can have axis in range [-4, 3)
2518 // -1 == 3, -2 == 2, -3 == 1, -4 == 0
2519 return Fail("%s: Operation has invalid axis %i. Axis must be in range [-n, n-1]", __func__, axis);
2520 }
2521 auto splitDim = armnnUtils::GetUnsignedAxis(inputDimSize, axis);
2522
2523 if (inputDimSize > MaxNumOfTensorDimensions)
2524 {
2525 return Fail("%s: The number of dimensions %i for split operation cannot be greater than %i",
2526 __func__, inputInfo.GetNumDimensions(), MaxNumOfTensorDimensions);
2527 }
2528 std::vector<uint32_t> splitterDimSizes(inputDimSize);
2529
2530 // Add current input shape to splitterDimSizes
2531 for (uint32_t i = 0; i < inputDimSize; ++i)
2532 {
2533 splitterDimSizes[i] = inputInfo.GetShape()[i];
2534 }
2535
2536 if (splitterDimSizes[splitDim] % numSplits != 0)
2537 {
2538 return Fail("%s: The number of splits %i must evenly divide the dimension %i",
2539 __func__, numSplits, splitterDimSizes[splitDim]);
2540 }
2541 splitterDimSizes[splitDim] /= numSplits;
2542
2543 ViewsDescriptor descriptor(numSplits, inputDimSize);
2544
2545 for (int32_t i = 0; i < numSplits; ++i)
2546 {
2547 // Set the size of the views.
2548 for (uint32_t dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
2549 {
2550 descriptor.SetViewSize(i, dimIdx, splitterDimSizes[dimIdx]);
2551 }
2552 descriptor.SetViewOriginCoord(i, splitDim, splitterDimSizes[splitDim] * i);
2553 }
2554
2555 std::vector<TensorInfo> outputInfos;
2556 for (int32_t i = 0; i < numSplits; ++i)
2557 {
2558 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, i, model);
2559 if (!output)
2560 {
2561 return Fail("%s: Could not read output %i", __func__, i);
2562 }
2563
2564 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2565 outputInfos.template emplace_back(outputInfo);
2566 }
2567 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(outputInfos.begin(), outputInfos.end());
2568 bool isSupported = false;
2569 armnn::BackendId setBackend;
2570
2571 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2572 IsSplitterSupported,
2573 data.m_Backends,
2574 isSupported,
2575 setBackend,
2576 inputInfo,
2577 splitterOutputInfos,
2578 descriptor);
2579
2580 if (!isSupported)
2581 {
2582 return Fail("%s: Layer is not supported", __func__);
2583 }
2584
2585 for (int32_t i = 0; i < numSplits; ++i)
2586 {
2587 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, i, model);
2588 if (!output)
2589 {
2590 return Fail("%s: Could not read output %i", __func__, i);
2591 }
2592
2593 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2594 if (IsDynamicTensor(outputInfo))
2595 {
2596 return Fail("%s: Dynamic output tensors are not supported", __func__);
2597 }
2598 }
2599
2600 IConnectableLayer* layer = data.m_Network->AddSplitterLayer(descriptor, "Split");
2601 if (!layer)
2602 {
2603 return Fail("%s: could not add the Layer", __func__);
2604 }
2605 input.Connect(layer->GetInputSlot(0));
2606
2607 auto validateFunc = [&](const armnn::TensorInfo&, bool& isSupported)
2608 {
2609 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2610 IsSplitterSupported,
2611 data.m_Backends,
2612 isSupported,
2613 setBackend,
2614 inputInfo,
2615 splitterOutputInfos,
2616 descriptor);
2617 };
2618
2619 for (int32_t i = 0; i < numSplits; ++i)
2620 {
2621 bool ok = SetupAndTrackLayerOutputSlot<HalPolicy>(operation, i, *layer, model, data, nullptr, validateFunc);
2622
2623 if (!ok)
2624 {
2625 return false;
2626 }
2627 }
2628 return true;
2629}
2630
2631template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +00002632 typename HalOperation = typename HalPolicy::Operation,
2633 typename HalModel = typename HalPolicy::Model>
2634bool ConvertLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
2635{
2636 using HalOperand = typename HalPolicy::Operand;
2637 using HalOperandType = typename HalPolicy::OperandType;
2638
2639 ALOGV("HalPolicy::ConvertLstm()");
2640
2641 // Inputs:
2642 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2643 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2644 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2645 if (!input.IsValid())
2646 {
2647 return Fail("%s: Could not read input 0: input", __func__);
2648 }
2649 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2650 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
2651 if (!outputStateIn.IsValid())
2652 {
2653 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2654 }
2655 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2656 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
2657 if (!cellStateIn.IsValid())
2658 {
2659 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2660 }
2661
2662 // Get the mandatory input tensors:
2663 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2664 // [num_units, input_size].
2665 const ConstTensorPin inputToForgetWeightsPin =
2666 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
2667 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2668 // [num_units, input_size].
2669 const ConstTensorPin inputToCellWeightsPin =
2670 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
2671 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2672 // [num_units, input_size].
2673 const ConstTensorPin inputToOutputWeightsPin =
2674 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
2675 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2676 // [num_units, output_size].
2677 const ConstTensorPin recurrentToForgetWeightsPin =
2678 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
2679 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2680 // [num_units, output_size].
2681 const ConstTensorPin recurrentToCellWeightsPin =
2682 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
2683 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2684 // [num_units, output_size].
2685 const ConstTensorPin recurrentToOutputWeightsPin =
2686 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
2687 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2688 const ConstTensorPin forgetGateBiasPin =
2689 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
2690 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2691 const ConstTensorPin cellBiasPin =
2692 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
2693 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2694 const ConstTensorPin outputGateBiasPin =
2695 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
2696
2697 if (!inputToForgetWeightsPin.IsValid() ||
2698 !inputToCellWeightsPin.IsValid() ||
2699 !inputToOutputWeightsPin.IsValid() ||
2700 !recurrentToForgetWeightsPin.IsValid() ||
2701 !recurrentToCellWeightsPin.IsValid() ||
2702 !recurrentToOutputWeightsPin.IsValid() ||
2703 !forgetGateBiasPin.IsValid() ||
2704 !cellBiasPin.IsValid() ||
2705 !outputGateBiasPin.IsValid())
2706 {
2707 return Fail("%s: Operation has invalid tensor inputs", __func__);
2708 }
2709
2710 // Get the optional input tensors:
2711 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2712 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2713 const ConstTensorPin inputToInputWeightsPin =
2714 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
2715 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2716 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2717 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2718 const ConstTensorPin recurrentToInputWeightsPin =
2719 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
2720 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2721 const ConstTensorPin cellToInputWeightsPin =
2722 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
2723 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2724 const ConstTensorPin cellToForgetWeightsPin =
2725 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
2726 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2727 const ConstTensorPin cellToOutputWeightsPin =
2728 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
2729 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2730 const ConstTensorPin inputGateBiasPin =
2731 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2732 12,
2733 model,
2734 data,
2735 g_DontPermute,
2736 nullptr,
2737 true);
2738
2739 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2740 // [output_size, num_units].
2741 const ConstTensorPin projectionWeightsPin =
2742 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
2743 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2744 const ConstTensorPin projectionBiasPin =
2745 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2746 17,
2747 model,
2748 data,
2749 g_DontPermute,
2750 nullptr,
2751 true);
2752
2753 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2754 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2755 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2756 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2757 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2758 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2759 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2760 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2761 {
2762 return Fail("%s: Operation has invalid tensor inputs", __func__);
2763 }
2764
2765 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2766 // 20: The activation function: A value indicating the activation function:
2767 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2768 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2769 // If set to 0.0 then clipping is disabled.
2770 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2771 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
Cathal Corbett3fe3a542022-05-25 09:35:37 +01002772 ActivationFn activation = ActivationFn::kActivationNone;
Kevin May42477c12020-03-26 13:34:14 +00002773 float cellClip;
2774 float projClip;
2775 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
2776 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
2777 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
2778 {
2779 return Fail("%s: Operation has invalid scalar inputs", __func__);
2780 }
2781
2782 // Get the normalization tensors
2783 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2784 // Used to rescale normalized inputs to activation at input gate.
2785 const ConstTensorPin inputLayerNormWeightsPin
2786 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 23, true));
2787
2788 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2789 // Used to rescale normalized inputs to activation at forget gate.
2790 const ConstTensorPin forgetLayerNormWeightsPin =
2791 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2792 24,
2793 model,
2794 data,
2795 g_DontPermute,
2796 nullptr,
2797 true);
2798
2799 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2800 // Used to rescale normalized inputs to activation at cell gate.
2801 const ConstTensorPin cellLayerNormWeightsPin =
2802 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2803 25,
2804 model,
2805 data,
2806 g_DontPermute,
2807 nullptr,
2808 true);
2809
2810 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2811 // Used to rescale normalized inputs to activation at output gate.
2812 const ConstTensorPin outputLayerNormWeightsPin =
2813 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2814 26,
2815 model,
2816 data,
2817 g_DontPermute,
2818 nullptr,
2819 true);
2820
2821 // Outputs:
2822 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2823 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2824 const HalOperand* scratchBuffer = GetOutputOperand<HalPolicy>(operation, 0, model);
2825 if (!scratchBuffer)
2826 {
2827 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2828 }
2829 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2830 const HalOperand* outputStateOut = GetOutputOperand<HalPolicy>(operation, 1, model);
2831 if (!outputStateOut)
2832 {
2833 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2834 }
2835 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2836 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 2, model);
2837 if (!cellStateOut)
2838 {
2839 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2840 }
2841 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2842 // effectively the same as the current “output state (out)” value.
2843 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 3, model);
2844 if (!output)
2845 {
2846 return Fail("%s: Could not read output 3: output", __func__);
2847 }
2848
2849 // set the params structure for the AddLstmLayer call
2850 LstmInputParams params;
2851 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2852 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2853 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2854 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2855 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2856 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2857 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2858 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2859 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2860 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2861 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2862 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2863 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2864 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2865 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2866 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2867 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2868 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2869 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2870 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2871 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2872
2873 // set the layer descriptor
2874 LstmDescriptor desc;
2875 desc.m_ActivationFunc = activation;
2876 desc.m_ClippingThresCell = cellClip;
2877 desc.m_ClippingThresProj = projClip;
2878 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2879 params.m_RecurrentToInputWeights == nullptr ||
2880 params.m_InputGateBias == nullptr);
2881 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2882 params.m_CellToOutputWeights != nullptr);
2883 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2884 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2885 params.m_ForgetLayerNormWeights != nullptr ||
2886 params.m_CellLayerNormWeights != nullptr ||
2887 params.m_OutputLayerNormWeights != nullptr);
2888
2889 // validate the optional input groups
2890 if (desc.m_CifgEnabled &&
2891 (params.m_InputToInputWeights != nullptr ||
2892 params.m_RecurrentToInputWeights != nullptr ||
2893 params.m_InputGateBias != nullptr))
2894 {
2895 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2896 " and input gate bias must be provided", __func__);
2897 }
2898
2899 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2900 {
2901 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2902 }
2903
2904 if (desc.m_PeepholeEnabled &&
2905 (params.m_CellToForgetWeights == nullptr ||
2906 params.m_CellToOutputWeights == nullptr ||
2907 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2908 {
2909 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2910 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2911 }
2912
2913 if (desc.m_LayerNormEnabled &&
2914 (params.m_ForgetLayerNormWeights == nullptr ||
2915 params.m_CellLayerNormWeights == nullptr ||
2916 params.m_OutputLayerNormWeights == nullptr ||
2917 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2918 {
2919 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2920 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2921 }
2922
2923 // Check if the layer is supported
2924 // Inputs
2925 const TensorInfo& inputInfo = input.GetTensorInfo();
2926 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2927 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
2928
2929 // Outputs
2930 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2931 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2932 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2933 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2934
Kevin May42477c12020-03-26 13:34:14 +00002935 // Basic parameters
2936 LstmInputParamsInfo paramsInfo;
2937 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2938 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2939 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2940 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2941 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2942 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2943 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2944 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2945 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2946
2947 // Optional parameters
2948 if (!desc.m_CifgEnabled)
2949 {
2950 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2951 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2952 if (params.m_CellToInputWeights != nullptr)
2953 {
2954 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2955 }
2956 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2957 }
2958
2959 if (desc.m_ProjectionEnabled)
2960 {
2961 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2962 if (params.m_ProjectionBias != nullptr)
2963 {
2964 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2965 }
2966 }
2967
2968 if (desc.m_PeepholeEnabled)
2969 {
2970 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2971 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2972 }
2973
2974 if (desc.m_LayerNormEnabled)
2975 {
2976 if(!desc.m_CifgEnabled)
2977 {
2978 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2979 }
2980 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2981 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2982 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2983 }
2984
2985 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01002986 armnn::BackendId setBackend;
Sadik Armagandbda4b72020-09-03 11:33:07 +01002987 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2988 {
2989 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2990 IsLstmSupported,
2991 data.m_Backends,
2992 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01002993 setBackend,
Sadik Armagandbda4b72020-09-03 11:33:07 +01002994 inputInfo,
2995 outputStateInInfo,
2996 cellStateInInfo,
2997 scratchBufferInfo,
2998 outputStateOutInfo,
2999 cellStateOutInfo,
3000 outputInfo,
3001 desc,
3002 paramsInfo);
3003 };
Teresa Charlin4bd9a742020-08-12 12:58:50 +01003004
Sadik Armagandbda4b72020-09-03 11:33:07 +01003005 bool isDynamic = false;
3006 if (!IsDynamicTensor(outputStateOutInfo) &&
3007 !IsDynamicTensor(scratchBufferInfo) &&
3008 !IsDynamicTensor(cellStateOutInfo) &&
3009 !IsDynamicTensor(outputInfo))
3010 {
3011 validateFunc(outputInfo, isSupported);
3012 }
3013 else
3014 {
3015 isDynamic = true;
3016 isSupported = AreDynamicTensorsSupported();
3017 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +01003018
Kevin May42477c12020-03-26 13:34:14 +00003019 if (!isSupported)
3020 {
3021 return false;
3022 }
3023
3024 // Add the layer
3025 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
Cathal Corbett8de96f72022-09-01 13:34:59 +01003026 layer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00003027
3028 input.Connect(layer->GetInputSlot(0));
3029 outputStateIn.Connect(layer->GetInputSlot(1));
3030 cellStateIn.Connect(layer->GetInputSlot(2));
3031
Sadik Armagandbda4b72020-09-03 11:33:07 +01003032 if (!isDynamic)
3033 {
3034 return (
3035 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
3036 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
3037 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
3038 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 3, *layer, 3, model, data));
3039 }
3040 else
3041 {
3042 return (
3043 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
3044 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
3045 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
3046 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01003047 operation, 3, *layer, 3, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armagandbda4b72020-09-03 11:33:07 +01003048 }
3049
Kevin May42477c12020-03-26 13:34:14 +00003050}
3051
3052template<typename HalPolicy,
3053 typename HalOperation = typename HalPolicy::Operation,
3054 typename HalModel = typename HalPolicy::Model>
Teresa Charlin7f0ff162023-07-24 23:42:10 +01003055bool ConvertTile(const HalOperation& operation, const HalModel& model, ConversionData& data)
3056{
3057 using HalOperand = typename HalPolicy::Operand;
3058
3059 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3060
3061 if (!input.IsValid())
3062 {
3063 return Fail("%s: Operation has invalid inputs", __func__);
3064 }
3065
3066 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3067
3068 if (!output)
3069 {
3070 return Fail("%s: Could not read output", __func__);
3071 }
3072
3073 const HalOperand* multiplesOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3074 if (!multiplesOperand)
3075 {
3076 return Fail("%s: Could not read input 1", __func__);
3077 }
3078 std::vector<int32_t> multiples;
3079 if (!GetTensorInt32Values<HalPolicy>(*multiplesOperand, multiples, model, data))
3080 {
3081 return Fail("%s: Input 1 has invalid values", __func__);
3082 }
3083 // Convert the multiples from int to unsigned int,
3084 // as values are always going to be positive despite the data type being integer.
3085 TileDescriptor descriptor;
3086 descriptor.m_Multiples.assign(multiples.begin(), multiples.end());
3087
3088 const TensorInfo& inputInfo = input.GetTensorInfo();
3089 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3090
3091 bool isSupported = false;
3092 armnn::BackendId setBackend;
3093 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
3094 {
3095 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3096 IsTileSupported,
3097 data.m_Backends,
3098 isSupported,
3099 setBackend,
3100 inputInfo,
3101 outputInfo,
3102 descriptor);
3103 };
3104
3105 if(IsDynamicTensor(outputInfo))
3106 {
3107 isSupported = AreDynamicTensorsSupported();
3108 }
3109 else
3110 {
3111 validateFunc(outputInfo, isSupported);
3112 }
3113 if (!isSupported)
3114 {
3115 return false;
3116 }
3117
3118 IConnectableLayer* tileLayer = data.m_Network->AddTileLayer(descriptor);
3119 if (!tileLayer)
3120 {
3121 return Fail("%s: AddTileLayer failed", __func__);
3122 }
3123 tileLayer->SetBackendId(setBackend);
3124
3125 input.Connect(tileLayer->GetInputSlot(0));
3126
3127 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *tileLayer, model, data, nullptr, validateFunc);
3128}
3129
3130template<typename HalPolicy,
3131 typename HalOperation = typename HalPolicy::Operation,
3132 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +00003133bool ConvertTransposeConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
3134{
3135 using HalOperand = typename HalPolicy::Operand;
3136 using HalOperandType = typename HalPolicy::OperandType;
3137
3138 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3139
3140 if (!input.IsValid())
3141 {
3142 return Fail("%s: Operation has invalid inputs", __func__);
3143 }
3144
3145 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3146
3147 if (!output)
3148 {
3149 return Fail("%s: Could not read output 0", __func__);
3150 }
3151
3152 const TensorInfo& inputInfo = input.GetTensorInfo();
3153 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00003154
3155 // ArmNN does not currently support non-fixed weights or bias
3156 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
3157 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
3158
3159 if (weightsOperand == nullptr)
3160 {
3161 return Fail("%s: Operand is invalid", __func__);
3162 }
3163 TransposeConvolution2dDescriptor desc;
3164 desc.m_DataLayout = DataLayout::NHWC;
3165
3166 // Determine whether padding is implicit or explicit
3167 bool implicitPadding = operation.inputs.size() == 9;
3168
3169 if (implicitPadding )
3170 {
3171 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
3172 }
3173 else
3174 {
3175 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
3176 }
3177
3178 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
3179 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
3180 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
3181
3182 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
3183
3184 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
3185 // We have to permute it to OIHW if the data layout is NCHW.
3186 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
3187 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
3188 model, data, OHWIToOIHW) :
3189 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
3190
3191 // Bias is a 1D tensor
3192 const ConstTensorPin biasPin =
3193 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
3194
3195 if (!weightsPin.IsValid())
3196 {
3197 return Fail("%s: Operation has invalid weights", __func__);
3198 }
3199
3200 if (!biasPin.IsValid())
3201 {
3202 return Fail("%s: Operation has invalid biases", __func__);
3203 }
3204
3205 ConstTensor weights = weightsPin.GetConstTensor();
3206 ConstTensor bias = biasPin.GetConstTensor();
3207 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
3208
3209 ActivationFn activation;
3210
3211 if (implicitPadding)
3212 {
3213 int32_t strideX{0};
3214 int32_t strideY{0};
3215 int32_t padLeft{0};
3216 int32_t padRight{0};
3217 int32_t padTop{0};
3218 int32_t padBottom{0};
3219
3220 android::nn::PaddingScheme paddingScheme;
3221 if (!GetInputPaddingScheme<HalPolicy>(operation, 4, paddingScheme, model, data) ||
3222 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, strideX, model, data) ||
3223 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, strideY, model, data) ||
3224 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
3225 {
3226 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
3227 }
3228
3229 const uint32_t kernelX = weights.GetShape()[widthIndex];
3230 const uint32_t kernelY = weights.GetShape()[heightIndex];
Kevin May42477c12020-03-26 13:34:14 +00003231
Colm Donelan8f3d33e2020-07-06 15:41:43 +01003232 // If output shape has been specified as a parameter then extract it and make it available.
3233 const HalOperand* outputShapeOperand = GetInputOperand<HalPolicy>(operation, 3, model, false);
3234 std::vector<int32_t> outputShape;
3235 if ((outputShapeOperand) && (GetTensorInt32Values<HalPolicy>(*outputShapeOperand, outputShape, model, data)))
3236 {
3237 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
3238 for (int dimension : outputShape)
3239 {
3240 desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
3241 }
3242 desc.m_OutputShapeEnabled = true;
3243 }
3244
Finn Williams8fe50c62020-10-09 15:52:57 +01003245 uint32_t outputX;
3246 uint32_t outputY;
3247
3248 if (IsDynamicTensor(outputInfo))
3249 {
3250 if (outputShape.size() == 0)
3251 {
3252 return Fail("%s: Padding sizes cannot be inferred", __func__);
3253 }
3254
3255 outputX = outputShape[widthIndex];
3256 outputY = outputShape[heightIndex];
3257 }
3258 else
3259 {
3260 outputX = outputInfo.GetShape()[widthIndex];
3261 outputY = outputInfo.GetShape()[heightIndex];
3262 }
3263
3264 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
3265 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
3266
3267 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
3268 // but Arm NN only supports values >= 0
3269 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
3270 {
3271 return Fail("%s: Negative padding values are not supported", __func__);
3272 }
3273
Matthew Sloyan9b088d92020-09-14 15:12:55 +01003274 desc.m_StrideX = armnn::numeric_cast<uint32_t>(strideX);
3275 desc.m_StrideY = armnn::numeric_cast<uint32_t>(strideY);
3276 desc.m_PadLeft = armnn::numeric_cast<uint32_t>(padLeft);
3277 desc.m_PadRight = armnn::numeric_cast<uint32_t>(padRight);
3278 desc.m_PadTop = armnn::numeric_cast<uint32_t>(padTop);
3279 desc.m_PadBottom = armnn::numeric_cast<uint32_t>(padBottom);
Kevin May42477c12020-03-26 13:34:14 +00003280 }
3281 else if (operation.inputs.size() == 11)
3282 {
3283 // explicit padding
3284 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
3285 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
3286 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
3287 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
3288 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
3289 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
3290 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
3291 {
3292 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
3293 }
3294 }
3295 else
3296 {
3297 return Fail("%s: Unsupported number of operation inputs", __func__);
3298 }
3299
3300 desc.m_BiasEnabled = true;
3301 Optional<TensorInfo> biases(bias.GetInfo());
3302
3303 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01003304 armnn::BackendId setBackend;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01003305 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
3306 {
3307 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3308 IsTransposeConvolution2dSupported,
3309 data.m_Backends,
3310 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01003311 setBackend,
Teresa Charlin4bd9a742020-08-12 12:58:50 +01003312 inputInfo,
3313 outputInfo,
3314 desc,
3315 weights.GetInfo(),
3316 biases);
3317 };
3318
3319 if(IsDynamicTensor(outputInfo))
3320 {
3321 isSupported = AreDynamicTensorsSupported();
3322 }
3323 else
3324 {
3325 validateFunc(outputInfo, isSupported);
3326 }
Kevin May42477c12020-03-26 13:34:14 +00003327 if (!isSupported)
3328 {
3329 return false;
3330 }
3331
3332 IConnectableLayer* startLayer =
3333 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
Cathal Corbett8de96f72022-09-01 13:34:59 +01003334 startLayer->SetBackendId(setBackend);
Kevin May42477c12020-03-26 13:34:14 +00003335 if (!startLayer)
3336 {
3337 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
3338 }
3339
Kevin May42477c12020-03-26 13:34:14 +00003340 input.Connect(startLayer->GetInputSlot(0));
3341
Kevin Mayfcf2a152020-09-08 16:06:32 +01003342 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
3343 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +00003344}
3345
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003346template<typename HalPolicy,
3347 typename HalOperation = typename HalPolicy::Operation,
3348 typename HalModel = typename HalPolicy::Model>
3349bool ConvertUnidirectionalSequenceLstm(const HalOperation& operation,
3350 const HalModel& model,
3351 ConversionData& data)
3352{
3353 using HalOperand = typename HalPolicy::Operand;
3354 using HalOperandType = typename HalPolicy::OperandType;
3355
3356 ALOGV("HalPolicy::ConvertUnidirectionalSequenceLstm()");
3357
3358 // Determine if input OperandType is ANEURALNETWORKS_TENSOR_FLOAT 32 or 16
3359 HalOperandType inputType;
3360 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
3361 {
3362 return Fail("%s: Operation has invalid inputs", __func__);
3363 }
3364
3365 // Inputs:
3366 // 0: The input: A 3-D tensor of shape: If time-major: [max_time, batch_size, input_size] If batch-major:
3367 // [batch_size, max_time, input_size] where “max_time” is the number of timesteps (sequence length), “batch_size”
3368 // corresponds to the batching dimension, and “input_size” is the size of the input.
3369 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3370 if (!input.IsValid())
3371 {
3372 return Fail("%s: Could not read input 0: input", __func__);
3373 }
3374 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [batch_size, output_size].
3375 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
3376 if (!outputStateIn.IsValid())
3377 {
3378 return Fail("%s: Could not read input 18: outputStateIn", __func__);
3379 }
3380 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [batch_size, num_units].
3381 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
3382 if (!cellStateIn.IsValid())
3383 {
3384 return Fail("%s: Could not read input 19: cellStateIn", __func__);
3385 }
3386
3387 // Get the mandatory input tensors:
3388 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3389 // [num_units, input_size].
3390 const ConstTensorPin inputToForgetWeightsPin =
3391 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
3392 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3393 // [num_units, input_size].
3394 const ConstTensorPin inputToCellWeightsPin =
3395 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
3396 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3397 // [num_units, input_size].
3398 const ConstTensorPin inputToOutputWeightsPin =
3399 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
3400 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3401 // [num_units, output_size].
3402 const ConstTensorPin recurrentToForgetWeightsPin =
3403 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
3404 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
3405 // [num_units, output_size].
3406 const ConstTensorPin recurrentToCellWeightsPin =
3407 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
3408 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3409 // [num_units, output_size].
3410 const ConstTensorPin recurrentToOutputWeightsPin =
3411 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
3412 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3413 const ConstTensorPin forgetGateBiasPin =
3414 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
3415 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3416 const ConstTensorPin cellBiasPin =
3417 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
3418 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3419 const ConstTensorPin outputGateBiasPin =
3420 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
3421
3422 if (!inputToForgetWeightsPin.IsValid() ||
3423 !inputToCellWeightsPin.IsValid() ||
3424 !inputToOutputWeightsPin.IsValid() ||
3425 !recurrentToForgetWeightsPin.IsValid() ||
3426 !recurrentToCellWeightsPin.IsValid() ||
3427 !recurrentToOutputWeightsPin.IsValid() ||
3428 !forgetGateBiasPin.IsValid() ||
3429 !cellBiasPin.IsValid() ||
3430 !outputGateBiasPin.IsValid())
3431 {
3432 return Fail("%s: Operation has invalid tensor inputs", __func__);
3433 }
3434
3435 // Get the optional input tensors:
3436 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3437 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
3438 const ConstTensorPin inputToInputWeightsPin =
3439 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
3440 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3441 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
3442 // “num_units”), or the second dimension of the “projection_weights”, if defined.
3443 const ConstTensorPin recurrentToInputWeightsPin =
3444 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
3445 // 09: The cell-to-input weights: Optional.
3446 // A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3447 const ConstTensorPin cellToInputWeightsPin =
3448 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
3449 // 10: The cell-to-forget weights: Optional.
3450 // A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3451 const ConstTensorPin cellToForgetWeightsPin =
3452 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
3453 // 11: The cell-to-output weights: Optional.
3454 // A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3455 const ConstTensorPin cellToOutputWeightsPin =
3456 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
3457 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3458 const ConstTensorPin inputGateBiasPin =
3459 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3460 12,
3461 model,
3462 data,
3463 g_DontPermute,
3464 nullptr,
3465 true);
3466
3467 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3468 // [output_size, num_units].
3469 const ConstTensorPin projectionWeightsPin =
3470 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
3471 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [output_size].
3472 const ConstTensorPin projectionBiasPin =
3473 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3474 17,
3475 model,
3476 data,
3477 g_DontPermute,
3478 nullptr,
3479 true);
3480
3481 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
3482 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
3483 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
3484 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
3485 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
3486 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
3487 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
3488 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
3489 {
3490 return Fail("%s: Operation has invalid tensor inputs", __func__);
3491 }
3492
3493 // Get the mandatory input scalars (actually 1-D tensors of size 1):
3494 // 20: The activation function: A value indicating the activation function:
3495 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
3496 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
3497 // If set to 0.0 then clipping is disabled.
3498 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
3499 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
3500 // Determine data type of input tensor
Cathal Corbett3fe3a542022-05-25 09:35:37 +01003501 ActivationFn activation = ActivationFn::kActivationNone;
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003502 LstmDescriptor desc;
3503
3504 if (inputType == HalOperandType::TENSOR_FLOAT32)
3505 {
3506 float cellClip;
3507 float projClip;
3508
3509 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
3510 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
3511 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
3512 {
3513 return Fail("%s: Operation has invalid scalar inputs", __func__);
3514 }
3515
3516 desc.m_ClippingThresCell = cellClip;
3517 desc.m_ClippingThresProj = projClip;
3518 }
3519
3520 if (inputType == HalOperandType::TENSOR_FLOAT16)
3521 {
3522 Half cellClip;
3523 Half projClip;
3524
3525 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
3526 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT16, cellClip, model, data) ||
3527 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT16, projClip, model, data))
3528 {
3529 return Fail("%s: Operation has invalid scalar inputs", __func__);
3530 }
3531
3532 desc.m_ClippingThresCell = cellClip;
3533 desc.m_ClippingThresProj = projClip;
3534 }
3535
3536 // Determine if time-major or batch-major.
3537 // 23: Time-major if true, batch-major if false.
3538 bool isTimeMajor = GetOptionalBool<HalPolicy>(operation, 23, model, data);
3539
3540 // Get the normalization tensors
3541 // 24: The input layer normalization weights. A 1-D tensor of shape [num_units].
3542 // Used to rescale normalized inputs to activation at input gate.
3543 const ConstTensorPin inputLayerNormWeightsPin
3544 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 24, true));
3545
3546 // 25: The forget layer normalization weights. A 1-D tensor of shape [num_units].
3547 // Used to rescale normalized inputs to activation at forget gate.
3548 const ConstTensorPin forgetLayerNormWeightsPin =
3549 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3550 25,
3551 model,
3552 data,
3553 g_DontPermute,
3554 nullptr,
3555 true);
3556
3557 // 26: The cell layer normalization weights. A 1-D tensor of shape [num_units].
3558 // Used to rescale normalized inputs to activation at cell gate.
3559 const ConstTensorPin cellLayerNormWeightsPin =
3560 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3561 26,
3562 model,
3563 data,
3564 g_DontPermute,
3565 nullptr,
3566 true);
3567
3568 // 27: The output layer normalization weights. A 1-D tensor of shape [num_units].
3569 // Used to rescale normalized inputs to activation at output gate.
3570 const ConstTensorPin outputLayerNormWeightsPin =
3571 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3572 27,
3573 model,
3574 data,
3575 g_DontPermute,
3576 nullptr,
3577 true);
3578
3579 // Outputs:
3580 // 00: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16. Shape: if time-major:
3581 // [max_time, batch_size, output_size] If batch-major: [batch_size, max_time, output_size]
3582 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3583 if (!output)
3584 {
3585 return Fail("%s: Could not read output: ", __func__);
3586 }
3587
3588 //
3589 // 01 & 02:
3590 // hiddenStateOut and cellStateOut are not currently supported by our android versioning.
3591 //
3592
3593 // set the params structure for the AddLstmLayer call
3594 LstmInputParams params;
3595 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
3596 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
3597 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
3598 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
3599 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
3600 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
3601 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
3602 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
3603 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
3604 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
3605 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
3606 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
3607 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
3608 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
3609 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
3610 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
3611 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
3612 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
3613 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
3614 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
3615 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
3616
3617 // set the layer descriptor
3618 desc.m_ActivationFunc = activation;
3619 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
3620 params.m_RecurrentToInputWeights == nullptr ||
3621 params.m_InputGateBias == nullptr);
3622 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
3623 params.m_CellToOutputWeights != nullptr);
3624 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
3625 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
3626 params.m_ForgetLayerNormWeights != nullptr ||
3627 params.m_CellLayerNormWeights != nullptr ||
3628 params.m_OutputLayerNormWeights != nullptr);
3629 desc.m_TimeMajor = isTimeMajor;
3630
3631 // validate the optional input groups
3632 if (desc.m_CifgEnabled &&
3633 (params.m_InputToInputWeights != nullptr ||
3634 params.m_RecurrentToInputWeights != nullptr ||
3635 params.m_InputGateBias != nullptr))
3636 {
3637 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
3638 " and input gate bias must be provided", __func__);
3639 }
3640
3641 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
3642 {
3643 return Fail("%s: projection bias should not be provided without projection weights", __func__);
3644 }
3645
3646 if (desc.m_PeepholeEnabled &&
3647 (params.m_CellToForgetWeights == nullptr ||
3648 params.m_CellToOutputWeights == nullptr ||
3649 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
3650 {
3651 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
3652 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
3653 }
3654
3655 if (desc.m_LayerNormEnabled &&
3656 (params.m_ForgetLayerNormWeights == nullptr ||
3657 params.m_CellLayerNormWeights == nullptr ||
3658 params.m_OutputLayerNormWeights == nullptr ||
3659 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
3660 {
3661 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
3662 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
3663 }
3664
3665 // Check if the layer is supported
3666 // Inputs
3667 const TensorInfo& inputInfo = input.GetTensorInfo();
3668 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
3669 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
3670
3671 // Outputs
3672 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3673
Mike Kelly0ae102a2022-04-25 16:18:57 +01003674 unsigned int batchSize = inputInfo.GetShape()[0];
3675 unsigned int outputSize = outputInfo.GetShape()[2];
3676 unsigned int numUnits = cellStateInInfo.GetShape()[1];
3677
3678 armnn::DataType dataType = inputInfo.GetDataType();
3679 float qScale = inputInfo.GetQuantizationScale();
3680 int qOffset = inputInfo.GetQuantizationOffset();
3681
3682 armnn::TensorInfo cellStateOutInfo({batchSize, numUnits}, cellStateInInfo.GetDataType(),
3683 cellStateInInfo.GetQuantizationScale(), cellStateInInfo.GetQuantizationOffset());
3684 armnn::TensorInfo outputStateOutInfo({batchSize, outputSize}, dataType, qScale, qOffset);
3685
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003686 // Basic parameters
3687 LstmInputParamsInfo paramsInfo;
3688 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
3689 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
3690 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
3691 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
3692 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
3693 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
3694 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
3695 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
3696 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
3697
3698 // Optional parameters
3699 if (!desc.m_CifgEnabled)
3700 {
3701 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
3702 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
3703 if (params.m_CellToInputWeights != nullptr)
3704 {
3705 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
3706 }
3707 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
3708 }
3709
3710 if (desc.m_ProjectionEnabled)
3711 {
3712 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
3713 if (params.m_ProjectionBias != nullptr)
3714 {
3715 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
3716 }
3717 }
3718
3719 if (desc.m_PeepholeEnabled)
3720 {
3721 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
3722 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
3723 }
3724
3725 if (desc.m_LayerNormEnabled)
3726 {
3727 if(!desc.m_CifgEnabled)
3728 {
3729 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
3730 }
3731 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
3732 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
3733 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
3734 }
3735
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003736 bool isSupported = false;
Cathal Corbett8de96f72022-09-01 13:34:59 +01003737 armnn::BackendId setBackend;
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003738 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
3739 {
3740 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3741 IsUnidirectionalSequenceLstmSupported,
3742 data.m_Backends,
3743 isSupported,
Cathal Corbett8de96f72022-09-01 13:34:59 +01003744 setBackend,
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003745 inputInfo,
3746 outputStateInInfo,
3747 cellStateInInfo,
Mike Kelly0ae102a2022-04-25 16:18:57 +01003748 outputStateOutInfo,
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003749 cellStateOutInfo,
Mike Kelly0ae102a2022-04-25 16:18:57 +01003750 outputInfo,
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003751 desc,
3752 paramsInfo);
3753 };
3754
3755 bool isDynamic = false;
3756 if (!IsDynamicTensor(outputInfo))
3757 {
3758 validateFunc(outputInfo, isSupported);
3759 }
3760 else
3761 {
3762 isDynamic = true;
3763 isSupported = AreDynamicTensorsSupported();
3764 }
3765
3766 if (!isSupported)
3767 {
3768 return false;
3769 }
3770
3771 // Add the layer
3772 IConnectableLayer* layer = data.m_Network->AddUnidirectionalSequenceLstmLayer(desc,
3773 params,
3774 "UnidirectionalSequenceLstm");
Cathal Corbett8de96f72022-09-01 13:34:59 +01003775 layer->SetBackendId(setBackend);
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003776
3777 input.Connect(layer->GetInputSlot(0));
3778 outputStateIn.Connect(layer->GetInputSlot(1));
3779 cellStateIn.Connect(layer->GetInputSlot(2));
3780
3781 if (!isDynamic)
3782 {
Mike Kelly0ae102a2022-04-25 16:18:57 +01003783 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 2, model, data));
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003784 }
3785 else
3786 {
Mike Kelly0ae102a2022-04-25 16:18:57 +01003787 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 2, model, data, nullptr,
3788 validateFunc, ActivationFn::kActivationNone, true));
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003789 }
3790}
3791
Kevin May42477c12020-03-26 13:34:14 +00003792} // armnn_driver namespace