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