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