blob: 171d61bdadd398d8c1067346d30b7daa703ce9ef [file] [log] [blame]
Kevin May42477c12020-03-26 13:34:14 +00001//
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002// Copyright © 2020 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;
165
166 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
167 {
168 FORWARD_LAYER_SUPPORT_FUNC(__func__,
169 IsCastSupported,
170 data.m_Backends,
171 isSupported,
172 inputInfo,
173 outputInfo);
174 };
175
176 if(!IsDynamicTensor(outputInfo))
177 {
178 validateFunc(outputInfo, isSupported);
179 }
180 else
181 {
182 isSupported = AreDynamicTensorsSupported();
183 }
184
185 if (!isSupported)
186 {
187 return false;
188 }
189
190 IConnectableLayer* layer = data.m_Network->AddCastLayer();
Mike Kellye2d611e2021-10-14 12:35:58 +0100191 if (!layer)
192 {
193 return Fail("%s: Could not add the CastLayer", __func__);
194 }
Sadik Armagan92b5fd12021-04-26 09:52:06 +0100195 input.Connect(layer->GetInputSlot(0));
196
197 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
198}
199
200template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +0000201 typename HalOperation = typename HalPolicy::Operation,
202 typename HalModel = typename HalPolicy::Model>
Teresa Charlin7f5b51e2021-09-01 14:19:38 +0100203bool ConvertChannelShuffle(const HalOperation& operation,
204 const HalModel& model,
205 ConversionData& data)
206{
207 using HalOperand = typename HalPolicy::Operand;
208 using HalOperandType = typename HalPolicy::OperandType;
209
210 ALOGV("HalPolicy::ConvertChannelShuffle()");
211
212 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
213 if (!input.IsValid())
214 {
215 return Fail("%s: Operation has invalid inputs", __func__);
216 }
217 auto inputDimensions = static_cast<int32_t>(input.GetTensorInfo().GetNumDimensions());
218
219 ChannelShuffleDescriptor descriptor;
220
221 int32_t groups;
222 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, groups, model, data))
223 {
224 return Fail("%s: Operation has invalid or unsupported number of groups operand", __func__);
225 }
226 descriptor.m_NumGroups = static_cast<uint32_t>(groups);
227
228 int32_t axis;
229 if (!GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, axis, model, data))
230 {
231 return Fail("%s: Operation has invalid or unsupported dimension channel shuffle operand", __func__);
232 }
233 if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
234 {
235 return Fail("%s: Operation has invalid dimension: %d. It is out of bounds [-%d, %d))", __func__, axis,
236 inputDimensions, inputDimensions);
237 }
238 int positiveAxis = (axis < 0) ? inputDimensions + axis : axis;
239 descriptor.m_Axis = static_cast<uint32_t>(positiveAxis);
240
241 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
242 if (!output)
243 {
244 return Fail("%s: Could not read output 0", __func__);
245 }
246
247 const TensorInfo& inputInfo = input.GetTensorInfo();
248 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
249
250 bool isSupported = false;
251
252 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
253 {
254 FORWARD_LAYER_SUPPORT_FUNC(__func__,
255 IsChannelShuffleSupported,
256 data.m_Backends,
257 isSupported,
258 inputInfo,
259 outputInfo,
260 descriptor);
261 };
262
263 if(!IsDynamicTensor(outputInfo))
264 {
265 validateFunc(outputInfo, isSupported);
266 }
267 else
268 {
269 isSupported = AreDynamicTensorsSupported();
270 }
271
272 if (!isSupported)
273 {
274 return false;
275 }
276
277 IConnectableLayer* layer = data.m_Network->AddChannelShuffleLayer(descriptor);
278 assert(layer != nullptr);
279 input.Connect(layer->GetInputSlot(0));
280
281 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
282}
283
284template<typename HalPolicy,
285 typename HalOperation = typename HalPolicy::Operation,
286 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +0000287bool ConvertComparison_1_2(const HalOperation& operation,
288 const HalModel& model,
289 ConversionData& data,
290 ComparisonOperation comparisonOperation)
291{
292 using HalOperand = typename HalPolicy::Operand;
293
294 ALOGV("HalPolicy::ConvertComparison()");
295 ALOGV("comparisonOperation = %s", GetComparisonOperationAsCString(comparisonOperation));
296
297 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
298 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
299
300 if (!(input0.IsValid() && input1.IsValid()))
301 {
302 return Fail("%s: Operation has invalid inputs", __func__);
303 }
304
305 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
306 if (!output)
307 {
308 return Fail("%s: Could not read output 0", __func__);
309 }
310
311 const TensorInfo& inputInfo0 = input0.GetTensorInfo();
312 const TensorInfo& inputInfo1 = input1.GetTensorInfo();
313 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
314
Kevin May42477c12020-03-26 13:34:14 +0000315 ComparisonDescriptor descriptor(comparisonOperation);
316
317 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100318 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
319 {
320 FORWARD_LAYER_SUPPORT_FUNC(__func__,
321 IsComparisonSupported,
322 data.m_Backends,
323 isSupported,
324 inputInfo0,
325 inputInfo1,
326 outputInfo,
327 descriptor);
328
329 };
330
331 if(!IsDynamicTensor(outputInfo))
332 {
333 validateFunc(outputInfo, isSupported);
334 }
335 else
336 {
337 isSupported = AreDynamicTensorsSupported();
338 }
Kevin May42477c12020-03-26 13:34:14 +0000339
340 if (!isSupported)
341 {
342 return false;
343 }
344
345 IConnectableLayer* layer = data.m_Network->AddComparisonLayer(descriptor);
Mike Kellye2d611e2021-10-14 12:35:58 +0100346 if (!layer)
347 {
348 return Fail("%s: Could not add the ComparisonLayer", __func__);
349 }
Kevin May42477c12020-03-26 13:34:14 +0000350
351 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
352 if (!isReshapeSupported)
353 {
354 return false;
355 }
356
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100357 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000358}
359
360template<typename HalPolicy,
361 typename HalOperation = typename HalPolicy::Operation,
362 typename HalModel = typename HalPolicy::Model>
363bool ConvertConv2d_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
364{
365
366 using HalOperand = typename HalPolicy::Operand;
367 using HalOperandType = typename HalPolicy::OperandType;
368
369 ALOGV("HalPolicy::ConvertConv2d_1_2()");
370
371 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
372 if (!input.IsValid())
373 {
374 return Fail("%s: Operation has invalid inputs", __func__);
375 }
376
377 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
378 if (!output)
379 {
380 return Fail("%s: Could not read output 0", __func__);
381 }
382
383 const TensorInfo& inputInfo = input.GetTensorInfo();
384 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
385
Kevin May42477c12020-03-26 13:34:14 +0000386 Convolution2dDescriptor desc;
387 desc.m_DataLayout = DataLayout::NHWC;
388
389 // Determine whether padding is implicit or explicit
390 bool implicitPadding = operation.inputs.size() == 7 ||
391 (operation.inputs.size() >= 8 &&
392 GetInputOperand<HalPolicy>(operation, 7, model)->type == HalOperandType::BOOL);
393
394 if (implicitPadding)
395 {
396 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 7, model, data);
397 }
398 else if (operation.inputs.size() >= 10)
399 {
400 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
401 }
402
403 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
404
405 // ArmNN does not currently support non-fixed weights or bias
406 // The NNAPI filter is always OHWI [depth_out, filter_height, filter_width, depth_in] but ArmNN expects the
407 // filter's height and width indices to match the input's height and width indices so we permute it to OIHW if
408 // the DataLayout is NCHW
Kevin May42477c12020-03-26 13:34:14 +0000409
Keith Davis8f22bed2022-04-29 10:57:27 +0100410
411 if (!IsWeightsValid<HalPolicy>(operation, 1, model) && desc.m_DataLayout == DataLayout::NCHW)
Kevin May42477c12020-03-26 13:34:14 +0000412 {
Keith Davis8f22bed2022-04-29 10:57:27 +0100413 return Fail("%s: Operation has unsupported weights HalOperandLifeTime", __func__);
Kevin May42477c12020-03-26 13:34:14 +0000414 }
415
Keith Davis8f22bed2022-04-29 10:57:27 +0100416 LayerInputHandle weightsInput = (desc.m_DataLayout == DataLayout::NCHW) ?
417 ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data, OHWIToOIHW) :
418 ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
419
420 if (!weightsInput.IsValid())
Kevin May42477c12020-03-26 13:34:14 +0000421 {
Keith Davis8f22bed2022-04-29 10:57:27 +0100422 return Fail("%s: Operation has invalid inputs", __func__);
Kevin May42477c12020-03-26 13:34:14 +0000423 }
424
Keith Davis8f22bed2022-04-29 10:57:27 +0100425 LayerInputHandle biasInput = ConvertToLayerInputHandle<HalPolicy>(operation, 2, model, data); // 1D
426 if (!biasInput.IsValid())
427 {
428 return Fail("%s: Operation has invalid inputs", __func__);
429 }
430
431 biasInput.SanitizeQuantizationScale(weightsInput, input);
432 armnn::TensorInfo weightsInfo = weightsInput.GetTensorInfo();
433 armnn::TensorInfo biasInfo = biasInput.GetTensorInfo();
Kevin May42477c12020-03-26 13:34:14 +0000434
435 ActivationFn activation;
436
437 if (implicitPadding)
438 {
439 android::nn::PaddingScheme paddingScheme;
440 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
441 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
442 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
443 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data) ||
444 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 8, desc, model, data))
445 {
446 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
447 }
448
449 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
450 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
451 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
Keith Davis8f22bed2022-04-29 10:57:27 +0100452 const uint32_t kernelX = weightsInfo.GetShape()[widthIndex];
453 const uint32_t kernelY = weightsInfo.GetShape()[heightIndex];
Kevin May42477c12020-03-26 13:34:14 +0000454 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
455 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
456
457 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
458 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
459
460 }
461 else if (operation.inputs.size() >= 10)
462 {
463 // explicit padding
464 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
465 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
466 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
467 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
468 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
469 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
470 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data) ||
471 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 11, desc, model, data))
472 {
473 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
474 }
475 }
476 else
477 {
478 return Fail("%s: Unsupported number of operation inputs", __func__);
479 }
480
481 desc.m_BiasEnabled = true;
Keith Davis8f22bed2022-04-29 10:57:27 +0100482 Optional<TensorInfo> biases(biasInfo);
Kevin May42477c12020-03-26 13:34:14 +0000483
484 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100485 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
486 {
487 FORWARD_LAYER_SUPPORT_FUNC(__func__,
488 IsConvolution2dSupported,
489 data.m_Backends,
490 isSupported,
491 inputInfo,
492 outputInfo,
493 desc,
Keith Davis8f22bed2022-04-29 10:57:27 +0100494 weightsInfo,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100495 biases);
496 };
497
498 if(!IsDynamicTensor(outputInfo))
499 {
500 validateFunc(outputInfo, isSupported);
501 }
502 else
503 {
504 isSupported = AreDynamicTensorsSupported();
505 }
Kevin May42477c12020-03-26 13:34:14 +0000506
507 if (!isSupported)
508 {
509 return false;
510 }
511
Keith Davis8f22bed2022-04-29 10:57:27 +0100512 armnn::IConnectableLayer* startLayer = data.m_Network->AddConvolution2dLayer(desc);
Kevin May42477c12020-03-26 13:34:14 +0000513
514 if (!startLayer)
515 {
516 return Fail("%s: AddConvolution2dLayer failed", __func__);
517 }
518
Kevin May42477c12020-03-26 13:34:14 +0000519 input.Connect(startLayer->GetInputSlot(0));
Keith Davis8f22bed2022-04-29 10:57:27 +0100520 weightsInput.Connect(startLayer->GetInputSlot(1));
521 biasInput.Connect(startLayer->GetInputSlot(2));
Kevin May42477c12020-03-26 13:34:14 +0000522
Kevin Mayfcf2a152020-09-08 16:06:32 +0100523 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
524 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +0000525}
526
527template<typename HalPolicy,
528 typename HalOperation = typename HalPolicy::Operation,
529 typename HalModel = typename HalPolicy::Model>
530bool ConvertDepthwiseConv2d_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
531{
532 using HalOperand = typename HalPolicy::Operand;
533 using HalOperandType = typename HalPolicy::OperandType;
534
535 ALOGV("HalPolicy::ConvertDepthwiseConv2d_1_2()");
536
537 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
538
539 if (!input.IsValid())
540 {
541 return Fail("%s: Operation has invalid inputs", __func__);
542 }
543
544 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
545
546 if (!output)
547 {
548 return Fail("%s: Could not read output 0", __func__);
549 }
550
551 const TensorInfo& inputInfo = input.GetTensorInfo();
552 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
553
Kevin May42477c12020-03-26 13:34:14 +0000554 // ArmNN does not currently support non-fixed weights or bias
555 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
556 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
Cathal Corbett915f2a72022-04-15 14:12:08 +0100557 if (!weightsOperand)
Kevin May42477c12020-03-26 13:34:14 +0000558 {
Cathal Corbett915f2a72022-04-15 14:12:08 +0100559 return Fail("%s: Could not read weights", __func__);
Kevin May42477c12020-03-26 13:34:14 +0000560 }
Cathal Corbett915f2a72022-04-15 14:12:08 +0100561 if (weightsOperand->dimensions[0] != 1)
Kevin May42477c12020-03-26 13:34:14 +0000562 {
563 return Fail("%s: Invalid weights; for depthwise convolution, dimension 0 must be 1 but it is %i",
564 __func__, weightsOperand->dimensions[0] );
565 }
566
567 DepthwiseConvolution2dDescriptor desc;
568 desc.m_DataLayout = DataLayout::NHWC;
569
570 // Determine whether padding is implicit or explicit
571 bool implicitPadding = operation.inputs.size() == 8 ||
572 (operation.inputs.size() >= 9 &&
573 GetInputOperand<HalPolicy>(operation, 8, model)->type == HalOperandType::BOOL);
574
575 // Look ahead to find the optional DataLayout, if present
576 const uint32_t dataLayoutFlagIndex = implicitPadding ? 8 : 11;
577 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, dataLayoutFlagIndex, model, data);
578
579 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
Kevin May42477c12020-03-26 13:34:14 +0000580 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
581 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
582
Cathal Corbett915f2a72022-04-15 14:12:08 +0100583 LayerInputHandle weightsInput = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
584 if (!weightsInput.IsValid())
Kevin May42477c12020-03-26 13:34:14 +0000585 {
Cathal Corbett915f2a72022-04-15 14:12:08 +0100586 return Fail("%s: Operation has invalid inputs", __func__);
Kevin May42477c12020-03-26 13:34:14 +0000587 }
588
Cathal Corbett915f2a72022-04-15 14:12:08 +0100589 const HalOperand* biasOperand = GetInputOperand<HalPolicy>(operation, 2, model);
590 if (!biasOperand)
Kevin May42477c12020-03-26 13:34:14 +0000591 {
Cathal Corbett915f2a72022-04-15 14:12:08 +0100592 return Fail("%s: Could not read bias", __func__);
Kevin May42477c12020-03-26 13:34:14 +0000593 }
594
Cathal Corbett915f2a72022-04-15 14:12:08 +0100595 LayerInputHandle biasInput = ConvertToLayerInputHandle<HalPolicy>(operation, 2, model, data); // 1D
596 if (!biasInput.IsValid())
597 {
598 return Fail("%s: Operation has invalid inputs", __func__);
599 }
600
601 biasInput.SanitizeQuantizationScale(weightsInput, input);
602 armnn::TensorInfo weightsInfo = weightsInput.GetTensorInfo();
603 armnn::TensorInfo biasInfo = biasInput.GetTensorInfo();
Kevin May42477c12020-03-26 13:34:14 +0000604
605 ActivationFn activation;
606
607 if (implicitPadding)
608 {
609 android::nn::PaddingScheme paddingScheme;
610 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
611 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
612 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
613 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data) ||
614 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 9, desc, model, data))
615 {
616 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
617 }
618
Cathal Corbett915f2a72022-04-15 14:12:08 +0100619 const uint32_t kernelX = weightsInfo.GetShape()[2];
620 const uint32_t kernelY = weightsInfo.GetShape()[1];
Kevin May42477c12020-03-26 13:34:14 +0000621 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
622 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
623
624 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
625 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
626 }
627 else if (operation.inputs.size() >= 11)
628 {
629 // explicit padding
630 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
631 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
632 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
633 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
634 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
635 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
636 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data) ||
637 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 12, desc, model, data))
638 {
639 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
640 }
641 }
642 else
643 {
644 return Fail("%s: Unsupported number of operation inputs", __func__);
645 }
646
647 desc.m_BiasEnabled = true;
Cathal Corbett915f2a72022-04-15 14:12:08 +0100648 Optional<TensorInfo> biases(biasInfo);
Kevin May42477c12020-03-26 13:34:14 +0000649
650 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100651 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
652 {
653 FORWARD_LAYER_SUPPORT_FUNC(__func__,
654 IsDepthwiseConvolutionSupported,
655 data.m_Backends,
656 isSupported,
657 inputInfo,
658 outputInfo,
659 desc,
Cathal Corbett915f2a72022-04-15 14:12:08 +0100660 weightsInfo,
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100661 biases);
662 };
663
664 if(!IsDynamicTensor(outputInfo))
665 {
666 validateFunc(outputInfo, isSupported);
667 }
668 else
669 {
670 isSupported = AreDynamicTensorsSupported();
671 }
Kevin May42477c12020-03-26 13:34:14 +0000672
673 if (!isSupported)
674 {
675 return false;
676 }
677
Cathal Corbett915f2a72022-04-15 14:12:08 +0100678 armnn::IConnectableLayer* startLayer = data.m_Network->AddDepthwiseConvolution2dLayer(desc);
Kevin May42477c12020-03-26 13:34:14 +0000679
680 if (!startLayer)
681 {
682 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
683 }
684
Kevin May42477c12020-03-26 13:34:14 +0000685 input.Connect(startLayer->GetInputSlot(0));
686
Cathal Corbett915f2a72022-04-15 14:12:08 +0100687 // Connect weights and bias inputs
688 weightsInput.Connect(startLayer->GetInputSlot(1));
689 biasInput.Connect(startLayer->GetInputSlot(2));
690
Kevin Mayfcf2a152020-09-08 16:06:32 +0100691 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
692 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +0000693}
694
695template<typename HalPolicy,
696 typename HalOperation = typename HalPolicy::Operation,
697 typename HalModel = typename HalPolicy::Model>
698bool ConvertDequantize_1_2(const HalOperation& operation, const HalModel& model, ConversionData& data)
699{
700 ALOGV("HalPolicy::ConvertDequantize()");
701
702 if (IsQSymmDequantizeForWeights<HalPolicy>(operation, model))
703 {
704 // NOTE: QSymm8 weights are dequantized internally by the driver,
705 // therefore this type of Dequantize is implicitly supported
706 return true;
707 }
708
709 return ::ConvertDequantize<HalPolicy>(operation, model, data);
710}
711
712template<typename HalPolicy,
713 typename HalOperation = typename HalPolicy::Operation,
714 typename HalModel = typename HalPolicy::Model>
715bool ConvertElementwiseUnary(const HalOperation& operation,
716 const HalModel& model,
717 ConversionData& data,
718 UnaryOperation unaryOperation)
719{
720 using HalOperand = typename HalPolicy::Operand;
721
722 ALOGV("HalPolicy::ConvertElementwiseUnary()");
723 ALOGV("unaryOperation = %s", GetUnaryOperationAsCString(unaryOperation));
724
725 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
726
727 if (!input.IsValid())
728 {
729 return Fail("%s: Operation has invalid input", __func__);
730 }
731
732 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
733 if (!output)
734 {
735 return Fail("%s: Could not read output 0", __func__);
736 }
737
738 const TensorInfo& inputInfo = input.GetTensorInfo();
739 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
740
Kevin May42477c12020-03-26 13:34:14 +0000741 ElementwiseUnaryDescriptor descriptor(unaryOperation);
742
743 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100744
745 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
746 {
747 FORWARD_LAYER_SUPPORT_FUNC(__func__,
748 IsElementwiseUnarySupported,
749 data.m_Backends,
750 isSupported,
751 inputInfo,
752 outputInfo,
753 descriptor);
754 };
755
756 if(!IsDynamicTensor(outputInfo))
757 {
758 validateFunc(outputInfo, isSupported);
759 }
760 else
761 {
762 isSupported = AreDynamicTensorsSupported();
763 }
Kevin May42477c12020-03-26 13:34:14 +0000764
765 if (!isSupported)
766 {
767 return false;
768 }
769
770 IConnectableLayer* layer = data.m_Network->AddElementwiseUnaryLayer(descriptor);
Mike Kellye2d611e2021-10-14 12:35:58 +0100771 if (!layer)
772 {
773 return Fail("%s: Could not add the ElementwiseUnaryLayer", __func__);
774 }
Kevin May42477c12020-03-26 13:34:14 +0000775 input.Connect(layer->GetInputSlot(0));
776
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100777 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000778}
779
780template<typename HalPolicy,
781 typename HalOperation = typename HalPolicy::Operation,
782 typename HalModel = typename HalPolicy::Model>
783bool ConvertExpandDims(const HalOperation& operation, const HalModel& model, ConversionData& data)
784{
785 using HalOperand = typename HalPolicy::Operand;
786 using HalOperandType = typename HalPolicy::OperandType;
787
788 ALOGV("HalPolicy::ConvertExpandDims()");
789
790 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
791
792 if (!input.IsValid())
793 {
794 return Fail("%s: Operation has invalid input", __func__);
795 }
796
797 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
798 if (!output)
799 {
800 return Fail("%s: Operation has invalid output", __func__);
801 }
802
803 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +0000804
805 int32_t axis;
806 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
807 {
808 return Fail("%s: failed to get axis input value", __func__);
809 }
810
811 TensorShape targetShape;
812
813 try
814 {
815 targetShape = armnnUtils::ExpandDims(input.GetTensorInfo().GetShape(), axis);
816 }
817 catch (const std::exception& e)
818 {
819 return Fail("%s: %s", __func__, e.what());
820 }
821
Kevin May42477c12020-03-26 13:34:14 +0000822 ReshapeDescriptor reshapeDescriptor;
823 reshapeDescriptor.m_TargetShape = targetShape;
824
825 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100826 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
827 {
828 FORWARD_LAYER_SUPPORT_FUNC(__func__,
829 IsReshapeSupported,
830 data.m_Backends,
831 isSupported,
832 input.GetTensorInfo(),
833 outputInfo,
834 reshapeDescriptor);
835 };
836
837 if(!IsDynamicTensor(outputInfo))
838 {
Nikhil Rajc5e0bb02021-04-02 10:02:16 +0100839 if (targetShape != outputInfo.GetShape())
840 {
841 return Fail("%s: Shape of the output operand does not match the resolved expanded shape", __func__);
842 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100843 validateFunc(outputInfo, isSupported);
844 }
845 else
846 {
847 isSupported = AreDynamicTensorsSupported();
848 }
Kevin May42477c12020-03-26 13:34:14 +0000849
850 if (!isSupported)
851 {
852 return false;
853 }
854
855 IConnectableLayer* layer = data.m_Network->AddReshapeLayer(reshapeDescriptor);
Mike Kellye2d611e2021-10-14 12:35:58 +0100856 if (!layer)
857 {
858 return Fail("%s: Could not add the ReshapeLayer", __func__);
859 }
Kevin May42477c12020-03-26 13:34:14 +0000860 input.Connect(layer->GetInputSlot(0));
861
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100862 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +0000863}
864
865template<typename HalPolicy,
Teresa Charlinf931af92020-04-10 16:46:53 +0100866 typename HalOperation = typename HalPolicy::Operation,
867 typename HalModel = typename HalPolicy::Model>
868bool ConvertGather(const HalOperation& operation, const HalModel& model, ConversionData& data)
869{
870 using HalOperand = typename HalPolicy::Operand;
871 using HalOperandType = typename HalPolicy::OperandType;
872
873 ALOGV("HalPolicy::ConvertGather()");
874
875 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
876 if (!input.IsValid())
877 {
878 return Fail("%s: Operation has invalid input", __func__);
879 }
880 auto inputDimensions = input.GetTensorInfo().GetNumDimensions();
881
882 LayerInputHandle indices = ConvertToLayerInputHandle<HalPolicy>(operation, 2, model, data);
883 if (!indices.IsValid())
884 {
885 return Fail("%s: Operation has invalid indices", __func__);
886 }
887 auto indicesDimensions = indices.GetTensorInfo().GetNumDimensions();
888
889 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
890 if (!output)
891 {
892 return Fail("%s: Operation has invalid output", __func__);
893 }
894 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
895 auto outputDimensions = outputInfo.GetNumDimensions();
Teresa Charlinf931af92020-04-10 16:46:53 +0100896 if (outputDimensions != inputDimensions + indicesDimensions - 1)
897 {
898 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 +0100899 __func__, outputDimensions, inputDimensions, indicesDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100900 }
901
Finn Williamsf769f292021-06-25 12:53:09 +0100902 uint32_t axis;
Teresa Charlinf931af92020-04-10 16:46:53 +0100903 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, axis, model, data))
904 {
905 return Fail("%s: Operation has invalid or unsupported axis operand", __func__);
906 }
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100907 if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
Teresa Charlinf931af92020-04-10 16:46:53 +0100908 {
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100909 return Fail("%s: Operation has invalid axis: %d. It is out of bounds [-%d, %d))", __func__, axis,
910 inputDimensions, inputDimensions);
Teresa Charlinf931af92020-04-10 16:46:53 +0100911 }
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100912
913 GatherDescriptor desc;
914 desc.m_Axis = axis;
Teresa Charlinf931af92020-04-10 16:46:53 +0100915
916 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100917 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
918 {
919 FORWARD_LAYER_SUPPORT_FUNC(__func__,
920 IsGatherSupported,
921 data.m_Backends,
922 isSupported,
923 input.GetTensorInfo(),
924 indices.GetTensorInfo(),
925 outputInfo,
926 desc);
927 };
928
929 if(!IsDynamicTensor(outputInfo))
930 {
931 validateFunc(outputInfo, isSupported);
932 }
933 else
934 {
935 isSupported = AreDynamicTensorsSupported();
936 }
937
Teresa Charlinf931af92020-04-10 16:46:53 +0100938 if (!isSupported)
939 {
940 return false;
941 }
942
Teresa Charlin5d4873f2020-06-03 14:39:29 +0100943 IConnectableLayer* layer = data.m_Network->AddGatherLayer(desc);
Mike Kellye2d611e2021-10-14 12:35:58 +0100944 if (!layer)
945 {
946 return Fail("%s: Could not add the GatherLayer", __func__);
947 }
Teresa Charlinf931af92020-04-10 16:46:53 +0100948 input.Connect(layer->GetInputSlot(0));
949 indices.Connect(layer->GetInputSlot(1));
950
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100951 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Teresa Charlinf931af92020-04-10 16:46:53 +0100952}
953
954template<typename HalPolicy,
Kevin May42477c12020-03-26 13:34:14 +0000955 typename HalOperation = typename HalPolicy::Operation,
956 typename HalModel = typename HalPolicy::Model>
957bool ConvertGroupedConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
958{
959 using HalOperand = typename HalPolicy::Operand;
960 using HalOperandType = typename HalPolicy::OperandType;
961
962 ALOGV("HalPolicy::ConvertGroupedConv2d()");
963
964 //
965 // Parse data
966 //
967 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
968 if (!input.IsValid())
969 {
970 return Fail("%s: Operation has invalid inputs", __func__);
971 }
972 const TensorInfo& inputInfo = input.GetTensorInfo();
973
974 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
975 if (!output)
976 {
977 return Fail("%s: Could not read output 0", __func__);
978 }
Finn Williamsb0331172020-10-08 14:33:13 +0100979 TensorInfo outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +0000980
981 // Look ahead to determine data layout
982 DataLayout dataLayout = DataLayout::NHWC;
983 if (operation.inputs.size() == 12)
984 {
985 dataLayout = OptionalDataLayout<HalPolicy>(operation, 11, model, data);
986 }
987 else
988 {
989 dataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
990 }
991
992 // NOTE:
993 // NNAPI weights are always OHWI, i.e. [depth_out, filter_height, filter_width, depth_group],
994 // but Arm NN expects the filter's height and width indices to match the input's height and
995 // width indices so when the DataLayout is NCHW, we need to permute the weights to OIHW
996 const PermutationVector ohwiToOihw = { 0u, 2u, 3u, 1u };
997 const ConstTensorPin weightsPin = (dataLayout == DataLayout::NCHW) ?
998 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
999 model, data, ohwiToOihw) :
1000 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1001 const ConstTensorPin biasesPin =
1002 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
1003 if (!weightsPin.IsValid() || !biasesPin.IsValid())
1004 {
1005 return Fail("%s: Operation has invalid inputs", __func__);
1006 }
1007
1008 ConstTensor weights = weightsPin.GetConstTensor();
1009 ConstTensor biases = biasesPin.GetConstTensor();
1010 SanitizeBiasQuantizationScale(biases.GetInfo(), weights.GetInfo(), inputInfo);
1011
1012 const TensorShape& inputShape = inputInfo.GetShape();
1013 const TensorShape& outputShape = outputInfo.GetShape();
1014 const TensorShape& weightsShape = weights.GetShape();
Kevin May42477c12020-03-26 13:34:14 +00001015
1016 armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
1017 const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
1018 const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
1019 const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
1020
1021 Convolution2dDescriptor desc;
1022 desc.m_DataLayout = dataLayout;
1023 desc.m_BiasEnabled = true;
1024
Finn Williamsf769f292021-06-25 12:53:09 +01001025 unsigned int numGroups;
Kevin May42477c12020-03-26 13:34:14 +00001026 ActivationFn activation;
1027
1028 if (operation.inputs.size() == 12)
1029 {
1030 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1031 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1032 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1033 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1034 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1035 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1036 !GetInputScalar<HalPolicy>(operation, 9, HalOperandType::INT32, numGroups, model, data) ||
1037 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data))
1038 {
1039 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
1040 }
1041
1042 }
1043 else if (operation.inputs.size() == 9)
1044 {
1045 android::nn::PaddingScheme paddingScheme;
1046 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
1047 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1048 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1049 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, numGroups, model, data) ||
1050 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
1051 {
1052 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
1053 }
1054
1055 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
1056 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
1057
1058 const uint32_t kernelX = weightsShape[widthIndex];
1059 const uint32_t kernelY = weightsShape[heightIndex];
1060
1061 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
1062 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
1063 }
1064 else
1065 {
1066 return Fail("%s: Unsupported number of operation inputs", __func__);
1067 }
1068
Finn Williamsb0331172020-10-08 14:33:13 +01001069 // Equivalent to outputShape[channelsIndex], but we can't know the outputShape in the case of dynamic tensors
1070 const unsigned int outputChannels = weightsShape[0];
Kevin May42477c12020-03-26 13:34:14 +00001071
1072 const unsigned int channelsPerGroup = weightsShape[channelsIndex];
1073 const unsigned int channelMultiplier = outputChannels / numGroups;
1074
1075 //
1076 // Validate all relevant inputs
1077 //
1078 if (numGroups <= 0)
1079 {
1080 return Fail("%s: Number of groups must be greater than 0. Got: %d", __func__, numGroups);
1081 }
1082
1083 if (outputChannels % numGroups != 0u)
1084 {
1085 return Fail("%s: Output channels must be divisible by the number of groups", __func__);
1086 }
1087
1088 //
1089 // Set up Splitter layer
1090 //
1091 unsigned int splitterDimSizes[4] = { inputShape[0], inputShape[1], inputShape[2], inputShape[3] };
1092 splitterDimSizes[channelsIndex] /= numGroups; // split in depth
1093
1094 TensorInfo splitterOutputInfo(4,
1095 splitterDimSizes,
1096 inputInfo.GetDataType(),
1097 inputInfo.GetQuantizationScale(),
1098 inputInfo.GetQuantizationOffset());
1099
1100 std::vector<std::reference_wrapper<TensorInfo>> splitterOutputInfos(numGroups, std::ref(splitterOutputInfo));
1101
1102 ViewsDescriptor splitterDesc(numGroups);
1103 for (unsigned int group = 0u; group < numGroups; ++group)
1104 {
1105 splitterDesc.SetViewOriginCoord(group, channelsIndex, splitterDimSizes[channelsIndex] * group);
1106 for (unsigned int dimIdx = 0u; dimIdx < 4u; dimIdx++)
1107 {
1108 splitterDesc.SetViewSize(group, dimIdx, splitterDimSizes[dimIdx]);
1109 }
1110 }
1111
1112 bool isSupported = false;
1113 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1114 IsSplitterSupported,
1115 data.m_Backends,
1116 isSupported,
1117 inputInfo,
1118 splitterOutputInfos,
1119 splitterDesc);
1120 if (!isSupported)
1121 {
1122 return false;
1123 }
1124
1125 IConnectableLayer* splitterLayer = data.m_Network->AddSplitterLayer(splitterDesc);
1126 if (!splitterLayer)
1127 {
1128 return Fail("%s: Failed to add SplitterLayer", __func__);
1129 }
1130
1131 input.Connect(splitterLayer->GetInputSlot(0));
1132 for (unsigned int group = 0u; group < splitterLayer->GetNumOutputSlots(); ++group)
1133 {
1134 splitterLayer->GetOutputSlot(group).SetTensorInfo(splitterOutputInfo);
1135 }
1136
1137 //
1138 // Set up Convolution2d layers for each group
1139 //
1140
1141 // Set up group tensor shapes
1142 TensorShape groupInputShape(inputShape);
1143 groupInputShape[channelsIndex] = channelsPerGroup;
1144
Kevin May42477c12020-03-26 13:34:14 +00001145 TensorShape groupWeightsShape(weightsShape);
1146 groupWeightsShape[0] /= channelMultiplier * numGroups;
1147
1148 TensorShape groupBiasesShape({ 1 });
1149
1150 // Set up group tensor infos
1151 TensorInfo groupInputInfo(inputInfo);
1152 groupInputInfo.SetShape(groupInputShape);
1153
1154 const TensorInfo& weightsInfo = weights.GetInfo();
1155 TensorInfo groupWeightsInfo(weightsInfo);
1156 groupWeightsInfo.SetShape(groupWeightsShape);
1157
1158 const TensorInfo& biasesInfo = biases.GetInfo();
1159 TensorInfo groupBiasesInfo(biasesInfo);
1160 groupBiasesInfo.SetShape(groupBiasesShape);
1161
1162 TensorInfo groupOutputInfo(outputInfo);
Finn Williamsb0331172020-10-08 14:33:13 +01001163
1164 TensorShape groupOutputShape(outputShape);
1165 const bool isDynamic = IsDynamicTensor(outputInfo);
1166 if (!isDynamic)
1167 {
1168 groupOutputShape[channelsIndex] = 1;
1169 }
Kevin May42477c12020-03-26 13:34:14 +00001170 groupOutputInfo.SetShape(groupOutputShape);
1171
1172 const unsigned int weightsDataTypeSize = GetDataTypeSize(groupWeightsInfo.GetDataType());
1173 const unsigned int biasesDataTypeSize = GetDataTypeSize(groupBiasesInfo.GetDataType());
1174
1175 std::vector<IConnectableLayer*> convLayers(numGroups * channelMultiplier, nullptr);
1176 for (unsigned int group = 0u; group < numGroups; ++group)
1177 {
1178 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1179 {
1180 auto index = group * channelMultiplier + m;
1181
1182 const unsigned int weightsDataOffset = groupWeightsShape.GetNumElements() * index * weightsDataTypeSize;
1183 const unsigned int biasesDataOffset = groupBiasesShape.GetNumElements() * index * biasesDataTypeSize;
1184
1185 if (weightsInfo.HasPerAxisQuantization())
1186 {
1187 // Extract per-axis quantization scales for group weights
1188 const std::vector<float>& weightsQuantScales = weightsInfo.GetQuantizationScales();
1189 groupWeightsInfo.SetQuantizationScales(
1190 std::vector<float>(weightsQuantScales.begin() + index,
1191 weightsQuantScales.begin() + index + groupWeightsShape[0]));
1192
1193 // Extract per-axis quantization scales for group biases
1194 const std::vector<float>& biasesQuantScales = biasesInfo.GetQuantizationScales();
1195 groupBiasesInfo.SetQuantizationScales(
1196 std::vector<float>(biasesQuantScales.begin() + index,
1197 biasesQuantScales.begin() + index + groupWeightsShape[0]));
1198 }
1199
1200 // Extract weights and biases data for current group convolution
1201 ConstTensor groupWeights(groupWeightsInfo,
1202 static_cast<const void *>(reinterpret_cast<const char *>(weights.GetMemoryArea()) +
1203 weightsDataOffset));
1204 ConstTensor groupBiases(groupBiasesInfo,
1205 static_cast<const void *>(reinterpret_cast<const char *>(biases.GetMemoryArea()) +
1206 biasesDataOffset));
1207
1208 isSupported = false;
Finn Williamsb0331172020-10-08 14:33:13 +01001209 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1210 {
1211 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1212 IsConvolution2dSupported,
1213 data.m_Backends,
1214 isSupported,
1215 groupInputInfo,
1216 outputInfo,
1217 desc,
1218 groupWeightsInfo,
1219 Optional<TensorInfo>(groupBiasesInfo));
1220 };
1221
1222 if(!isDynamic)
1223 {
1224 validateFunc(groupOutputInfo, isSupported);
1225 }
1226 else
1227 {
1228 isSupported = AreDynamicTensorsSupported();
1229 }
1230
Kevin May42477c12020-03-26 13:34:14 +00001231 if (!isSupported)
1232 {
1233 return false;
1234 }
1235
Keith Davis8f22bed2022-04-29 10:57:27 +01001236 ARMNN_NO_DEPRECATE_WARN_BEGIN
Kevin May42477c12020-03-26 13:34:14 +00001237 IConnectableLayer* convLayer =
1238 data.m_Network->AddConvolution2dLayer(desc, groupWeights, Optional<ConstTensor>(groupBiases));
Keith Davis8f22bed2022-04-29 10:57:27 +01001239 ARMNN_NO_DEPRECATE_WARN_END
1240
Kevin May42477c12020-03-26 13:34:14 +00001241 if (!convLayer)
1242 {
1243 return Fail("%s: AddConvolution2dLayer failed", __func__);
1244 }
1245
1246 splitterLayer->GetOutputSlot(group).Connect(convLayer->GetInputSlot(0));
1247 convLayer->GetOutputSlot(0).SetTensorInfo(groupOutputInfo);
1248
Finn Williamsb0331172020-10-08 14:33:13 +01001249 if(isDynamic)
1250 {
1251 convLayer->GetOutputSlot(0).IsTensorInfoSet();
1252
1253 validateFunc(convLayer->GetOutputSlot(0).GetTensorInfo(), isSupported);
1254
1255 outputInfo = convLayer->GetOutputSlot(0).GetTensorInfo();
1256
1257 if (!isSupported)
1258 {
1259 return false;
1260 }
1261 }
1262
Kevin May42477c12020-03-26 13:34:14 +00001263 convLayers[index] = convLayer;
1264 }
1265 }
1266
1267 //
1268 // Set up Concat layer
1269 //
Finn Williamsb0331172020-10-08 14:33:13 +01001270 ConcatDescriptor concatDescriptor;
1271 // Equivalent to outputShape[channelsIndex], but we can't know the outputShape in the case of dynamic tensors
1272 concatDescriptor = ConcatDescriptor(weightsShape[0]);
Kevin May42477c12020-03-26 13:34:14 +00001273 for (unsigned int group = 0u; group < numGroups; ++group)
1274 {
1275 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1276 {
1277 auto index = group * channelMultiplier + m;
1278 concatDescriptor.SetViewOriginCoord(index, channelsIndex, index);
1279 concatDescriptor.SetConcatAxis(channelsIndex);
1280 }
1281 }
1282
1283 isSupported = false;
Finn Williamsb0331172020-10-08 14:33:13 +01001284 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1285 IsConcatSupported,
1286 data.m_Backends,
1287 isSupported,
1288 std::vector<const TensorInfo*>(numGroups * channelMultiplier, &groupOutputInfo),
1289 outputInfo,
1290 concatDescriptor);
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001291
Kevin May42477c12020-03-26 13:34:14 +00001292 if (!isSupported)
1293 {
1294 return false;
1295 }
1296
1297 IConnectableLayer* concatLayer = data.m_Network->AddConcatLayer(concatDescriptor);
1298 if (!concatLayer)
1299 {
1300 return Fail("%s: AddConcatLayer failed", __func__);
1301 }
1302
1303 for (unsigned int group = 0u; group < numGroups; ++group)
1304 {
1305 for (unsigned int m = 0u; m < channelMultiplier; ++m)
1306 {
1307 auto index = group * channelMultiplier + m;
1308 convLayers[index]->GetOutputSlot(0).Connect(concatLayer->GetInputSlot(index));
1309 }
1310 }
1311 concatLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1312
Kevin Mayfcf2a152020-09-08 16:06:32 +01001313 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *concatLayer, model,
Finn Williamsb0331172020-10-08 14:33:13 +01001314 data, nullptr, nullptr, activation);
Kevin May42477c12020-03-26 13:34:14 +00001315}
1316
1317template<typename HalPolicy,
1318 typename HalOperation = typename HalPolicy::Operation,
1319 typename HalModel = typename HalPolicy::Model>
1320bool ConvertInstanceNormalization(const HalOperation& operation, const HalModel& model, ConversionData& data)
1321{
1322 using HalOperand = typename HalPolicy::Operand;
1323 using HalOperandType = typename HalPolicy::OperandType;
1324
1325 ALOGV("HalPolicy::ConvertInstanceNormalization()");
1326
1327 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1328 if (!input.IsValid())
1329 {
1330 return Fail("%s: Operation has an invalid input 0", __func__);
1331 }
1332
1333 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1334 if (!output)
1335 {
1336 return Fail("%s: Operation has an invalid output", __func__);
1337 }
1338
1339 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001340
1341 // Determine data type of input tensor
1342 HalOperandType inputType;
1343 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1344 {
1345 return Fail("%s: Operation has invalid inputs", __func__);
1346 }
1347
1348 InstanceNormalizationDescriptor desc;
1349
1350 // Read gamma, beta & epsilon
1351 if (inputType == HalOperandType::TENSOR_FLOAT16)
1352 {
1353 Half fp16Gamma;
1354 Half fp16Beta;
1355 Half fp16Epsilon;
1356
1357 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Gamma, model, data) ||
1358 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, fp16Beta, model, data) ||
1359 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT16, fp16Epsilon, model, data))
1360 {
1361 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
1362 }
1363
1364 desc.m_Gamma = static_cast<float>(fp16Gamma);
1365 desc.m_Beta = static_cast<float>(fp16Beta);
1366 desc.m_Eps = static_cast<float>(fp16Epsilon);
1367 }
1368 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1369 {
1370 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, desc.m_Gamma, model, data) ||
1371 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT32, desc.m_Beta, model, data) ||
1372 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::FLOAT32, desc.m_Eps, model, data))
1373 {
1374 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
1375 }
1376 }
1377 else
1378 {
1379 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1380 }
1381
1382 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 4, model, data);
1383
1384 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001385 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1386 {
1387 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1388 IsInstanceNormalizationSupported,
1389 data.m_Backends,
1390 isSupported,
1391 input.GetTensorInfo(),
1392 outputInfo,
1393 desc);
1394 };
1395
1396 if(IsDynamicTensor(outputInfo))
1397 {
1398 isSupported = AreDynamicTensorsSupported();
1399 }
1400 else
1401 {
1402 validateFunc(outputInfo, isSupported);
1403 }
1404
Kevin May42477c12020-03-26 13:34:14 +00001405 if (!isSupported)
1406 {
1407 return false;
1408 }
1409
1410 IConnectableLayer* layer = data.m_Network->AddInstanceNormalizationLayer(desc);
1411 input.Connect(layer->GetInputSlot(0));
1412
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001413 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001414}
1415
1416template<typename HalPolicy,
1417 typename HalOperation = typename HalPolicy::Operation,
1418 typename HalModel = typename HalPolicy::Model>
1419bool ConvertLogSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
1420{
1421 using HalOperand = typename HalPolicy::Operand;
1422 using HalOperandType = typename HalPolicy::OperandType;
1423
1424 ALOGV("HalPolicy::ConvertLogSoftmax()");
1425
1426 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1427 if (!input.IsValid())
1428 {
1429 return Fail("%s: Failed to read input 0", __func__);
1430 }
1431
1432 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1433 if (!output)
1434 {
1435 return Fail("%s: Failed to read output", __func__);
1436 }
1437
1438 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001439
1440 // Determine data type of input tensor
1441 HalOperandType inputType;
1442 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
1443 {
1444 return Fail("%s: Operation has invalid inputs", __func__);
1445 }
1446
1447 LogSoftmaxDescriptor descriptor;
1448
1449 // Read beta
1450 if (inputType == HalOperandType::TENSOR_FLOAT16)
1451 {
1452 Half fp16Beta;
1453 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, fp16Beta, model, data))
1454 {
1455 return Fail("%s: Failed to read input 1 (FLOAT16)", __func__);
1456 }
1457
1458 descriptor.m_Beta = static_cast<float>(fp16Beta);
1459 }
1460 else if (inputType == HalOperandType::TENSOR_FLOAT32)
1461 {
1462 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, descriptor.m_Beta, model, data))
1463 {
1464 return Fail("%s: Failed to read input 1 (FLOAT32)", __func__);
1465 }
1466 }
1467 else
1468 {
1469 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
1470 }
1471
1472 // Read axis
1473 if (!GetInputInt32<HalPolicy>(operation, 2, descriptor.m_Axis, model, data))
1474 {
1475 return Fail("%s: Failed to read input 2", __func__);
1476 }
1477
1478 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001479 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1480 {
1481 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1482 IsLogSoftmaxSupported,
1483 data.m_Backends,
1484 isSupported,
1485 input.GetTensorInfo(),
1486 outputInfo,
1487 descriptor);
1488 };
1489
1490 if(IsDynamicTensor(outputInfo))
1491 {
1492 isSupported = AreDynamicTensorsSupported();
1493 }
1494 else
1495 {
1496 validateFunc(outputInfo, isSupported);
1497 }
1498
Kevin May42477c12020-03-26 13:34:14 +00001499 if (!isSupported)
1500 {
1501 return false;
1502 }
1503
1504 IConnectableLayer* layer = data.m_Network->AddLogSoftmaxLayer(descriptor);
1505 if (!layer)
1506 {
Mike Kellye2d611e2021-10-14 12:35:58 +01001507 return Fail("%s: Could not add the LogSoftmaxLayer", __func__);
Kevin May42477c12020-03-26 13:34:14 +00001508 }
Kevin May42477c12020-03-26 13:34:14 +00001509 input.Connect(layer->GetInputSlot(0));
1510
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001511 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001512}
1513
1514template<typename HalPolicy,
1515 typename HalOperation = typename HalPolicy::Operation,
1516 typename HalModel = typename HalPolicy::Model>
1517bool ConvertMaximum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1518{
1519 using HalOperand = typename HalPolicy::Operand;
1520
1521 ALOGV("HalPolicy::ConvertMaximum()");
1522
1523 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1524 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1525
1526 if (!input0.IsValid() || !input1.IsValid())
1527 {
1528 return Fail("%s: Operation has invalid inputs", __func__);
1529 }
1530
1531 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1532 if (!outputOperand)
1533 {
1534 return Fail("%s: Could not read output", __func__);
1535 }
1536
1537 const TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001538
1539 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001540 auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported)
1541 {
1542 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1543 IsMaximumSupported,
1544 data.m_Backends,
1545 isSupported,
1546 input0.GetTensorInfo(),
1547 input1.GetTensorInfo(),
1548 outInfo);
1549 };
1550
1551 if(IsDynamicTensor(outInfo))
1552 {
1553 isSupported = AreDynamicTensorsSupported();
1554 }
1555 else
1556 {
1557 validateFunc(outInfo, isSupported);
1558 }
Kevin May42477c12020-03-26 13:34:14 +00001559
1560 if (!isSupported)
1561 {
1562 return false;
1563 }
1564
1565 IConnectableLayer* layer = data.m_Network->AddMaximumLayer();
Mike Kellye2d611e2021-10-14 12:35:58 +01001566 if (!layer)
1567 {
1568 return Fail("%s: Could not add the MaximumLayer", __func__);
1569 }
Kevin May42477c12020-03-26 13:34:14 +00001570 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1571 if (!isReshapeSupported)
1572 {
1573 return false;
1574 }
1575
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001576 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001577}
1578
1579template<typename HalPolicy,
1580 typename HalOperation = typename HalPolicy::Operation,
1581 typename HalModel = typename HalPolicy::Model>
1582bool ConvertMinimum(const HalOperation& operation, const HalModel& model, ConversionData& data)
1583{
1584 using HalOperand = typename HalPolicy::Operand;
1585
1586 ALOGV("HalPolicy::ConvertMinimum()");
1587
1588 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1589 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1590
1591 if (!input0.IsValid() || !input1.IsValid())
1592 {
1593 return Fail("%s: Operation has invalid inputs", __func__);
1594 }
1595
1596 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1597 if (!output)
1598 {
1599 return Fail("%s: Could not read output 0", __func__);
1600 }
1601
1602 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001603
1604 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001605 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1606 {
1607 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1608 IsMinimumSupported,
1609 data.m_Backends,
1610 isSupported,
1611 input0.GetTensorInfo(),
1612 input1.GetTensorInfo(),
1613 outputInfo);
1614 };
1615
1616 if(IsDynamicTensor(outputInfo))
1617 {
1618 isSupported = AreDynamicTensorsSupported();
1619 }
1620 else
1621 {
1622 validateFunc(outputInfo, isSupported);
1623 }
Kevin May42477c12020-03-26 13:34:14 +00001624
1625 if (!isSupported)
1626 {
1627 return false;
1628 }
1629
1630 IConnectableLayer* const layer = data.m_Network->AddMinimumLayer();
Mike Kellye2d611e2021-10-14 12:35:58 +01001631 if (!layer)
1632 {
1633 return Fail("%s: Could not add the MinimumLayer", __func__);
1634 }
Kevin May42477c12020-03-26 13:34:14 +00001635 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
1636 if (!isReshapeSupported)
1637 {
1638 return false;
1639 }
1640
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001641 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001642}
1643
1644template<typename HalPolicy,
1645 typename HalOperation = typename HalPolicy::Operation,
1646 typename HalModel = typename HalPolicy::Model>
1647bool ConvertPadV2(const HalOperation& operation, const HalModel& model, ConversionData& data)
1648{
1649 using HalOperand = typename HalPolicy::Operand;
1650 using HalOperandType = typename HalPolicy::OperandType;
1651
1652 ALOGV("HalPolicy::ConvertPadV2()");
1653
1654 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1655 if (!input.IsValid())
1656 {
1657 return Fail("%s: Could not read input 0", __func__);
1658 }
1659
1660 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1661 if (!output)
1662 {
1663 return Fail("%s: Could not read output", __func__);
1664 }
1665
1666 const TensorInfo& inputInfo = input.GetTensorInfo();
1667 unsigned int rank = inputInfo.GetNumDimensions();
1668
1669 PadDescriptor descriptor;
1670 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
1671 {
1672 return Fail("%s: Could not convert paddings", __func__);
1673 }
1674
1675 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00001676
1677 // Determine type of padding value
1678 HalOperandType operandType0;
1679 HalOperandType operandType2;
1680
1681 if (!GetOperandType<HalPolicy>(operation, 0, model, operandType0) ||
1682 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
1683 {
1684 return Fail("%s: Operation has invalid inputs", __func__);
1685 }
1686
1687 // Read value to use for padding
1688 if (operandType0 == HalOperandType::TENSOR_FLOAT16 && operandType2 == HalOperandType::FLOAT16)
1689 {
1690 Half f16PadValue;
1691 if (!GetInputScalar<HalPolicy>(operation, 2, operandType2, f16PadValue, model, data))
1692 {
1693 return Fail("%s: Could not read input 2 (FLOAT16)", __func__);
1694 }
1695
1696 descriptor.m_PadValue = f16PadValue;
1697 }
1698 else if (operandType0 == HalOperandType::TENSOR_FLOAT32 && operandType2 == HalOperandType::FLOAT32)
1699 {
1700 if (!GetInputFloat32<HalPolicy>(operation, 2, descriptor.m_PadValue, model, data))
1701 {
1702 return Fail("%s: Could not read input 2 (FLOAT32)", __func__);
1703 }
1704 }
Teresa Charlind3381d52021-06-02 18:35:16 +01001705 else if (isQuantizedOperand(operandType0) && operandType2 == HalOperandType::INT32)
Kevin May42477c12020-03-26 13:34:14 +00001706 {
1707 int32_t intPadValue = 0;
1708 if (!GetInputInt32<HalPolicy>(operation, 2, intPadValue, model, data))
1709 {
1710 return Fail("%s: Could not read input 2 (INT32)", __func__);
1711 }
1712 descriptor.m_PadValue = intPadValue;
1713 }
1714 else
1715 {
1716 return Fail("%s: Operation has invalid inputs: type mismatch", __func__);
1717 }
1718
1719 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001720 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1721 {
1722 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1723 IsPadSupported,
1724 data.m_Backends,
1725 isSupported,
1726 inputInfo,
1727 outputInfo,
1728 descriptor);
1729 };
1730
1731 if(IsDynamicTensor(outputInfo))
1732 {
1733 isSupported = AreDynamicTensorsSupported();
1734 }
1735 else
1736 {
1737 validateFunc(outputInfo, isSupported);
1738 }
1739
Kevin May42477c12020-03-26 13:34:14 +00001740 if (!isSupported)
1741 {
1742 return false;
1743 }
1744
1745 IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
Mike Kellye2d611e2021-10-14 12:35:58 +01001746 if (!layer)
1747 {
1748 return Fail("%s: Could not add the PadLayer", __func__);
1749 }
Kevin May42477c12020-03-26 13:34:14 +00001750 input.Connect(layer->GetInputSlot(0));
Kevin May42477c12020-03-26 13:34:14 +00001751
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001752 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001753}
1754
1755template<typename HalPolicy,
1756 typename HalOperation = typename HalPolicy::Operation,
1757 typename HalModel = typename HalPolicy::Model>
1758bool ConvertPrelu(const HalOperation& operation, const HalModel& model, ConversionData& data)
1759{
1760 using HalOperand = typename HalPolicy::Operand;
1761
1762 ALOGV("HalPolicy::ConvertPrelu()");
1763
1764 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1765 LayerInputHandle alpha = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
1766
1767 if (!input.IsValid() || !alpha.IsValid())
1768 {
1769 return Fail("%s: Operation has invalid inputs", __func__);
1770 }
1771
1772 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1773
1774 if (!output)
1775 {
1776 return Fail("%s: Could not read output", __func__);
1777 }
1778
1779 const TensorInfo& inputInfo = input.GetTensorInfo();
1780 const TensorInfo& alphaInfo = alpha.GetTensorInfo();
1781 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1782
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001783 bool isSupported = false;
1784 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
Kevin May42477c12020-03-26 13:34:14 +00001785 {
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001786 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1787 IsPreluSupported,
1788 data.m_Backends,
1789 isSupported,
1790 inputInfo,
1791 alphaInfo,
1792 outputInfo);
1793 };
1794
1795 if(IsDynamicTensor(outputInfo))
1796 {
1797 isSupported = AreDynamicTensorsSupported();
1798 }
1799 else
1800 {
1801 validateFunc(outputInfo, isSupported);
Kevin May42477c12020-03-26 13:34:14 +00001802 }
1803
Kevin May42477c12020-03-26 13:34:14 +00001804 if (!isSupported)
1805 {
1806 return false;
1807 }
1808
1809 IConnectableLayer* const layer = data.m_Network->AddPreluLayer();
Kevin May42477c12020-03-26 13:34:14 +00001810 if (!layer)
1811 {
Mike Kellye2d611e2021-10-14 12:35:58 +01001812 return Fail("%s: Could not add the PreluLayer", __func__);
Kevin May42477c12020-03-26 13:34:14 +00001813 }
1814
1815 bool isReshapeSupported = BroadcastTensor(input, alpha, layer, data);
1816 if (!isReshapeSupported)
1817 {
1818 return false;
1819 }
1820
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001821 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001822}
1823
1824template<typename HalPolicy,
1825 typename HalOperation = typename HalPolicy::Operation,
1826 typename HalModel = typename HalPolicy::Model>
1827bool ConvertQuantize(const HalOperation& operation, const HalModel& model, ConversionData& data)
1828{
1829 using HalOperand = typename HalPolicy::Operand;
1830
1831 ALOGV("HalPolicy::ConvertQuantize()");
1832
1833 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1834 if (!input.IsValid())
1835 {
1836 return Fail("%s: Operation has invalid input", __func__);
1837 }
1838
1839 const HalOperand* const outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
1840 if (!outputOperand)
1841 {
1842 return Fail("%s: Operation has invalid outputs", __func__);
1843 }
1844
1845 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00001846
1847 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001848 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
1849 {
1850 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1851 IsQuantizeSupported,
1852 data.m_Backends,
1853 isSupported,
1854 input.GetTensorInfo(),
1855 outputInfo);
1856 };
1857
1858 if(IsDynamicTensor(outputInfo))
1859 {
1860 isSupported = AreDynamicTensorsSupported();
1861 }
1862 else
1863 {
1864 validateFunc(outputInfo, isSupported);
1865 }
1866
Kevin May42477c12020-03-26 13:34:14 +00001867 if (!isSupported)
1868 {
1869 return false;
1870 }
1871
1872 IConnectableLayer* const layer = data.m_Network->AddQuantizeLayer();
Mike Kellye2d611e2021-10-14 12:35:58 +01001873 if (!layer)
1874 {
1875 return Fail("%s: Could not add the QuantizeLayer", __func__);
1876 }
Kevin May42477c12020-03-26 13:34:14 +00001877 input.Connect(layer->GetInputSlot(0));
1878
Teresa Charlin4bd9a742020-08-12 12:58:50 +01001879 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00001880}
1881
1882template<typename HalPolicy,
1883 typename HalOperation = typename HalPolicy::Operation,
1884 typename HalModel = typename HalPolicy::Model>
Sadik Armagan813f2302020-05-19 14:10:30 +01001885bool ConvertQuantized16BitLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
Kevin May42477c12020-03-26 13:34:14 +00001886{
1887 using HalOperand = typename HalPolicy::Operand;
1888
Sadik Armagan813f2302020-05-19 14:10:30 +01001889 ALOGV("HalPolicy::ConvertQuantized16BitLstm()");
Kevin May42477c12020-03-26 13:34:14 +00001890
1891 //Inputs:
1892 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
1893 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
1894 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1895 if (!input.IsValid())
1896 {
1897 return Fail("%s: Could not read input 0: input", __func__);
1898 }
1899
1900 //13: The previous cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape
1901 // [numBatches, outputSize] specifying the cell state from the previous time step of the LSTM cell.
1902 // It is quantized using a quantization range of -2^4, 2^4 * 32767/32768.
1903 LayerInputHandle previousCellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 13, model, data);
1904 if (!previousCellStateIn.IsValid())
1905 {
1906 return Fail("%s: Could not read input 13: previousCellStateIn", __func__);
1907 }
1908
1909 // 14: The previous output state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1910 // [numBathes, outputSize] specifying the output of the LSTM cell from previous time-step. Tensor
1911 // is quantized with a fixed quantization range of -1, 127/128.
1912 LayerInputHandle previousOutputIn = ConvertToLayerInputHandle<HalPolicy>(operation, 14, model, data);
1913 if (!previousOutputIn.IsValid())
1914 {
1915 return Fail("%s: Could not read input 14: previousOutputIn", __func__);
1916 }
1917
1918 // Get the input tensors:
1919 // 1: The input-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1920 // [outputSize, inputSize] specifying input-to-input part of weights for fully-connected layer inside the
1921 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1922 const ConstTensorPin inputToInputWeightsPin =
1923 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1924
1925 // 2: The input-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1926 // [outputSize, inputSize] specifying input-to-forget part of weights for fully-connected layer inside the
1927 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1928 const ConstTensorPin inputToForgetWeightsPin =
1929 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
1930
1931 // 3: The input-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1932 // [outputSize, inputSize] specifying input-to-cell part of weights for fully-connected layer inside the
1933 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1934 const ConstTensorPin inputToCellWeightsPin =
1935 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 3, model, data);
1936
1937 // 4: The input-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1938 // [outputSize, inputSize] specifying input-to-output part of weights for fully-connected layer inside the
1939 // LSTM cell. Quantization zero point and scale must be the same across all the weights.
1940 const ConstTensorPin inputToOutputWeightsPin =
1941 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 4, model, data);
1942
1943 // 5: The recurrent-to-input weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1944 // [outputSize, outputSize] specifying recurrent-to-input part of weights for fully-connected layer inside
1945 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1946 const ConstTensorPin recurrentToInputWeightsPin =
1947 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 5, model, data);
1948
1949 // 6: The recurrent-to-forget weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1950 // [outputSize, outputSize] specifying recurrent-to-forget part of weights for fully-connected layer inside
1951 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1952 const ConstTensorPin recurrentToForgetWeightsPin =
1953 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 6, model, data);
1954
1955 // 7: The recurrent-to-cell weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1956 // [outputSize, outputSize] specifying recurrent-to-cell part of weights for fully-connected layer inside
1957 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1958 const ConstTensorPin recurrentToCellWeightsPin =
1959 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 7, model, data);
1960
1961 // 8: The recurrent-to-output weights. A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape
1962 // [outputSize, outputSize] specifying recurrent-to-output part of weights for fully-connected layer inside
1963 // the LSTM cell. Quantization zero point and scale must be the same across all the weights.
1964 const ConstTensorPin recurrentToOutputWeightsPin =
1965 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 8, model, data);
1966
1967 // 9: The input gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the
1968 // bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1969 // of input and weights scales and zeroPoint equal to 0.
1970 const ConstTensorPin inputGateBiasPin =
1971 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 9, model, data);
1972
1973 // 10: The forget gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1974 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1975 // of input and weights scales and zeroPoint equal to 0.
1976 const ConstTensorPin forgetGateBiasPin =
1977 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 10, model, data);
1978
1979 // 11:The cell bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying the bias
1980 // for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product of input
1981 // and weights scales and zeroPoint equal to 0.
1982 const ConstTensorPin cellBiasPin =
1983 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 11, model, data);
1984
1985 // 12:The output gate bias. A 1-D tensor of type ANEURALNETWORKS_TENSOR_INT32 and shape [outputSize] specifying
1986 // the bias for the fully-connected layer inside the LSTM cell. Bias is quantized with scale being a product
1987 // of input and weights scales and zeroPoint equal to 0.
1988 const ConstTensorPin outputGateBiasPin =
1989 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 12, model, data);
1990
1991 if (!inputToInputWeightsPin.IsValid() ||
1992 !inputToForgetWeightsPin.IsValid() ||
1993 !inputToCellWeightsPin.IsValid() ||
1994 !inputToOutputWeightsPin.IsValid() ||
1995 !recurrentToInputWeightsPin.IsValid() ||
1996 !recurrentToForgetWeightsPin.IsValid() ||
1997 !recurrentToCellWeightsPin.IsValid() ||
1998 !recurrentToOutputWeightsPin.IsValid() ||
1999 !inputGateBiasPin.IsValid() ||
2000 !forgetGateBiasPin.IsValid() ||
2001 !cellBiasPin.IsValid() ||
2002 !outputGateBiasPin.IsValid())
2003 {
2004 return Fail("%s: Operation has invalid tensor inputs", __func__);
2005 }
2006
2007 // Outputs:
2008 // 0: The cell state: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT16_SYMM and shape [numBatches, outputSize]
2009 // which contains a cell state from the current time step. Tensor is quantized using a quantization range
2010 // of -2^4, 2^4 * 32767/32768.
2011 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 0, model);
2012 if (!cellStateOut)
2013 {
2014 return Fail("%s: Could not read output 0: cellStateOut", __func__);
2015 }
2016
2017 // 1: The output: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBathes, outputSize] which
2018 // contains the output value. Tensor is quantized with a fixed quantization range of -1, 127/128.
2019 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 1, model);
2020 if (!output)
2021 {
2022 return Fail("%s: Could not read output 1: output", __func__);
2023 }
2024
2025 // Inputs
2026 const TensorInfo& inputInfo = input.GetTensorInfo();
2027 const TensorInfo& previousCellStateInInfo = previousCellStateIn.GetTensorInfo();
2028 const TensorInfo& previousOutputInInfo = previousOutputIn.GetTensorInfo();
2029
2030 // Outputs
2031 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2032 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2033
2034 // Dynamic tensors currently not supported
2035 if (IsDynamicTensor(cellStateOutInfo) || IsDynamicTensor(outputInfo))
2036 {
2037 return Fail("%s: Dynamic output tensors are not supported", __func__);
2038 }
2039
2040 QuantizedLstmInputParams params;
2041
2042 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2043 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2044 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2045 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2046 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2047 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2048 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2049 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2050 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2051 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2052 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2053 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2054
2055 QuantizedLstmInputParamsInfo paramsInfo;
2056 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2057 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2058 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2059 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2060 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2061 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2062 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2063 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2064 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2065 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2066 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2067 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2068
2069 bool isSupported = false;
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002070 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2071 {
2072 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2073 IsQuantizedLstmSupported,
2074 data.m_Backends,
2075 isSupported,
2076 inputInfo,
2077 previousCellStateInInfo,
2078 previousOutputInInfo,
2079 cellStateOutInfo,
2080 outputInfo,
2081 paramsInfo);
2082 };
2083
2084 bool isDynamic = false;
2085 if (!IsDynamicTensor(cellStateOutInfo) &&
2086 !IsDynamicTensor(outputInfo))
2087 {
2088 validateFunc(outputInfo, isSupported);
2089 }
2090 else
2091 {
2092 isDynamic = true;
2093 isSupported = AreDynamicTensorsSupported();
2094 }
Kevin May42477c12020-03-26 13:34:14 +00002095
2096 if (!isSupported)
2097 {
2098 return false;
2099 }
2100
2101 IConnectableLayer* const layer = data.m_Network->AddQuantizedLstmLayer(params, "QuantizedLstm");
2102 input.Connect(layer->GetInputSlot(0));
2103 previousCellStateIn.Connect(layer->GetInputSlot(1));
2104 previousOutputIn.Connect(layer->GetInputSlot(2));
2105
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002106 if (!isDynamic)
2107 {
2108 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2109 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data));
2110 }
2111 else
2112 {
2113 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2114 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01002115 operation, 1, *layer, 1, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armaganbaa1f9f2020-09-03 10:57:43 +01002116 }
2117
Kevin May42477c12020-03-26 13:34:14 +00002118}
2119
2120template<typename HalPolicy,
2121 typename HalOperation = typename HalPolicy::Operation,
2122 typename HalModel = typename HalPolicy::Model>
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002123bool ConvertReduce(const HalOperation& operation,
2124 const HalModel& model,
2125 ConversionData& data,
2126 ReduceOperation reduceOperation)
2127{
2128 using HalOperand = typename HalPolicy::Operand;
2129 using HalOperandType = typename HalPolicy::OperandType;
2130
2131 armnn::ReduceDescriptor descriptor;
2132 descriptor.m_ReduceOperation = reduceOperation;
2133
2134 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2135 if (!input.IsValid())
2136 {
2137 return Fail("%s: Operation has invalid inputs", __func__);
2138 }
2139 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
2140
2141 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2142 if (!output)
2143 {
2144 return Fail("%s: Could not read output 0", __func__);
2145 }
2146 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2147
2148 const HalOperand* axisOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2149 if (!axisOperand)
2150 {
2151 return Fail("%s: Could not read input 1", __func__);
2152 }
2153 std::vector<int32_t> axis;
2154 if (!GetTensorInt32Values<HalPolicy>(*axisOperand, axis, model, data))
2155 {
2156 return Fail("%s: Input 1 has invalid values", __func__);
2157 }
2158
2159 // Convert the axis to unsigned int and remove duplicates.
2160 unsigned int rank = inputInfo.GetNumDimensions();
2161 std::set<unsigned int> uniqueAxis;
2162 std::transform(axis.begin(), axis.end(),
2163 std::inserter(uniqueAxis, uniqueAxis.begin()),
2164 [rank](int i) -> unsigned int { return (i + rank) % rank; });
2165 descriptor.m_vAxis.assign(uniqueAxis.begin(), uniqueAxis.end());
2166
2167 // Get the "keep dims" flag.
2168 if (!GetInputScalar<HalPolicy>(operation, 2, HalOperandType::BOOL, descriptor.m_KeepDims, model, data))
2169 {
2170 return Fail("%s: Could not read input 2", __func__);
2171 }
2172
2173 bool isSupported = false;
2174 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2175 {
2176 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2177 IsReduceSupported,
2178 data.m_Backends,
2179 isSupported,
2180 inputInfo,
2181 outputInfo,
2182 descriptor);
2183 };
2184
2185 if(!IsDynamicTensor(outputInfo))
2186 {
2187 validateFunc(outputInfo, isSupported);
2188 }
2189 else
2190 {
2191 isSupported = AreDynamicTensorsSupported();
2192 }
2193
2194 if (!isSupported)
2195 {
2196 return false;
2197 }
2198
2199 armnn::IConnectableLayer* const layer = data.m_Network->AddReduceLayer(descriptor);
Mike Kellye2d611e2021-10-14 12:35:58 +01002200 if (!layer)
2201 {
2202 return Fail("%s: Could not add the ReduceLayer", __func__);
2203 }
Teresa Charlin89cbb3a2021-02-11 21:00:47 +00002204 input.Connect(layer->GetInputSlot(0));
2205
2206 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
2207}
2208
2209template<typename HalPolicy,
2210 typename HalOperation = typename HalPolicy::Operation,
2211 typename HalModel = typename HalPolicy::Model>
Kevin May42477c12020-03-26 13:34:14 +00002212bool ConvertResize(const HalOperation& operation,
2213 const HalModel& model,
2214 ConversionData& data,
2215 ResizeMethod resizeMethod)
2216{
2217 using HalOperand = typename HalPolicy::Operand;
2218 using HalOperandType = typename HalPolicy::OperandType;
2219 ALOGV("HalPolicy::ConvertResize()");
2220 ALOGV("resizeMethod = %s", GetResizeMethodAsCString(resizeMethod));
2221
2222 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2223 if (!input.IsValid())
2224 {
2225 return Fail("%s: Could not read input 0", __func__);
2226 }
2227
2228 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2229 if (!output)
2230 {
2231 return Fail("%s: Could not read output 0", __func__);
2232 }
2233
2234 const TensorInfo& inputInfo = input.GetTensorInfo();
2235 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2236
Kevin May42477c12020-03-26 13:34:14 +00002237 ResizeDescriptor descriptor;
2238 descriptor.m_Method = resizeMethod;
2239 descriptor.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 3, model, data);
2240
2241 HalOperandType operandType1;
2242 HalOperandType operandType2;
2243
2244 if (!GetOperandType<HalPolicy>(operation, 1, model, operandType1) ||
2245 !GetOperandType<HalPolicy>(operation, 2, model, operandType2))
2246 {
2247 return Fail("%s: Operation has invalid inputs", __func__);
2248 }
2249
2250 if (operandType1 != operandType2)
2251 {
2252 return Fail("%s: Operation has invalid inputs. Type of input 1 and 2 should be the same", __func__);
2253 }
2254
2255 if (operandType1 == HalOperandType::INT32)
2256 {
2257 // Case 1: resizing by shape
2258 int32_t targetWidth = 0;
2259 int32_t targetHeight = 0;
2260
2261 if (!GetInputInt32<HalPolicy>(operation, 1, targetWidth, model, data) ||
2262 !GetInputInt32<HalPolicy>(operation, 2, targetHeight, model, data))
2263 {
2264 return Fail("%s: Operation has invalid inputs for resizing by shape", __func__);
2265 }
2266
2267 if (targetWidth < 0 || targetHeight < 0)
2268 {
2269 return Fail("%s: Operation has invalid inputs for resizing by shape. "
2270 "Target width/height cannot be < 0", __func__);
2271 }
2272
2273 descriptor.m_TargetWidth = static_cast<uint32_t>(targetWidth);
2274 descriptor.m_TargetHeight = static_cast<uint32_t>(targetHeight);
2275 }
2276 else if (operandType1 == HalOperandType::FLOAT32)
2277 {
2278 // Case 2: resizing by scale
2279 float widthScale = 1.0f;
2280 float heightScale = 1.0f;
2281
2282 if (!GetInputFloat32<HalPolicy>(operation, 1, widthScale, model, data) ||
2283 !GetInputFloat32<HalPolicy>(operation, 2, heightScale, model, data))
2284 {
2285 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2286 }
2287
2288 const TensorShape& inputShape = inputInfo.GetShape();
2289 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2290
2291 float width = inputShape[dataLayoutIndexed.GetWidthIndex()];
2292 float height = inputShape[dataLayoutIndexed.GetHeightIndex()];
2293
2294 descriptor.m_TargetWidth = std::floor(width * widthScale);
2295 descriptor.m_TargetHeight = std::floor(height * heightScale);
2296 }
2297 else if (operandType1 == HalOperandType::FLOAT16)
2298 {
2299 Half widthScale;
2300 Half heightScale;
2301
2302 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, widthScale, model, data) ||
2303 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::FLOAT16, heightScale, model, data))
2304 {
2305 return Fail("%s: Operation has invalid inputs for resizing by scale", __func__);
2306 }
2307
2308 const TensorShape& inputShape = inputInfo.GetShape();
2309 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
2310
2311 Half width = static_cast<Half>(inputShape[dataLayoutIndexed.GetWidthIndex()]);
2312 Half height = static_cast<Half>(inputShape[dataLayoutIndexed.GetHeightIndex()]);
2313
2314 descriptor.m_TargetWidth = std::floor(width * widthScale);
2315 descriptor.m_TargetHeight = std::floor(height * heightScale);
2316 }
2317 else
2318 {
2319 return Fail("%s: Operand has invalid data type for resizing by scale", __func__);
2320 }
2321
David Monahanf057e6f2020-06-22 09:55:23 +01002322 descriptor.m_AlignCorners = GetOptionalBool<HalPolicy>(operation, 4, model, data);
2323 descriptor.m_HalfPixelCenters = GetOptionalBool<HalPolicy>(operation, 5, model, data);
David Monahan51e0b132020-04-20 16:12:06 +01002324
Kevin May42477c12020-03-26 13:34:14 +00002325 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002326 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2327 {
2328 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2329 IsResizeSupported,
2330 data.m_Backends,
2331 isSupported,
2332 inputInfo,
2333 outputInfo,
2334 descriptor);
2335 };
2336
2337 if(IsDynamicTensor(outputInfo))
2338 {
2339 isSupported = AreDynamicTensorsSupported();
2340 }
2341 else
2342 {
2343 validateFunc(outputInfo, isSupported);
2344 }
Kevin May42477c12020-03-26 13:34:14 +00002345
2346 if (!isSupported)
2347 {
2348 return false;
2349 }
2350
2351 IConnectableLayer* layer = data.m_Network->AddResizeLayer(descriptor);
Mike Kellye2d611e2021-10-14 12:35:58 +01002352 if (!layer)
2353 {
2354 return Fail("%s: Could not add the ResizeLayer", __func__);
2355 }
Kevin May42477c12020-03-26 13:34:14 +00002356 input.Connect(layer->GetInputSlot(0));
2357
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002358 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002359}
2360
2361template<typename HalPolicy,
2362 typename HalOperation = typename HalPolicy::Operation,
2363 typename HalModel = typename HalPolicy::Model>
2364bool ConvertSpaceToDepth(const HalOperation& operation, const HalModel& model, ConversionData& data)
2365{
2366 using HalOperand = typename HalPolicy::Operand;
2367 using HalOperandType = typename HalPolicy::OperandType;
2368
2369 ALOGV("HalPolicy::ConvertSpaceToDepth()");
2370
2371 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2372 if (!input.IsValid() )
2373 {
2374 return Fail("%s: Operation has invalid inputs", __func__);
2375 }
2376
2377 const TensorInfo& inputInfo = input.GetTensorInfo();
2378 unsigned int rank = inputInfo.GetNumDimensions();
2379 if (rank != 4)
2380 {
2381 return Fail("%s: Only inputs with rank 4 are supported", __func__);
2382 }
2383
2384 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2385 if (!output)
2386 {
2387 return Fail("%s: Could not read output 0", __func__);
2388 }
2389
2390 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002391
2392 SpaceToDepthDescriptor desc;
2393
2394 GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_BlockSize, model, data);
2395
2396 if (desc.m_BlockSize <= 1)
2397 {
2398 return Fail("%s: Block size must be at least 1 in all dimensions");
2399 }
2400
2401 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 2, model, data);
2402
2403 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002404 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2405 {
2406 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2407 IsSpaceToDepthSupported,
2408 data.m_Backends,
2409 isSupported,
2410 inputInfo,
2411 outputInfo,
2412 desc);
2413 };
2414
2415 if(IsDynamicTensor(outputInfo))
2416 {
2417 isSupported = AreDynamicTensorsSupported();
2418 }
2419 else
2420 {
2421 validateFunc(outputInfo, isSupported);
2422 }
2423
Kevin May42477c12020-03-26 13:34:14 +00002424 if (!isSupported)
2425 {
2426 return false;
2427 }
2428
2429 IConnectableLayer* const layer = data.m_Network->AddSpaceToDepthLayer(desc);
Mike Kellye2d611e2021-10-14 12:35:58 +01002430 if (!layer)
2431 {
Mike Kelly1b46d132021-11-03 11:12:45 +00002432 return Fail("%s: Could not add the SpaceToDepthLayer", __func__);
Mike Kellye2d611e2021-10-14 12:35:58 +01002433 }
Kevin May42477c12020-03-26 13:34:14 +00002434 input.Connect(layer->GetInputSlot(0));
2435
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002436 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002437}
2438
2439template<typename HalPolicy,
2440 typename HalOperation = typename HalPolicy::Operation,
2441 typename HalModel = typename HalPolicy::Model>
2442bool ConvertSoftmax(const HalOperation& operation, const HalModel& model, ConversionData& data)
2443{
2444 using HalOperand = typename HalPolicy::Operand;
2445 using HalOperandType = typename HalPolicy::OperandType;
2446
2447 ALOGV("HalPolicy::ConvertSoftmax()");
2448
2449 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2450 if (!input.IsValid())
2451 {
2452 return Fail("%s: Operation has invalid inputs", __func__);
2453 }
2454
2455 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
2456 if (!outputOperand)
2457 {
2458 return Fail("%s: Operation has no outputs", __func__);
2459 }
2460
2461 const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
Kevin May42477c12020-03-26 13:34:14 +00002462
2463 SoftmaxDescriptor desc;
Teresa Charlinbf866e22020-08-09 23:55:01 +01002464 HalOperandType outputType = outputOperand->type;
2465
2466 // Read beta value
2467 if (outputType == HalOperandType::TENSOR_FLOAT16)
Kevin May42477c12020-03-26 13:34:14 +00002468 {
Teresa Charlinbf866e22020-08-09 23:55:01 +01002469 Half value;
2470
2471 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, value, model, data))
2472 {
2473 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2474 }
2475
2476 desc.m_Beta = static_cast<float>(value);
2477 }
2478 else
2479 {
2480 if (!GetInputFloat32<HalPolicy>(operation, 1, desc.m_Beta, model, data))
2481 {
2482 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
2483 }
Kevin May42477c12020-03-26 13:34:14 +00002484 }
2485
2486 if (operation.inputs.size() > 2 && !GetInputScalar<HalPolicy>(operation,
Teresa Charlinbf866e22020-08-09 23:55:01 +01002487 2,
2488 HalOperandType::INT32,
2489 desc.m_Axis,
2490 model,
2491 data))
Kevin May42477c12020-03-26 13:34:14 +00002492 {
2493 return Fail("%s: Operation has invalid inputs", __func__);
2494 }
2495
Kevin May42477c12020-03-26 13:34:14 +00002496 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002497 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2498 {
2499 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2500 IsSoftmaxSupported,
2501 data.m_Backends,
2502 isSupported,
2503 input.GetTensorInfo(),
2504 outputInfo,
2505 desc);
2506 };
2507
2508 if(IsDynamicTensor(outputInfo))
2509 {
2510 isSupported = AreDynamicTensorsSupported();
2511 }
2512 else
2513 {
2514 validateFunc(outputInfo, isSupported);
2515 }
2516
Kevin May42477c12020-03-26 13:34:14 +00002517 if (!isSupported)
2518 {
2519 return false;
2520 }
2521
2522 IConnectableLayer* layer = data.m_Network->AddSoftmaxLayer(desc);
Mike Kellye2d611e2021-10-14 12:35:58 +01002523 if (!layer)
2524 {
2525 return Fail("%s: Could not add the SoftmaxLayer", __func__);
2526 }
Kevin May42477c12020-03-26 13:34:14 +00002527 input.Connect(layer->GetInputSlot(0));
2528
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002529 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
Kevin May42477c12020-03-26 13:34:14 +00002530}
2531
2532template<typename HalPolicy,
2533 typename HalOperation = typename HalPolicy::Operation,
2534 typename HalModel = typename HalPolicy::Model>
2535bool ConvertLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
2536{
2537 using HalOperand = typename HalPolicy::Operand;
2538 using HalOperandType = typename HalPolicy::OperandType;
2539
2540 ALOGV("HalPolicy::ConvertLstm()");
2541
2542 // Inputs:
2543 // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where
2544 // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input.
2545 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2546 if (!input.IsValid())
2547 {
2548 return Fail("%s: Could not read input 0: input", __func__);
2549 }
2550 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2551 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
2552 if (!outputStateIn.IsValid())
2553 {
2554 return Fail("%s: Could not read input 18: outputStateIn", __func__);
2555 }
2556 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2557 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
2558 if (!cellStateIn.IsValid())
2559 {
2560 return Fail("%s: Could not read input 19: cellStateIn", __func__);
2561 }
2562
2563 // Get the mandatory input tensors:
2564 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2565 // [num_units, input_size].
2566 const ConstTensorPin inputToForgetWeightsPin =
2567 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
2568 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2569 // [num_units, input_size].
2570 const ConstTensorPin inputToCellWeightsPin =
2571 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
2572 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2573 // [num_units, input_size].
2574 const ConstTensorPin inputToOutputWeightsPin =
2575 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
2576 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2577 // [num_units, output_size].
2578 const ConstTensorPin recurrentToForgetWeightsPin =
2579 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
2580 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2581 // [num_units, output_size].
2582 const ConstTensorPin recurrentToCellWeightsPin =
2583 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
2584 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2585 // [num_units, output_size].
2586 const ConstTensorPin recurrentToOutputWeightsPin =
2587 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
2588 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2589 const ConstTensorPin forgetGateBiasPin =
2590 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
2591 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2592 const ConstTensorPin cellBiasPin =
2593 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
2594 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2595 const ConstTensorPin outputGateBiasPin =
2596 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
2597
2598 if (!inputToForgetWeightsPin.IsValid() ||
2599 !inputToCellWeightsPin.IsValid() ||
2600 !inputToOutputWeightsPin.IsValid() ||
2601 !recurrentToForgetWeightsPin.IsValid() ||
2602 !recurrentToCellWeightsPin.IsValid() ||
2603 !recurrentToOutputWeightsPin.IsValid() ||
2604 !forgetGateBiasPin.IsValid() ||
2605 !cellBiasPin.IsValid() ||
2606 !outputGateBiasPin.IsValid())
2607 {
2608 return Fail("%s: Operation has invalid tensor inputs", __func__);
2609 }
2610
2611 // Get the optional input tensors:
2612 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2613 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
2614 const ConstTensorPin inputToInputWeightsPin =
2615 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
2616 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2617 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
2618 // “num_units”), or the second dimension of the “projection_weights”, if defined.
2619 const ConstTensorPin recurrentToInputWeightsPin =
2620 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
2621 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2622 const ConstTensorPin cellToInputWeightsPin =
2623 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
2624 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2625 const ConstTensorPin cellToForgetWeightsPin =
2626 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
2627 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2628 const ConstTensorPin cellToOutputWeightsPin =
2629 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
2630 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units].
2631 const ConstTensorPin inputGateBiasPin =
2632 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2633 12,
2634 model,
2635 data,
2636 g_DontPermute,
2637 nullptr,
2638 true);
2639
2640 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
2641 // [output_size, num_units].
2642 const ConstTensorPin projectionWeightsPin =
2643 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
2644 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size].
2645 const ConstTensorPin projectionBiasPin =
2646 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2647 17,
2648 model,
2649 data,
2650 g_DontPermute,
2651 nullptr,
2652 true);
2653
2654 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
2655 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
2656 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
2657 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
2658 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
2659 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
2660 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
2661 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
2662 {
2663 return Fail("%s: Operation has invalid tensor inputs", __func__);
2664 }
2665
2666 // Get the mandatory input scalars (actually 1-D tensors of size 1):
2667 // 20: The activation function: A value indicating the activation function:
2668 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
2669 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
2670 // If set to 0.0 then clipping is disabled.
2671 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
2672 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
Cathal Corbett3fe3a542022-05-25 09:35:37 +01002673 ActivationFn activation = ActivationFn::kActivationNone;
Kevin May42477c12020-03-26 13:34:14 +00002674 float cellClip;
2675 float projClip;
2676 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
2677 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
2678 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
2679 {
2680 return Fail("%s: Operation has invalid scalar inputs", __func__);
2681 }
2682
2683 // Get the normalization tensors
2684 // 23: The input layer normalization weights. A 1-D tensor of shape [num_units].
2685 // Used to rescale normalized inputs to activation at input gate.
2686 const ConstTensorPin inputLayerNormWeightsPin
2687 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 23, true));
2688
2689 // 24: The forget layer normalization weights. A 1-D tensor of shape [num_units].
2690 // Used to rescale normalized inputs to activation at forget gate.
2691 const ConstTensorPin forgetLayerNormWeightsPin =
2692 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2693 24,
2694 model,
2695 data,
2696 g_DontPermute,
2697 nullptr,
2698 true);
2699
2700 // 25: The cell layer normalization weights. A 1-D tensor of shape [num_units].
2701 // Used to rescale normalized inputs to activation at cell gate.
2702 const ConstTensorPin cellLayerNormWeightsPin =
2703 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2704 25,
2705 model,
2706 data,
2707 g_DontPermute,
2708 nullptr,
2709 true);
2710
2711 // 26: The output layer normalization weights. A 1-D tensor of shape [num_units].
2712 // Used to rescale normalized inputs to activation at output gate.
2713 const ConstTensorPin outputLayerNormWeightsPin =
2714 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
2715 26,
2716 model,
2717 data,
2718 g_DontPermute,
2719 nullptr,
2720 true);
2721
2722 // Outputs:
2723 // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4]
2724 // with CIFG, or [batch_size, num_units * 3] without CIFG.
2725 const HalOperand* scratchBuffer = GetOutputOperand<HalPolicy>(operation, 0, model);
2726 if (!scratchBuffer)
2727 {
2728 return Fail("%s: Could not read output 0: scratchBuffer", __func__);
2729 }
2730 // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size].
2731 const HalOperand* outputStateOut = GetOutputOperand<HalPolicy>(operation, 1, model);
2732 if (!outputStateOut)
2733 {
2734 return Fail("%s: Could not read output 1: outputStateOut", __func__);
2735 }
2736 // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units].
2737 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 2, model);
2738 if (!cellStateOut)
2739 {
2740 return Fail("%s: Could not read output 2: cellStateOut", __func__);
2741 }
2742 // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is
2743 // effectively the same as the current “output state (out)” value.
2744 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 3, model);
2745 if (!output)
2746 {
2747 return Fail("%s: Could not read output 3: output", __func__);
2748 }
2749
2750 // set the params structure for the AddLstmLayer call
2751 LstmInputParams params;
2752 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
2753 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
2754 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
2755 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
2756 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
2757 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
2758 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
2759 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
2760 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
2761 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
2762 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
2763 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
2764 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
2765 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
2766 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
2767 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
2768 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
2769 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
2770 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
2771 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
2772 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
2773
2774 // set the layer descriptor
2775 LstmDescriptor desc;
2776 desc.m_ActivationFunc = activation;
2777 desc.m_ClippingThresCell = cellClip;
2778 desc.m_ClippingThresProj = projClip;
2779 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
2780 params.m_RecurrentToInputWeights == nullptr ||
2781 params.m_InputGateBias == nullptr);
2782 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
2783 params.m_CellToOutputWeights != nullptr);
2784 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
2785 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
2786 params.m_ForgetLayerNormWeights != nullptr ||
2787 params.m_CellLayerNormWeights != nullptr ||
2788 params.m_OutputLayerNormWeights != nullptr);
2789
2790 // validate the optional input groups
2791 if (desc.m_CifgEnabled &&
2792 (params.m_InputToInputWeights != nullptr ||
2793 params.m_RecurrentToInputWeights != nullptr ||
2794 params.m_InputGateBias != nullptr))
2795 {
2796 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
2797 " and input gate bias must be provided", __func__);
2798 }
2799
2800 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
2801 {
2802 return Fail("%s: projection bias should not be provided without projection weights", __func__);
2803 }
2804
2805 if (desc.m_PeepholeEnabled &&
2806 (params.m_CellToForgetWeights == nullptr ||
2807 params.m_CellToOutputWeights == nullptr ||
2808 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
2809 {
2810 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
2811 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
2812 }
2813
2814 if (desc.m_LayerNormEnabled &&
2815 (params.m_ForgetLayerNormWeights == nullptr ||
2816 params.m_CellLayerNormWeights == nullptr ||
2817 params.m_OutputLayerNormWeights == nullptr ||
2818 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
2819 {
2820 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
2821 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
2822 }
2823
2824 // Check if the layer is supported
2825 // Inputs
2826 const TensorInfo& inputInfo = input.GetTensorInfo();
2827 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
2828 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
2829
2830 // Outputs
2831 const TensorInfo& scratchBufferInfo = GetTensorInfoForOperand(*scratchBuffer);
2832 const TensorInfo& outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
2833 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
2834 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
2835
Kevin May42477c12020-03-26 13:34:14 +00002836 // Basic parameters
2837 LstmInputParamsInfo paramsInfo;
2838 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
2839 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
2840 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
2841 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
2842 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
2843 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
2844 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
2845 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
2846 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
2847
2848 // Optional parameters
2849 if (!desc.m_CifgEnabled)
2850 {
2851 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
2852 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
2853 if (params.m_CellToInputWeights != nullptr)
2854 {
2855 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
2856 }
2857 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
2858 }
2859
2860 if (desc.m_ProjectionEnabled)
2861 {
2862 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
2863 if (params.m_ProjectionBias != nullptr)
2864 {
2865 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
2866 }
2867 }
2868
2869 if (desc.m_PeepholeEnabled)
2870 {
2871 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
2872 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
2873 }
2874
2875 if (desc.m_LayerNormEnabled)
2876 {
2877 if(!desc.m_CifgEnabled)
2878 {
2879 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
2880 }
2881 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
2882 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
2883 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
2884 }
2885
2886 bool isSupported = false;
Sadik Armagandbda4b72020-09-03 11:33:07 +01002887 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
2888 {
2889 FORWARD_LAYER_SUPPORT_FUNC(__func__,
2890 IsLstmSupported,
2891 data.m_Backends,
2892 isSupported,
2893 inputInfo,
2894 outputStateInInfo,
2895 cellStateInInfo,
2896 scratchBufferInfo,
2897 outputStateOutInfo,
2898 cellStateOutInfo,
2899 outputInfo,
2900 desc,
2901 paramsInfo);
2902 };
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002903
Sadik Armagandbda4b72020-09-03 11:33:07 +01002904 bool isDynamic = false;
2905 if (!IsDynamicTensor(outputStateOutInfo) &&
2906 !IsDynamicTensor(scratchBufferInfo) &&
2907 !IsDynamicTensor(cellStateOutInfo) &&
2908 !IsDynamicTensor(outputInfo))
2909 {
2910 validateFunc(outputInfo, isSupported);
2911 }
2912 else
2913 {
2914 isDynamic = true;
2915 isSupported = AreDynamicTensorsSupported();
2916 }
Teresa Charlin4bd9a742020-08-12 12:58:50 +01002917
Kevin May42477c12020-03-26 13:34:14 +00002918 if (!isSupported)
2919 {
2920 return false;
2921 }
2922
2923 // Add the layer
2924 IConnectableLayer* layer = data.m_Network->AddLstmLayer(desc, params, "Lstm");
2925
2926 input.Connect(layer->GetInputSlot(0));
2927 outputStateIn.Connect(layer->GetInputSlot(1));
2928 cellStateIn.Connect(layer->GetInputSlot(2));
2929
Sadik Armagandbda4b72020-09-03 11:33:07 +01002930 if (!isDynamic)
2931 {
2932 return (
2933 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2934 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2935 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2936 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 3, *layer, 3, model, data));
2937 }
2938 else
2939 {
2940 return (
2941 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 0, model, data) &&
2942 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
2943 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data) &&
2944 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +01002945 operation, 3, *layer, 3, model, data, nullptr, validateFunc, ActivationFn::kActivationNone, true));
Sadik Armagandbda4b72020-09-03 11:33:07 +01002946 }
2947
Kevin May42477c12020-03-26 13:34:14 +00002948}
2949
2950template<typename HalPolicy,
2951 typename HalOperation = typename HalPolicy::Operation,
2952 typename HalModel = typename HalPolicy::Model>
2953bool ConvertTransposeConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
2954{
2955 using HalOperand = typename HalPolicy::Operand;
2956 using HalOperandType = typename HalPolicy::OperandType;
2957
2958 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
2959
2960 if (!input.IsValid())
2961 {
2962 return Fail("%s: Operation has invalid inputs", __func__);
2963 }
2964
2965 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
2966
2967 if (!output)
2968 {
2969 return Fail("%s: Could not read output 0", __func__);
2970 }
2971
2972 const TensorInfo& inputInfo = input.GetTensorInfo();
2973 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
Kevin May42477c12020-03-26 13:34:14 +00002974
2975 // ArmNN does not currently support non-fixed weights or bias
2976 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
2977 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
2978
2979 if (weightsOperand == nullptr)
2980 {
2981 return Fail("%s: Operand is invalid", __func__);
2982 }
2983 TransposeConvolution2dDescriptor desc;
2984 desc.m_DataLayout = DataLayout::NHWC;
2985
2986 // Determine whether padding is implicit or explicit
2987 bool implicitPadding = operation.inputs.size() == 9;
2988
2989 if (implicitPadding )
2990 {
2991 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
2992 }
2993 else
2994 {
2995 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
2996 }
2997
2998 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
2999 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
3000 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
3001
3002 const PermutationVector OHWIToOIHW = {0, 2, 3, 1};
3003
3004 // The shape of the weight is [depth_out, filter_height, filter_width, depth_in].
3005 // We have to permute it to OIHW if the data layout is NCHW.
3006 const ConstTensorPin weightsPin = (desc.m_DataLayout == DataLayout::NCHW) ?
3007 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1,
3008 model, data, OHWIToOIHW) :
3009 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
3010
3011 // Bias is a 1D tensor
3012 const ConstTensorPin biasPin =
3013 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
3014
3015 if (!weightsPin.IsValid())
3016 {
3017 return Fail("%s: Operation has invalid weights", __func__);
3018 }
3019
3020 if (!biasPin.IsValid())
3021 {
3022 return Fail("%s: Operation has invalid biases", __func__);
3023 }
3024
3025 ConstTensor weights = weightsPin.GetConstTensor();
3026 ConstTensor bias = biasPin.GetConstTensor();
3027 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
3028
3029 ActivationFn activation;
3030
3031 if (implicitPadding)
3032 {
3033 int32_t strideX{0};
3034 int32_t strideY{0};
3035 int32_t padLeft{0};
3036 int32_t padRight{0};
3037 int32_t padTop{0};
3038 int32_t padBottom{0};
3039
3040 android::nn::PaddingScheme paddingScheme;
3041 if (!GetInputPaddingScheme<HalPolicy>(operation, 4, paddingScheme, model, data) ||
3042 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, strideX, model, data) ||
3043 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, strideY, model, data) ||
3044 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data))
3045 {
3046 return Fail("%s: Operation has invalid inputs (implicit padding)", __func__);
3047 }
3048
3049 const uint32_t kernelX = weights.GetShape()[widthIndex];
3050 const uint32_t kernelY = weights.GetShape()[heightIndex];
Kevin May42477c12020-03-26 13:34:14 +00003051
Colm Donelan8f3d33e2020-07-06 15:41:43 +01003052 // If output shape has been specified as a parameter then extract it and make it available.
3053 const HalOperand* outputShapeOperand = GetInputOperand<HalPolicy>(operation, 3, model, false);
3054 std::vector<int32_t> outputShape;
3055 if ((outputShapeOperand) && (GetTensorInt32Values<HalPolicy>(*outputShapeOperand, outputShape, model, data)))
3056 {
3057 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
3058 for (int dimension : outputShape)
3059 {
3060 desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
3061 }
3062 desc.m_OutputShapeEnabled = true;
3063 }
3064
Finn Williams8fe50c62020-10-09 15:52:57 +01003065 uint32_t outputX;
3066 uint32_t outputY;
3067
3068 if (IsDynamicTensor(outputInfo))
3069 {
3070 if (outputShape.size() == 0)
3071 {
3072 return Fail("%s: Padding sizes cannot be inferred", __func__);
3073 }
3074
3075 outputX = outputShape[widthIndex];
3076 outputY = outputShape[heightIndex];
3077 }
3078 else
3079 {
3080 outputX = outputInfo.GetShape()[widthIndex];
3081 outputY = outputInfo.GetShape()[heightIndex];
3082 }
3083
3084 CalcPaddingTransposeConv(outputX, kernelX, strideX, padLeft, padRight, paddingScheme);
3085 CalcPaddingTransposeConv(outputY, kernelY, strideY, padTop, padBottom, paddingScheme);
3086
3087 // NOTE: The Android NN API allows for negative padding values in TransposeConv2d,
3088 // but Arm NN only supports values >= 0
3089 if (padLeft < 0 || padRight < 0 || padTop < 0 || padBottom < 0)
3090 {
3091 return Fail("%s: Negative padding values are not supported", __func__);
3092 }
3093
Matthew Sloyan9b088d92020-09-14 15:12:55 +01003094 desc.m_StrideX = armnn::numeric_cast<uint32_t>(strideX);
3095 desc.m_StrideY = armnn::numeric_cast<uint32_t>(strideY);
3096 desc.m_PadLeft = armnn::numeric_cast<uint32_t>(padLeft);
3097 desc.m_PadRight = armnn::numeric_cast<uint32_t>(padRight);
3098 desc.m_PadTop = armnn::numeric_cast<uint32_t>(padTop);
3099 desc.m_PadBottom = armnn::numeric_cast<uint32_t>(padBottom);
Kevin May42477c12020-03-26 13:34:14 +00003100 }
3101 else if (operation.inputs.size() == 11)
3102 {
3103 // explicit padding
3104 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
3105 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
3106 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
3107 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
3108 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
3109 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
3110 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
3111 {
3112 return Fail("%s: Operation has invalid inputs (explicit padding)", __func__);
3113 }
3114 }
3115 else
3116 {
3117 return Fail("%s: Unsupported number of operation inputs", __func__);
3118 }
3119
3120 desc.m_BiasEnabled = true;
3121 Optional<TensorInfo> biases(bias.GetInfo());
3122
3123 bool isSupported = false;
Teresa Charlin4bd9a742020-08-12 12:58:50 +01003124 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
3125 {
3126 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3127 IsTransposeConvolution2dSupported,
3128 data.m_Backends,
3129 isSupported,
3130 inputInfo,
3131 outputInfo,
3132 desc,
3133 weights.GetInfo(),
3134 biases);
3135 };
3136
3137 if(IsDynamicTensor(outputInfo))
3138 {
3139 isSupported = AreDynamicTensorsSupported();
3140 }
3141 else
3142 {
3143 validateFunc(outputInfo, isSupported);
3144 }
Kevin May42477c12020-03-26 13:34:14 +00003145 if (!isSupported)
3146 {
3147 return false;
3148 }
3149
3150 IConnectableLayer* startLayer =
3151 data.m_Network->AddTransposeConvolution2dLayer(desc, weights, Optional<ConstTensor>(bias));
3152 if (!startLayer)
3153 {
3154 return Fail("%s: AddTransposeConvolution2dLayer failed", __func__);
3155 }
3156
Kevin May42477c12020-03-26 13:34:14 +00003157 input.Connect(startLayer->GetInputSlot(0));
3158
Kevin Mayfcf2a152020-09-08 16:06:32 +01003159 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *startLayer, model,
3160 data, nullptr, validateFunc, activation);
Kevin May42477c12020-03-26 13:34:14 +00003161}
3162
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003163template<typename HalPolicy,
3164 typename HalOperation = typename HalPolicy::Operation,
3165 typename HalModel = typename HalPolicy::Model>
3166bool ConvertUnidirectionalSequenceLstm(const HalOperation& operation,
3167 const HalModel& model,
3168 ConversionData& data)
3169{
3170 using HalOperand = typename HalPolicy::Operand;
3171 using HalOperandType = typename HalPolicy::OperandType;
3172
3173 ALOGV("HalPolicy::ConvertUnidirectionalSequenceLstm()");
3174
3175 // Determine if input OperandType is ANEURALNETWORKS_TENSOR_FLOAT 32 or 16
3176 HalOperandType inputType;
3177 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
3178 {
3179 return Fail("%s: Operation has invalid inputs", __func__);
3180 }
3181
3182 // Inputs:
3183 // 0: The input: A 3-D tensor of shape: If time-major: [max_time, batch_size, input_size] If batch-major:
3184 // [batch_size, max_time, input_size] where “max_time” is the number of timesteps (sequence length), “batch_size”
3185 // corresponds to the batching dimension, and “input_size” is the size of the input.
3186 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
3187 if (!input.IsValid())
3188 {
3189 return Fail("%s: Could not read input 0: input", __func__);
3190 }
3191 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [batch_size, output_size].
3192 LayerInputHandle outputStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
3193 if (!outputStateIn.IsValid())
3194 {
3195 return Fail("%s: Could not read input 18: outputStateIn", __func__);
3196 }
3197 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [batch_size, num_units].
3198 LayerInputHandle cellStateIn = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
3199 if (!cellStateIn.IsValid())
3200 {
3201 return Fail("%s: Could not read input 19: cellStateIn", __func__);
3202 }
3203
3204 // Get the mandatory input tensors:
3205 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3206 // [num_units, input_size].
3207 const ConstTensorPin inputToForgetWeightsPin =
3208 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 2));
3209 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3210 // [num_units, input_size].
3211 const ConstTensorPin inputToCellWeightsPin =
3212 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 3));
3213 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3214 // [num_units, input_size].
3215 const ConstTensorPin inputToOutputWeightsPin =
3216 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 4));
3217 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3218 // [num_units, output_size].
3219 const ConstTensorPin recurrentToForgetWeightsPin =
3220 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 6));
3221 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape
3222 // [num_units, output_size].
3223 const ConstTensorPin recurrentToCellWeightsPin =
3224 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 7));
3225 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3226 // [num_units, output_size].
3227 const ConstTensorPin recurrentToOutputWeightsPin =
3228 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 8));
3229 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3230 const ConstTensorPin forgetGateBiasPin =
3231 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
3232 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3233 const ConstTensorPin cellBiasPin =
3234 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
3235 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3236 const ConstTensorPin outputGateBiasPin =
3237 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
3238
3239 if (!inputToForgetWeightsPin.IsValid() ||
3240 !inputToCellWeightsPin.IsValid() ||
3241 !inputToOutputWeightsPin.IsValid() ||
3242 !recurrentToForgetWeightsPin.IsValid() ||
3243 !recurrentToCellWeightsPin.IsValid() ||
3244 !recurrentToOutputWeightsPin.IsValid() ||
3245 !forgetGateBiasPin.IsValid() ||
3246 !cellBiasPin.IsValid() ||
3247 !outputGateBiasPin.IsValid())
3248 {
3249 return Fail("%s: Operation has invalid tensor inputs", __func__);
3250 }
3251
3252 // Get the optional input tensors:
3253 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3254 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
3255 const ConstTensorPin inputToInputWeightsPin =
3256 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 1, true));
3257 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3258 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
3259 // “num_units”), or the second dimension of the “projection_weights”, if defined.
3260 const ConstTensorPin recurrentToInputWeightsPin =
3261 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 5, true));
3262 // 09: The cell-to-input weights: Optional.
3263 // A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3264 const ConstTensorPin cellToInputWeightsPin =
3265 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 9, true));
3266 // 10: The cell-to-forget weights: Optional.
3267 // A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3268 const ConstTensorPin cellToForgetWeightsPin =
3269 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 10, true));
3270 // 11: The cell-to-output weights: Optional.
3271 // A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3272 const ConstTensorPin cellToOutputWeightsPin =
3273 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 11, true));
3274 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [num_units].
3275 const ConstTensorPin inputGateBiasPin =
3276 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3277 12,
3278 model,
3279 data,
3280 g_DontPermute,
3281 nullptr,
3282 true);
3283
3284 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape
3285 // [output_size, num_units].
3286 const ConstTensorPin projectionWeightsPin =
3287 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 16, true));
3288 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16, of shape [output_size].
3289 const ConstTensorPin projectionBiasPin =
3290 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3291 17,
3292 model,
3293 data,
3294 g_DontPermute,
3295 nullptr,
3296 true);
3297
3298 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) ||
3299 (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional()) ||
3300 (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional()) ||
3301 (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional()) ||
3302 (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional()) ||
3303 (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional()) ||
3304 (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional()) ||
3305 (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
3306 {
3307 return Fail("%s: Operation has invalid tensor inputs", __func__);
3308 }
3309
3310 // Get the mandatory input scalars (actually 1-D tensors of size 1):
3311 // 20: The activation function: A value indicating the activation function:
3312 // 0: None; 1: Relu; 3: Relu6; 4: Tanh; 6: Sigmoid.
3313 // 21: The clipping threshold: for the cell state, such that values are bound within [-cell_clip, cell_clip].
3314 // If set to 0.0 then clipping is disabled.
3315 // 22: The clipping threshold: for the output from the projection layer, such that values are bound within
3316 // [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
3317 // Determine data type of input tensor
Cathal Corbett3fe3a542022-05-25 09:35:37 +01003318 ActivationFn activation = ActivationFn::kActivationNone;
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003319 LstmDescriptor desc;
3320
3321 if (inputType == HalOperandType::TENSOR_FLOAT32)
3322 {
3323 float cellClip;
3324 float projClip;
3325
3326 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
3327 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT32, cellClip, model, data) ||
3328 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT32, projClip, model, data))
3329 {
3330 return Fail("%s: Operation has invalid scalar inputs", __func__);
3331 }
3332
3333 desc.m_ClippingThresCell = cellClip;
3334 desc.m_ClippingThresProj = projClip;
3335 }
3336
3337 if (inputType == HalOperandType::TENSOR_FLOAT16)
3338 {
3339 Half cellClip;
3340 Half projClip;
3341
3342 if (!GetInputActivationFunctionFromTensor<HalPolicy>(operation, 20, activation, model, data) ||
3343 !GetInputScalar<HalPolicy>(operation, 21, HalOperandType::FLOAT16, cellClip, model, data) ||
3344 !GetInputScalar<HalPolicy>(operation, 22, HalOperandType::FLOAT16, projClip, model, data))
3345 {
3346 return Fail("%s: Operation has invalid scalar inputs", __func__);
3347 }
3348
3349 desc.m_ClippingThresCell = cellClip;
3350 desc.m_ClippingThresProj = projClip;
3351 }
3352
3353 // Determine if time-major or batch-major.
3354 // 23: Time-major if true, batch-major if false.
3355 bool isTimeMajor = GetOptionalBool<HalPolicy>(operation, 23, model, data);
3356
3357 // Get the normalization tensors
3358 // 24: The input layer normalization weights. A 1-D tensor of shape [num_units].
3359 // Used to rescale normalized inputs to activation at input gate.
3360 const ConstTensorPin inputLayerNormWeightsPin
3361 (DequantizeAndMakeConstTensorPin<HalPolicy>(operation, model, data, 24, true));
3362
3363 // 25: The forget layer normalization weights. A 1-D tensor of shape [num_units].
3364 // Used to rescale normalized inputs to activation at forget gate.
3365 const ConstTensorPin forgetLayerNormWeightsPin =
3366 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3367 25,
3368 model,
3369 data,
3370 g_DontPermute,
3371 nullptr,
3372 true);
3373
3374 // 26: The cell layer normalization weights. A 1-D tensor of shape [num_units].
3375 // Used to rescale normalized inputs to activation at cell gate.
3376 const ConstTensorPin cellLayerNormWeightsPin =
3377 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3378 26,
3379 model,
3380 data,
3381 g_DontPermute,
3382 nullptr,
3383 true);
3384
3385 // 27: The output layer normalization weights. A 1-D tensor of shape [num_units].
3386 // Used to rescale normalized inputs to activation at output gate.
3387 const ConstTensorPin outputLayerNormWeightsPin =
3388 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
3389 27,
3390 model,
3391 data,
3392 g_DontPermute,
3393 nullptr,
3394 true);
3395
3396 // Outputs:
3397 // 00: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32/16. Shape: if time-major:
3398 // [max_time, batch_size, output_size] If batch-major: [batch_size, max_time, output_size]
3399 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
3400 if (!output)
3401 {
3402 return Fail("%s: Could not read output: ", __func__);
3403 }
3404
3405 //
3406 // 01 & 02:
3407 // hiddenStateOut and cellStateOut are not currently supported by our android versioning.
3408 //
3409
3410 // set the params structure for the AddLstmLayer call
3411 LstmInputParams params;
3412 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
3413 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
3414 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
3415 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
3416 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
3417 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
3418 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
3419 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
3420 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
3421 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
3422 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
3423 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
3424 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
3425 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
3426 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
3427 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
3428 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
3429 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
3430 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
3431 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
3432 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
3433
3434 // set the layer descriptor
3435 desc.m_ActivationFunc = activation;
3436 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
3437 params.m_RecurrentToInputWeights == nullptr ||
3438 params.m_InputGateBias == nullptr);
3439 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
3440 params.m_CellToOutputWeights != nullptr);
3441 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
3442 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
3443 params.m_ForgetLayerNormWeights != nullptr ||
3444 params.m_CellLayerNormWeights != nullptr ||
3445 params.m_OutputLayerNormWeights != nullptr);
3446 desc.m_TimeMajor = isTimeMajor;
3447
3448 // validate the optional input groups
3449 if (desc.m_CifgEnabled &&
3450 (params.m_InputToInputWeights != nullptr ||
3451 params.m_RecurrentToInputWeights != nullptr ||
3452 params.m_InputGateBias != nullptr))
3453 {
3454 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
3455 " and input gate bias must be provided", __func__);
3456 }
3457
3458 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
3459 {
3460 return Fail("%s: projection bias should not be provided without projection weights", __func__);
3461 }
3462
3463 if (desc.m_PeepholeEnabled &&
3464 (params.m_CellToForgetWeights == nullptr ||
3465 params.m_CellToOutputWeights == nullptr ||
3466 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
3467 {
3468 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
3469 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
3470 }
3471
3472 if (desc.m_LayerNormEnabled &&
3473 (params.m_ForgetLayerNormWeights == nullptr ||
3474 params.m_CellLayerNormWeights == nullptr ||
3475 params.m_OutputLayerNormWeights == nullptr ||
3476 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
3477 {
3478 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
3479 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
3480 }
3481
3482 // Check if the layer is supported
3483 // Inputs
3484 const TensorInfo& inputInfo = input.GetTensorInfo();
3485 const TensorInfo& outputStateInInfo = outputStateIn.GetTensorInfo();
3486 const TensorInfo& cellStateInInfo = cellStateIn.GetTensorInfo();
3487
3488 // Outputs
3489 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
3490
Mike Kelly0ae102a2022-04-25 16:18:57 +01003491 unsigned int batchSize = inputInfo.GetShape()[0];
3492 unsigned int outputSize = outputInfo.GetShape()[2];
3493 unsigned int numUnits = cellStateInInfo.GetShape()[1];
3494
3495 armnn::DataType dataType = inputInfo.GetDataType();
3496 float qScale = inputInfo.GetQuantizationScale();
3497 int qOffset = inputInfo.GetQuantizationOffset();
3498
3499 armnn::TensorInfo cellStateOutInfo({batchSize, numUnits}, cellStateInInfo.GetDataType(),
3500 cellStateInInfo.GetQuantizationScale(), cellStateInInfo.GetQuantizationOffset());
3501 armnn::TensorInfo outputStateOutInfo({batchSize, outputSize}, dataType, qScale, qOffset);
3502
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003503 // Basic parameters
3504 LstmInputParamsInfo paramsInfo;
3505 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
3506 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
3507 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
3508 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
3509 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
3510 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
3511 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
3512 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
3513 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
3514
3515 // Optional parameters
3516 if (!desc.m_CifgEnabled)
3517 {
3518 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
3519 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
3520 if (params.m_CellToInputWeights != nullptr)
3521 {
3522 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
3523 }
3524 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
3525 }
3526
3527 if (desc.m_ProjectionEnabled)
3528 {
3529 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
3530 if (params.m_ProjectionBias != nullptr)
3531 {
3532 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
3533 }
3534 }
3535
3536 if (desc.m_PeepholeEnabled)
3537 {
3538 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
3539 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
3540 }
3541
3542 if (desc.m_LayerNormEnabled)
3543 {
3544 if(!desc.m_CifgEnabled)
3545 {
3546 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
3547 }
3548 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
3549 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
3550 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
3551 }
3552
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003553 bool isSupported = false;
3554 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
3555 {
3556 FORWARD_LAYER_SUPPORT_FUNC(__func__,
3557 IsUnidirectionalSequenceLstmSupported,
3558 data.m_Backends,
3559 isSupported,
3560 inputInfo,
3561 outputStateInInfo,
3562 cellStateInInfo,
Mike Kelly0ae102a2022-04-25 16:18:57 +01003563 outputStateOutInfo,
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003564 cellStateOutInfo,
Mike Kelly0ae102a2022-04-25 16:18:57 +01003565 outputInfo,
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003566 desc,
3567 paramsInfo);
3568 };
3569
3570 bool isDynamic = false;
3571 if (!IsDynamicTensor(outputInfo))
3572 {
3573 validateFunc(outputInfo, isSupported);
3574 }
3575 else
3576 {
3577 isDynamic = true;
3578 isSupported = AreDynamicTensorsSupported();
3579 }
3580
3581 if (!isSupported)
3582 {
3583 return false;
3584 }
3585
3586 // Add the layer
3587 IConnectableLayer* layer = data.m_Network->AddUnidirectionalSequenceLstmLayer(desc,
3588 params,
3589 "UnidirectionalSequenceLstm");
3590
3591 input.Connect(layer->GetInputSlot(0));
3592 outputStateIn.Connect(layer->GetInputSlot(1));
3593 cellStateIn.Connect(layer->GetInputSlot(2));
3594
3595 if (!isDynamic)
3596 {
Mike Kelly0ae102a2022-04-25 16:18:57 +01003597 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 2, model, data));
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003598 }
3599 else
3600 {
Mike Kelly0ae102a2022-04-25 16:18:57 +01003601 return (SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, 2, model, data, nullptr,
3602 validateFunc, ActivationFn::kActivationNone, true));
Cathal Corbett0fa5e6d2022-01-21 16:55:13 +00003603 }
3604}
3605
Kevin May42477c12020-03-26 13:34:14 +00003606} // armnn_driver namespace