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