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